VirtualBox

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

Last change on this file since 3313 was 3313, checked in by bird, 5 years ago

kmk,kWorker: Assign processor groups to kWorker processes. Added --special-env hack for having a mspdbsrv.exe instance per processor group (using _MSPDBSRV_ENDPOINT_). This was complicated by PCH requiring to share .pdb file and therefore mspdbsrv.exe instance, requiring a mspdb100.dll re-init hack to disconnect kWorker from the previous mspdbsrv when switching. fun.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 473.7 KB
Line 
1/* $Id: kWorker.c 3313 2020-03-16 02:31:38Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include <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 /** Set if executable. */
311 KBOOL fExe;
312 /** Set if native module entry. */
313 KBOOL fNative;
314 /** Loader module handle. */
315 PKLDRMOD pLdrMod;
316 /** The windows module handle. */
317 HMODULE hOurMod;
318 /** The of the loaded image bits. */
319 KSIZE cbImage;
320 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
321 KU8 iCrtSlot;
322 /** Loop prevention when working the tree. */
323 KBOOL fVisited;
324 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
325 KBOOL fNeedReInit;
326 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
327 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
328 KU8 fReInitOnMsPdbSrvEndpointChange;
329 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
330 char *pszMsPdbSrvEndpoint;
331
332 union
333 {
334 /** Data for a manually loaded image. */
335 struct
336 {
337 /** Where we load the image. */
338 KU8 *pbLoad;
339 /** Virgin copy of the image. */
340 KU8 *pbCopy;
341 /** Ldr pvBits argument. This is NULL till we've successfully resolved
342 * the imports. */
343 void *pvBits;
344 /** The state. */
345 KWMODSTATE enmState;
346#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
347 /** The number of entries in the table. */
348 KU32 cFunctions;
349 /** The function table address (in the copy). */
350 PRUNTIME_FUNCTION paFunctions;
351 /** Set if we've already registered a function table already. */
352 KBOOL fRegisteredFunctionTable;
353#endif
354 /** Set if we share memory with other executables. */
355 KBOOL fUseLdBuf;
356 /** Set after the first whole image copy is done. */
357 KBOOL fCanDoQuick;
358 /** Number of quick copy chunks. */
359 KU8 cQuickCopyChunks;
360 /** Number of quick zero chunks. */
361 KU8 cQuickZeroChunks;
362 /** Quicker image copy instructions that skips non-writable parts when
363 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
364 * image. */
365 struct
366 {
367 /** The copy destination. */
368 KU8 *pbDst;
369 /** The copy source. */
370 KU8 const *pbSrc;
371 /** How much to copy. */
372 KSIZE cbToCopy;
373 } aQuickCopyChunks[3];
374 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
375 struct
376 {
377 /** Where to start zeroing. */
378 KU8 *pbDst;
379 /** How much to zero. */
380 KSIZE cbToZero;
381 } aQuickZeroChunks[3];
382
383 /** TLS index if one was allocated, otherwise KU32_MAX. */
384 KU32 idxTls;
385 /** Offset (RVA) of the TLS initialization data. */
386 KU32 offTlsInitData;
387 /** Number of bytes of TLS initialization data. */
388 KU32 cbTlsInitData;
389 /** Number of allocated bytes for TLS. */
390 KU32 cbTlsAlloc;
391 /** Number of TLS callbacks. */
392 KU32 cTlsCallbacks;
393 /** Offset (RVA) of the TLS callback table. */
394 KU32 offTlsCallbacks;
395
396 /** Number of imported modules. */
397 KSIZE cImpMods;
398 /** Import array (variable size). */
399 PKWMODULE apImpMods[1];
400 } Manual;
401 } u;
402} KWMODULE;
403
404
405typedef struct KWDYNLOAD *PKWDYNLOAD;
406typedef struct KWDYNLOAD
407{
408 /** Pointer to the next in the list. */
409 PKWDYNLOAD pNext;
410
411 /** The module handle we present to the application.
412 * This is the LoadLibraryEx return value for special modules and the
413 * KWMODULE.hOurMod value for the others. */
414 HMODULE hmod;
415
416 /** The module for non-special resource stuff, NULL if special. */
417 PKWMODULE pMod;
418
419 /** The length of the LoadLibary filename. */
420 KSIZE cchRequest;
421 /** The LoadLibrary filename. */
422 char szRequest[1];
423} KWDYNLOAD;
424
425
426/**
427 * GetModuleHandle cache for system modules frequently queried.
428 */
429typedef struct KWGETMODULEHANDLECACHE
430{
431 const char *pszName;
432 KU8 cchName;
433 KU8 cwcName;
434 const wchar_t *pwszName;
435 HANDLE hmod;
436} KWGETMODULEHANDLECACHE;
437typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
438
439
440/**
441 * A cached file.
442 */
443typedef struct KFSWCACHEDFILE
444{
445 /** The user data core. */
446 KFSUSERDATA Core;
447
448 /** Cached file handle. */
449 HANDLE hCached;
450 /** Cached file section handle. */
451 HANDLE hSection;
452 /** Cached file content. */
453 KU8 *pbCached;
454 /** The file size. */
455 KU32 cbCached;
456#ifdef WITH_HASH_MD5_CACHE
457 /** Set if we've got a valid MD5 hash in abMd5Digest. */
458 KBOOL fValidMd5;
459 /** The MD5 digest if fValidMd5 is set. */
460 KU8 abMd5Digest[16];
461#endif
462
463 /** Circular self reference. Prevents the object from ever going away and
464 * keeps it handy for debugging. */
465 PKFSOBJ pFsObj;
466 /** The file path (for debugging). */
467 char szPath[1];
468} KFSWCACHEDFILE;
469/** Pointer to a cached filed. */
470typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
471
472#ifdef WITH_HASH_MD5_CACHE
473
474/** Pointer to a MD5 hash instance. */
475typedef struct KWHASHMD5 *PKWHASHMD5;
476/**
477 * A MD5 hash instance.
478 */
479typedef struct KWHASHMD5
480{
481 /** The magic value. */
482 KUPTR uMagic;
483 /** Pointer to the next hash handle. */
484 PKWHASHMD5 pNext;
485 /** The cached file we've associated this handle with. */
486 PKFSWCACHEDFILE pCachedFile;
487 /** The number of bytes we've hashed. */
488 KU32 cbHashed;
489 /** Set if this has gone wrong. */
490 KBOOL fGoneBad;
491 /** Set if we're in fallback mode (file not cached). */
492 KBOOL fFallbackMode;
493 /** Set if we've already finalized the digest. */
494 KBOOL fFinal;
495 /** The MD5 fallback context. */
496 struct MD5Context Md5Ctx;
497 /** The finalized digest. */
498 KU8 abDigest[16];
499
500} KWHASHMD5;
501/** Magic value for KWHASHMD5::uMagic (Les McCann). */
502# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
503
504#endif /* WITH_HASH_MD5_CACHE */
505#ifdef WITH_TEMP_MEMORY_FILES
506
507typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
508typedef struct KWFSTEMPFILESEG
509{
510 /** File offset of data. */
511 KU32 offData;
512 /** The size of the buffer pbData points to. */
513 KU32 cbDataAlloc;
514 /** The segment data. */
515 KU8 *pbData;
516} KWFSTEMPFILESEG;
517
518typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
519typedef struct KWFSTEMPFILE
520{
521 /** Pointer to the next temporary file for this run. */
522 PKWFSTEMPFILE pNext;
523 /** The UTF-16 path. (Allocated after this structure.) */
524 const wchar_t *pwszPath;
525 /** The path length. */
526 KU16 cwcPath;
527 /** Number of active handles using this file/mapping (<= 2). */
528 KU8 cActiveHandles;
529 /** Number of active mappings (mapped views) (0 or 1). */
530 KU8 cMappings;
531 /** The amount of space allocated in the segments. */
532 KU32 cbFileAllocated;
533 /** The current file size. */
534 KU32 cbFile;
535 /** The number of segments. */
536 KU32 cSegs;
537 /** Segments making up the file. */
538 PKWFSTEMPFILESEG paSegs;
539} KWFSTEMPFILE;
540
541#endif /* WITH_TEMP_MEMORY_FILES */
542#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
543
544/**
545 * Console line buffer or output full buffer.
546 */
547typedef struct KWOUTPUTSTREAMBUF
548{
549 /** The main output handle. */
550 HANDLE hOutput;
551 /** Our backup handle. */
552 HANDLE hBackup;
553 /** Set if this is a console handle and we're in line buffered mode.
554 * When clear, we may buffer multiple lines, though try flush on line
555 * boundraries when ever possible. */
556 KBOOL fIsConsole;
557 /** Compressed GetFileType result. */
558 KU8 fFileType;
559 KU8 abPadding[2];
560 union
561 {
562 /** Line buffer mode (fIsConsole == K_TRUE). */
563 struct
564 {
565 /** Amount of pending console output in wchar_t's. */
566 KU32 cwcBuf;
567 /** The allocated buffer size. */
568 KU32 cwcBufAlloc;
569 /** Pending console output. */
570 wchar_t *pwcBuf;
571 } Con;
572 /** Fully buffered mode (fIsConsole == K_FALSE). */
573 struct
574 {
575 /** Amount of pending output (in chars). */
576 KU32 cchBuf;
577#ifdef WITH_STD_OUT_ERR_BUFFERING
578 /** The allocated buffer size (in chars). */
579 KU32 cchBufAlloc;
580 /** Pending output. */
581 char *pchBuf;
582#endif
583 } Fully;
584 } u;
585} KWOUTPUTSTREAMBUF;
586/** Pointer to a console line buffer. */
587typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
588
589/**
590 * Combined console buffer of complete lines.
591 */
592typedef struct KWCONSOLEOUTPUT
593{
594 /** The console output handle.
595 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
596 * combined output buffering. */
597 HANDLE hOutput;
598 /** The current code page for the console. */
599 KU32 uCodepage;
600 /** Amount of pending console output in wchar_t's. */
601 KU32 cwcBuf;
602 /** Number of times we've flushed it in any way (for cl.exe hack). */
603 KU32 cFlushes;
604 /** Pending console output. */
605 wchar_t wszBuf[8192];
606} KWCONSOLEOUTPUT;
607/** Pointer to a combined console buffer. */
608typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
609
610#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
611
612/** Handle type. */
613typedef enum KWHANDLETYPE
614{
615 KWHANDLETYPE_INVALID = 0,
616 KWHANDLETYPE_FSOBJ_READ_CACHE,
617 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
618 KWHANDLETYPE_TEMP_FILE,
619 KWHANDLETYPE_TEMP_FILE_MAPPING,
620 KWHANDLETYPE_OUTPUT_BUF
621} KWHANDLETYPE;
622
623/** Handle data. */
624typedef struct KWHANDLE
625{
626 KWHANDLETYPE enmType;
627 /** Number of references */
628 KU32 cRefs;
629 /** The current file offset. */
630 KU32 offFile;
631 /** Handle access. */
632 KU32 dwDesiredAccess;
633 /** The handle. */
634 HANDLE hHandle;
635
636 /** Type specific data. */
637 union
638 {
639 /** The file system object. */
640 PKFSWCACHEDFILE pCachedFile;
641 /** Temporary file handle or mapping handle. */
642 PKWFSTEMPFILE pTempFile;
643#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
644 /** Buffered output stream. */
645 PKWOUTPUTSTREAMBUF pOutBuf;
646#endif
647 } u;
648} KWHANDLE;
649typedef KWHANDLE *PKWHANDLE;
650
651/**
652 * Tracking one of our memory mappings.
653 */
654typedef struct KWMEMMAPPING
655{
656 /** Number of references. */
657 KU32 cRefs;
658 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
659 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
660 KWHANDLETYPE enmType;
661 /** The mapping address. */
662 PVOID pvMapping;
663 /** Type specific data. */
664 union
665 {
666 /** The file system object. */
667 PKFSWCACHEDFILE pCachedFile;
668 /** Temporary file handle or mapping handle. */
669 PKWFSTEMPFILE pTempFile;
670 } u;
671} KWMEMMAPPING;
672/** Pointer to a memory mapping tracker. */
673typedef KWMEMMAPPING *PKWMEMMAPPING;
674
675
676/** Pointer to a VirtualAlloc tracker entry. */
677typedef struct KWVIRTALLOC *PKWVIRTALLOC;
678/**
679 * Tracking an VirtualAlloc allocation.
680 */
681typedef struct KWVIRTALLOC
682{
683 PKWVIRTALLOC pNext;
684 void *pvAlloc;
685 KSIZE cbAlloc;
686 /** This is KU32_MAX if not a preallocated chunk. */
687 KU32 idxPreAllocated;
688} KWVIRTALLOC;
689
690
691/** Pointer to a heap (HeapCreate) tracker entry. */
692typedef struct KWHEAP *PKWHEAP;
693/**
694 * Tracking an heap (HeapCreate)
695 */
696typedef struct KWHEAP
697{
698 PKWHEAP pNext;
699 HANDLE hHeap;
700} KWHEAP;
701
702
703/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
704typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
705/**
706 * Tracking an FlsAlloc/TlsAlloc index.
707 */
708typedef struct KWLOCALSTORAGE
709{
710 PKWLOCALSTORAGE pNext;
711 KU32 idx;
712} KWLOCALSTORAGE;
713
714
715/** Pointer to an at exit callback record */
716typedef struct KWEXITCALLACK *PKWEXITCALLACK;
717/**
718 * At exit callback record.
719 */
720typedef struct KWEXITCALLACK
721{
722 PKWEXITCALLACK pNext;
723 _onexit_t pfnCallback;
724 /** At exit doesn't have an exit code. */
725 KBOOL fAtExit;
726} KWEXITCALLACK;
727
728
729typedef enum KWTOOLTYPE
730{
731 KWTOOLTYPE_INVALID = 0,
732 KWTOOLTYPE_SANDBOXED,
733 KWTOOLTYPE_WATCOM,
734 KWTOOLTYPE_EXEC,
735 KWTOOLTYPE_END
736} KWTOOLTYPE;
737
738typedef enum KWTOOLHINT
739{
740 KWTOOLHINT_INVALID = 0,
741 KWTOOLHINT_NONE,
742 KWTOOLHINT_VISUAL_CPP_CL,
743 KWTOOLHINT_VISUAL_CPP_LINK,
744 KWTOOLHINT_END
745} KWTOOLHINT;
746
747
748/**
749 * A kWorker tool.
750 */
751typedef struct KWTOOL
752{
753 /** The user data core structure. */
754 KFSUSERDATA Core;
755
756 /** The normalized path to the program. */
757 const char *pszPath;
758 /** UTF-16 version of pszPath. */
759 wchar_t const *pwszPath;
760 /** The kind of tool. */
761 KWTOOLTYPE enmType;
762
763 union
764 {
765 struct
766 {
767 /** The main entry point. */
768 KUPTR uMainAddr;
769 /** The executable. */
770 PKWMODULE pExe;
771 /** List of dynamically loaded modules.
772 * These will be kept loaded till the tool is destroyed (if we ever do that). */
773 PKWDYNLOAD pDynLoadHead;
774 /** Module array sorted by hOurMod. */
775 PKWMODULE *papModules;
776 /** Number of entries in papModules. */
777 KU32 cModules;
778
779 /** Tool hint (for hacks and such). */
780 KWTOOLHINT enmHint;
781 } Sandboxed;
782 } u;
783} KWTOOL;
784/** Pointer to a tool. */
785typedef struct KWTOOL *PKWTOOL;
786
787
788typedef struct KWSANDBOX *PKWSANDBOX;
789typedef struct KWSANDBOX
790{
791 /** The tool currently running in the sandbox. */
792 PKWTOOL pTool;
793 /** Jump buffer. */
794 jmp_buf JmpBuf;
795 /** The thread ID of the main thread (owner of JmpBuf). */
796 DWORD idMainThread;
797 /** Copy of the NT TIB of the main thread. */
798 NT_TIB TibMainThread;
799 /** The NT_TIB::ExceptionList value inside the try case.
800 * We restore this prior to the longjmp. */
801 void *pOutXcptListHead;
802 /** The exit code in case of longjmp. */
803 int rcExitCode;
804 /** Set if we're running. */
805 KBOOL fRunning;
806 /** Whether to disable caching of ".pch" files. */
807 KBOOL fNoPchCaching;
808
809 /** The command line. */
810 char *pszCmdLine;
811 /** The UTF-16 command line. */
812 wchar_t *pwszCmdLine;
813 /** Number of arguments in papszArgs. */
814 int cArgs;
815 /** The argument vector. */
816 char **papszArgs;
817 /** The argument vector. */
818 wchar_t **papwszArgs;
819
820 /** The _pgmptr msvcrt variable. */
821 char *pgmptr;
822 /** The _wpgmptr msvcrt variable. */
823 wchar_t *wpgmptr;
824
825 /** The _initenv msvcrt variable. */
826 char **initenv;
827 /** The _winitenv msvcrt variable. */
828 wchar_t **winitenv;
829
830 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
831 KSIZE cEnvVarsAllocated;
832 /** The _environ msvcrt variable. */
833 char **environ;
834 /** The _wenviron msvcrt variable. */
835 wchar_t **wenviron;
836 /** The shadow _environ msvcrt variable. */
837 char **papszEnvVars;
838 /** The shadow _wenviron msvcrt variable. */
839 wchar_t **papwszEnvVars;
840
841
842 /** Handle table. */
843 PKWHANDLE *papHandles;
844 /** Size of the handle table. */
845 KU32 cHandles;
846 /** Number of active handles in the table. */
847 KU32 cActiveHandles;
848 /** Number of handles in the handle table that will not be freed. */
849 KU32 cFixedHandles;
850 /** Total number of leaked handles. */
851 KU32 cLeakedHandles;
852
853 /** Number of active memory mappings in paMemMappings. */
854 KU32 cMemMappings;
855 /** The allocated size of paMemMappings. */
856 KU32 cMemMappingsAlloc;
857 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
858 PKWMEMMAPPING paMemMappings;
859
860 /** Head of the list of temporary file. */
861 PKWFSTEMPFILE pTempFileHead;
862
863 /** Head of the virtual alloc allocations. */
864 PKWVIRTALLOC pVirtualAllocHead;
865 /** Head of the heap list (HeapCreate).
866 * This is only done from images we forcibly restore. */
867 PKWHEAP pHeapHead;
868 /** Head of the FlsAlloc indexes. */
869 PKWLOCALSTORAGE pFlsAllocHead;
870 /** Head of the TlsAlloc indexes. */
871 PKWLOCALSTORAGE pTlsAllocHead;
872
873 /** The at exit callback head.
874 * This is only done from images we forcibly restore. */
875 PKWEXITCALLACK pExitCallbackHead;
876
877 MY_UNICODE_STRING SavedCommandLine;
878
879#ifdef WITH_HASH_MD5_CACHE
880 /** The special MD5 hash instance. */
881 PKWHASHMD5 pHashHead;
882 /** ReadFile sets these while CryptHashData claims and clears them.
883 *
884 * This is part of the heuristics we use for MD5 caching for header files. The
885 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
886 * header, then passes the same buffer and read byte count to CryptHashData.
887 */
888 struct
889 {
890 /** The cached file last read from. */
891 PKFSWCACHEDFILE pCachedFile;
892 /** The file offset of the last cached read. */
893 KU32 offRead;
894 /** The number of bytes read last. */
895 KU32 cbRead;
896 /** The buffer pointer of the last read. */
897 void *pvRead;
898 } LastHashRead;
899#endif
900
901#ifdef WITH_CRYPT_CTX_REUSE
902 /** Reusable crypt contexts. */
903 struct
904 {
905 /** The creation provider type. */
906 KU32 dwProvType;
907 /** The creation flags. */
908 KU32 dwFlags;
909 /** The length of the container name. */
910 KU32 cwcContainer;
911 /** The length of the provider name. */
912 KU32 cwcProvider;
913 /** The container name string. */
914 wchar_t *pwszContainer;
915 /** The provider name string. */
916 wchar_t *pwszProvider;
917 /** The context handle. */
918 HCRYPTPROV hProv;
919 } aCryptCtxs[4];
920 /** Number of reusable crypt conexts in aCryptCtxs. */
921 KU32 cCryptCtxs;
922#endif
923
924
925#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
926 /** The internal standard output handle. */
927 KWHANDLE HandleStdOut;
928 /** The internal standard error handle. */
929 KWHANDLE HandleStdErr;
930 /** Standard output (and whatever else) buffer. */
931 KWOUTPUTSTREAMBUF StdOut;
932 /** Standard error buffer. */
933 KWOUTPUTSTREAMBUF StdErr;
934 /** Combined buffer of completed lines. */
935 KWCONSOLEOUTPUT Combined;
936#endif
937} KWSANDBOX;
938
939
940/** A CRT slot. */
941typedef struct KWCRTSLOT
942{
943 KU32 iSlot;
944
945 /** The CRT module data. */
946 PKWMODULE pModule;
947 /** Pointer to the malloc function. */
948 void * (__cdecl *pfnMalloc)(size_t);
949
950} KWCRTSLOT;
951typedef KWCRTSLOT *PKWCRTSLOT;
952
953
954/** Replacement function entry. */
955typedef struct KWREPLACEMENTFUNCTION
956{
957 /** The function name. */
958 const char *pszFunction;
959 /** The length of the function name. */
960 KSIZE cchFunction;
961 /** The module name (optional). */
962 const char *pszModule;
963 /** The replacement function, data address or CRT slot function array. */
964 KUPTR pfnReplacement;
965 /** Only replace in the executable.
966 * @todo fix the reinitialization of non-native DLLs! */
967 KBOOL fOnlyExe;
968 /** Set if pfnReplacement points to a CRT slot function array. */
969 KBOOL fCrtSlotArray;
970} KWREPLACEMENTFUNCTION;
971typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
972
973#if 0
974/** Replacement function entry. */
975typedef struct KWREPLACEMENTDATA
976{
977 /** The function name. */
978 const char *pszFunction;
979 /** The length of the function name. */
980 KSIZE cchFunction;
981 /** The module name (optional). */
982 const char *pszModule;
983 /** Function providing the replacement. */
984 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
985} KWREPLACEMENTDATA;
986typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
987#endif
988
989/**
990 * One test job (--full-test).
991 */
992typedef struct KWONETEST
993{
994 /** Where this job originated. */
995 const char *pszJobSrc;
996 /** The argument number it started with. */
997 unsigned iJobSrc;
998 /** Set if virgin, clear if modified. */
999 KBOOL fVirgin;
1000
1001 /** Number of runs to give it. */
1002 unsigned cRuns;
1003
1004 /** @name kSubmitHandleJobUnpacked arguments
1005 * @{ */
1006 const char *pszExecutable;
1007 const char *pszCwd;
1008 KU32 cArgs;
1009 const char **papszArgs;
1010 KU32 cEnvVars;
1011 const char **papszEnvVars;
1012 const char *pszSpecialEnv;
1013 KBOOL fWatcomBrainDamange;
1014 KBOOL fNoPchCaching;
1015 KU32 cPostCmdArgs;
1016 const char **papszPostCmdArgs;
1017 /** @} */
1018
1019 /** Pointer to the next one. */
1020 struct KWONETEST *pNext;
1021} KWONETEST;
1022/** Pointer to one test job. */
1023typedef KWONETEST *PKWONETEST;
1024
1025
1026/*********************************************************************************************************************************
1027* Global Variables *
1028*********************************************************************************************************************************/
1029/** The sandbox data. */
1030static KWSANDBOX g_Sandbox;
1031
1032/** The module currently occupying g_abDefLdBuf. */
1033static PKWMODULE g_pModInLdBuf = NULL;
1034
1035/** The module that previuosly occupied g_abDefLdBuf. */
1036static PKWMODULE g_pModPrevInLdBuf = NULL;
1037
1038/** Module list head. */
1039static PKWMODULE g_pModuleHead = NULL;
1040/** Where to insert the next module. */
1041static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1042
1043/** Module hash table. */
1044static PKWMODULE g_apModules[127];
1045
1046/** GetModuleHandle cache. */
1047static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1048{
1049#define MOD_CACHE_STRINGS(str) str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1, L##str
1050 { MOD_CACHE_STRINGS("KERNEL32.DLL"), NULL },
1051 { MOD_CACHE_STRINGS("mscoree.dll"), NULL },
1052};
1053
1054/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1055static PKWMODULE g_pModPendingTlsAlloc = NULL;
1056
1057/** CRT slots.
1058 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1059static KWCRTSLOT g_aCrtSlots[32];
1060
1061/** The file system cache. */
1062static PKFSCACHE g_pFsCache;
1063/** The current directory (referenced). */
1064static PKFSOBJ g_pCurDirObj = NULL;
1065#ifdef KBUILD_OS_WINDOWS
1066/** The windows system32 directory (referenced). */
1067static PKFSDIR g_pWinSys32 = NULL;
1068#endif
1069
1070/** Verbosity level. */
1071static int g_cVerbose = 2;
1072
1073/** Whether we should restart the worker. */
1074static KBOOL g_fRestart = K_FALSE;
1075
1076/** The process group this worker is tied to (--group option), -1 if none. */
1077static KI32 g_iProcessGroup = -1;
1078
1079/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1080static int volatile g_rcCtrlC = 0;
1081
1082/** The communication pipe handle. We break this when we see Ctrl-C such. */
1083#ifdef KBUILD_OS_WINDOWS
1084static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1085#else
1086static int g_hPipe = -1;
1087#endif
1088
1089
1090/* Further down. */
1091extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1092extern KU32 const g_cSandboxReplacements;
1093
1094extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1095extern KU32 const g_cSandboxNativeReplacements;
1096
1097extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1098extern KU32 const g_cSandboxGetProcReplacements;
1099
1100
1101/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1102 * cover the default executable link address of 0x400000.
1103 * @remarks Early main() makes it read+write+executable. Attempts as having
1104 * it as a separate section failed because the linker insists on
1105 * writing out every zero in the uninitialized section, resulting in
1106 * really big binaries. */
1107__declspec(align(0x1000))
1108static KU8 g_abDefLdBuf[16*1024*1024];
1109
1110#ifdef WITH_LOG_FILE
1111/** Log file handle. */
1112static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1113#endif
1114
1115
1116#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1117/** Virtual address space reserved for CL.EXE heap manager.
1118 *
1119 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1120 * address. It's among other things used for precompiled headers, which
1121 * seemingly have addresses hardcoded into them and won't work if mapped
1122 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1123 * for it. (The /Zm option may affect this allocation.)
1124 */
1125static struct
1126{
1127 /** The memory address we need. */
1128 KUPTR const uFixed;
1129 /** How much we need to fix. */
1130 KSIZE const cbFixed;
1131 /** What we actually got, NULL if given back. */
1132 void *pvReserved;
1133 /** Whether it is in use or not. */
1134 KBOOL fInUse;
1135} g_aFixedVirtualAllocs[] =
1136{
1137# if K_ARCH == K_ARCH_X86_32
1138 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1139 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1140 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1141# else
1142 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1143# endif
1144};
1145#endif
1146
1147
1148#ifdef WITH_HISTORY
1149/** The job history. */
1150static char *g_apszHistory[32];
1151/** Index of the next history entry. */
1152static unsigned g_iHistoryNext = 0;
1153#endif
1154
1155
1156/** Number of jobs executed. */
1157static KU32 g_cJobs;
1158/** Number of tools. */
1159static KU32 g_cTools;
1160/** Number of modules. */
1161static KU32 g_cModules;
1162/** Number of non-native modules. */
1163static KU32 g_cNonNativeModules;
1164/** Number of read-cached files. */
1165static KU32 g_cReadCachedFiles;
1166/** Total size of read-cached files. */
1167static KSIZE g_cbReadCachedFiles;
1168
1169/** Total number of ReadFile calls. */
1170static KSIZE g_cReadFileCalls;
1171/** Total bytes read via ReadFile. */
1172static KSIZE g_cbReadFileTotal;
1173/** Total number of read from read-cached files. */
1174static KSIZE g_cReadFileFromReadCached;
1175/** Total bytes read from read-cached files. */
1176static KSIZE g_cbReadFileFromReadCached;
1177/** Total number of read from in-memory temporary files. */
1178static KSIZE g_cReadFileFromInMemTemp;
1179/** Total bytes read from in-memory temporary files. */
1180static KSIZE g_cbReadFileFromInMemTemp;
1181
1182/** Total number of WriteFile calls. */
1183static KSIZE g_cWriteFileCalls;
1184/** Total bytes written via WriteFile. */
1185static KSIZE g_cbWriteFileTotal;
1186/** Total number of written to from in-memory temporary files. */
1187static KSIZE g_cWriteFileToInMemTemp;
1188/** Total bytes written to in-memory temporary files. */
1189static KSIZE g_cbWriteFileToInMemTemp;
1190
1191
1192/*********************************************************************************************************************************
1193* Internal Functions *
1194*********************************************************************************************************************************/
1195static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1196static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1197 const char *pszSearchPath, PKWMODULE *ppMod);
1198static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot);
1199static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1200static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1201#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1202static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1203#endif
1204static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1205
1206
1207
1208
1209/**
1210 * Debug printing.
1211 * @param pszFormat Debug format string.
1212 * @param ... Format argument.
1213 */
1214static void kwDbgPrintfV(const char *pszFormat, va_list va)
1215{
1216 if (g_cVerbose >= 2)
1217 {
1218 DWORD const dwSavedErr = GetLastError();
1219#ifdef WITH_LOG_FILE
1220 DWORD dwIgnored;
1221 char szTmp[2048];
1222 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1223 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1224 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1225 cch += cchPrefix;
1226 else
1227 {
1228 cch = sizeof(szTmp) - 1;
1229 szTmp[cch] = '\0';
1230 }
1231
1232 if (g_hLogFile == INVALID_HANDLE_VALUE)
1233 {
1234 wchar_t wszFilename[128];
1235 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1236 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1237 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1238 }
1239
1240 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1241#else
1242 fprintf(stderr, "debug: ");
1243 vfprintf(stderr, pszFormat, va);
1244#endif
1245
1246 SetLastError(dwSavedErr);
1247 }
1248}
1249
1250
1251/**
1252 * Debug printing.
1253 * @param pszFormat Debug format string.
1254 * @param ... Format argument.
1255 */
1256static void kwDbgPrintf(const char *pszFormat, ...)
1257{
1258 if (g_cVerbose >= 2)
1259 {
1260 va_list va;
1261 va_start(va, pszFormat);
1262 kwDbgPrintfV(pszFormat, va);
1263 va_end(va);
1264 }
1265}
1266
1267
1268/**
1269 * Debugger printing.
1270 * @param pszFormat Debug format string.
1271 * @param ... Format argument.
1272 */
1273static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1274{
1275 if (IsDebuggerPresent())
1276 {
1277 DWORD const dwSavedErr = GetLastError();
1278 char szTmp[2048];
1279
1280 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1281 OutputDebugStringA(szTmp);
1282
1283 SetLastError(dwSavedErr);
1284 }
1285}
1286
1287
1288/**
1289 * Debugger printing.
1290 * @param pszFormat Debug format string.
1291 * @param ... Format argument.
1292 */
1293static void kwDebuggerPrintf(const char *pszFormat, ...)
1294{
1295 va_list va;
1296 va_start(va, pszFormat);
1297 kwDebuggerPrintfV(pszFormat, va);
1298 va_end(va);
1299}
1300
1301
1302
1303/**
1304 * Error printing.
1305 * @param pszFormat Message format string.
1306 * @param ... Format argument.
1307 */
1308static void kwErrPrintfV(const char *pszFormat, va_list va)
1309{
1310 DWORD const dwSavedErr = GetLastError();
1311
1312 fprintf(stderr, "kWorker: error: ");
1313 vfprintf(stderr, pszFormat, va);
1314 fflush(stderr); /* In case it's a pipe. */
1315
1316 SetLastError(dwSavedErr);
1317}
1318
1319
1320/**
1321 * Error printing.
1322 * @param pszFormat Message format string.
1323 * @param ... Format argument.
1324 */
1325static void kwErrPrintf(const char *pszFormat, ...)
1326{
1327 va_list va;
1328 va_start(va, pszFormat);
1329 kwErrPrintfV(pszFormat, va);
1330 va_end(va);
1331}
1332
1333
1334/**
1335 * Error printing.
1336 * @return rc;
1337 * @param rc Return value
1338 * @param pszFormat Message format string.
1339 * @param ... Format argument.
1340 */
1341static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1342{
1343 va_list va;
1344 va_start(va, pszFormat);
1345 kwErrPrintfV(pszFormat, va);
1346 va_end(va);
1347 return rc;
1348}
1349
1350
1351#ifdef K_STRICT
1352
1353KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1354{
1355 DWORD const dwSavedErr = GetLastError();
1356
1357 fprintf(stderr,
1358 "\n"
1359 "!!Assertion failed!!\n"
1360 "Expression: %s\n"
1361 "Function : %s\n"
1362 "File: %s\n"
1363 "Line: %d\n"
1364 , pszExpr, pszFunction, pszFile, iLine);
1365
1366 SetLastError(dwSavedErr);
1367}
1368
1369
1370KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1371{
1372 DWORD const dwSavedErr = GetLastError();
1373 va_list va;
1374
1375 va_start(va, pszFormat);
1376 fprintf(stderr, pszFormat, va);
1377 va_end(va);
1378
1379 SetLastError(dwSavedErr);
1380}
1381
1382#endif /* K_STRICT */
1383
1384
1385/**
1386 * Hashes a string.
1387 *
1388 * @returns 32-bit string hash.
1389 * @param pszString String to hash.
1390 */
1391static KU32 kwStrHash(const char *pszString)
1392{
1393 /* This algorithm was created for sdbm (a public-domain reimplementation of
1394 ndbm) database library. it was found to do well in scrambling bits,
1395 causing better distribution of the keys and fewer splits. it also happens
1396 to be a good general hashing function with good distribution. the actual
1397 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1398 is the faster version used in gawk. [there is even a faster, duff-device
1399 version] the magic constant 65599 was picked out of thin air while
1400 experimenting with different constants, and turns out to be a prime.
1401 this is one of the algorithms used in berkeley db (see sleepycat) and
1402 elsewhere. */
1403 KU32 uHash = 0;
1404 KU32 uChar;
1405 while ((uChar = (unsigned char)*pszString++) != 0)
1406 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1407 return uHash;
1408}
1409
1410
1411/**
1412 * Hashes a string.
1413 *
1414 * @returns The string length.
1415 * @param pszString String to hash.
1416 * @param puHash Where to return the 32-bit string hash.
1417 */
1418static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1419{
1420 const char * const pszStart = pszString;
1421 KU32 uHash = 0;
1422 KU32 uChar;
1423 while ((uChar = (unsigned char)*pszString) != 0)
1424 {
1425 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1426 pszString++;
1427 }
1428 *puHash = uHash;
1429 return pszString - pszStart;
1430}
1431
1432
1433/**
1434 * Hashes a string.
1435 *
1436 * @returns The string length in wchar_t units.
1437 * @param pwszString String to hash.
1438 * @param puHash Where to return the 32-bit string hash.
1439 */
1440static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1441{
1442 const wchar_t * const pwszStart = pwszString;
1443 KU32 uHash = 0;
1444 KU32 uChar;
1445 while ((uChar = *pwszString) != 0)
1446 {
1447 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1448 pwszString++;
1449 }
1450 *puHash = uHash;
1451 return pwszString - pwszStart;
1452}
1453
1454
1455/**
1456 * Converts the given string to unicode.
1457 *
1458 * @returns Length of the resulting string in wchar_t's.
1459 * @param pszSrc The source string.
1460 * @param pwszDst The destination buffer.
1461 * @param cwcDst The size of the destination buffer in wchar_t's.
1462 */
1463static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1464{
1465 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1466 KSIZE offDst = 0;
1467 while (offDst < cwcDst)
1468 {
1469 char ch = *pszSrc++;
1470 pwszDst[offDst++] = ch;
1471 if (!ch)
1472 return offDst - 1;
1473 kHlpAssert((unsigned)ch < 127);
1474 }
1475
1476 pwszDst[offDst - 1] = '\0';
1477 return offDst;
1478}
1479
1480
1481/**
1482 * Converts the given string to UTF-16, allocating the buffer.
1483 *
1484 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1485 * the source string.
1486 * @param pchSrc The source string.
1487 * @param cchSrc The length of the source string.
1488 */
1489static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1490{
1491 DWORD const dwErrSaved = GetLastError();
1492 KSIZE cwcBuf = cchSrc + 1;
1493 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1494 if (pwszBuf)
1495 {
1496 if (cchSrc > 0)
1497 {
1498 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1499 if (cwcRet > 0)
1500 {
1501 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1502 pwszBuf[cwcRet] = '\0';
1503 }
1504 else
1505 {
1506 kHlpFree(pwszBuf);
1507
1508 /* Figure the length and allocate the right buffer size. */
1509 SetLastError(NO_ERROR);
1510 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1511 if (cwcRet)
1512 {
1513 cwcBuf = cwcRet + 2;
1514 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1515 if (pwszBuf)
1516 {
1517 SetLastError(NO_ERROR);
1518 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1519 if (cwcRet)
1520 {
1521 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1522 pwszBuf[cwcRet] = '\0';
1523 }
1524 else
1525 {
1526 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1527 kHlpFree(pwszBuf);
1528 pwszBuf = NULL;
1529 }
1530 }
1531 }
1532 else
1533 {
1534 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1535 pwszBuf = NULL;
1536 }
1537 }
1538 }
1539 else
1540 pwszBuf[0] = '\0';
1541 }
1542 SetLastError(dwErrSaved);
1543 return pwszBuf;
1544}
1545
1546
1547/**
1548 * Converts the given UTF-16 to a normal string.
1549 *
1550 * @returns Length of the resulting string.
1551 * @param pwszSrc The source UTF-16 string.
1552 * @param pszDst The destination buffer.
1553 * @param cbDst The size of the destination buffer in bytes.
1554 */
1555static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1556{
1557 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1558 KSIZE offDst = 0;
1559 while (offDst < cbDst)
1560 {
1561 wchar_t wc = *pwszSrc++;
1562 pszDst[offDst++] = (char)wc;
1563 if (!wc)
1564 return offDst - 1;
1565 kHlpAssert((unsigned)wc < 127);
1566 }
1567
1568 pszDst[offDst - 1] = '\0';
1569 return offDst;
1570}
1571
1572
1573/**
1574 * Converts the given UTF-16 to ASSI, allocating the buffer.
1575 *
1576 * @returns Pointer to the new heap allocation containing the ANSI version of
1577 * the source string.
1578 * @param pwcSrc The source string.
1579 * @param cwcSrc The length of the source string.
1580 */
1581static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1582{
1583 DWORD const dwErrSaved = GetLastError();
1584 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1585 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1586 if (pszBuf)
1587 {
1588 if (cwcSrc > 0)
1589 {
1590 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1591 if (cchRet > 0)
1592 {
1593 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1594 pszBuf[cchRet] = '\0';
1595 }
1596 else
1597 {
1598 kHlpFree(pszBuf);
1599
1600 /* Figure the length and allocate the right buffer size. */
1601 SetLastError(NO_ERROR);
1602 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1603 if (cchRet)
1604 {
1605 cbBuf = cchRet + 2;
1606 pszBuf = (char *)kHlpAlloc(cbBuf);
1607 if (pszBuf)
1608 {
1609 SetLastError(NO_ERROR);
1610 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1611 if (cchRet)
1612 {
1613 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1614 pszBuf[cchRet] = '\0';
1615 }
1616 else
1617 {
1618 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1619 kHlpFree(pszBuf);
1620 pszBuf = NULL;
1621 }
1622 }
1623 }
1624 else
1625 {
1626 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1627 pszBuf = NULL;
1628 }
1629 }
1630 }
1631 else
1632 pszBuf[0] = '\0';
1633 }
1634 SetLastError(dwErrSaved);
1635 return pszBuf;
1636}
1637
1638
1639
1640/** UTF-16 string length. */
1641static KSIZE kwUtf16Len(wchar_t const *pwsz)
1642{
1643 KSIZE cwc = 0;
1644 while (*pwsz != '\0')
1645 cwc++, pwsz++;
1646 return cwc;
1647}
1648
1649/**
1650 * Copy out the UTF-16 string following the convension of GetModuleFileName
1651 */
1652static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1653{
1654 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1655 if (cwcSrc + 1 <= cwcDst)
1656 {
1657 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1658 return (DWORD)cwcSrc;
1659 }
1660 if (cwcDst > 0)
1661 {
1662 KSIZE cwcDstTmp = cwcDst - 1;
1663 pwszDst[cwcDstTmp] = '\0';
1664 if (cwcDstTmp > 0)
1665 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1666 }
1667 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1668 return (DWORD)cwcDst;
1669}
1670
1671
1672/**
1673 * Copy out the ANSI string following the convension of GetModuleFileName
1674 */
1675static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1676{
1677 KSIZE cchSrc = kHlpStrLen(pszSrc);
1678 if (cchSrc + 1 <= cbDst)
1679 {
1680 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1681 return (DWORD)cchSrc;
1682 }
1683 if (cbDst > 0)
1684 {
1685 KSIZE cbDstTmp = cbDst - 1;
1686 pszDst[cbDstTmp] = '\0';
1687 if (cbDstTmp > 0)
1688 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1689 }
1690 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1691 return (DWORD)cbDst;
1692}
1693
1694
1695/**
1696 * Normalizes the path so we get a consistent hash.
1697 *
1698 * @returns status code.
1699 * @param pszPath The path.
1700 * @param pszNormPath The output buffer.
1701 * @param cbNormPath The size of the output buffer.
1702 */
1703static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1704{
1705 KFSLOOKUPERROR enmError;
1706 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1707 if (pFsObj)
1708 {
1709 KBOOL fRc;
1710 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1711 kFsCacheObjRelease(g_pFsCache, pFsObj);
1712 if (fRc)
1713 return 0;
1714 return KERR_BUFFER_OVERFLOW;
1715 }
1716 return KERR_FILE_NOT_FOUND;
1717}
1718
1719
1720/**
1721 * Get the pointer to the filename part of the path.
1722 *
1723 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1724 * @returns Pointer to the terminator char if no filename.
1725 * @param pszPath The path to parse.
1726 */
1727static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1728{
1729 const wchar_t *pwszLast = NULL;
1730 for (;;)
1731 {
1732 wchar_t wc = *pwszPath;
1733#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1734 if (wc == '/' || wc == '\\' || wc == ':')
1735 {
1736 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1737 /* nothing */;
1738 pwszLast = pwszPath;
1739 }
1740#else
1741 if (wc == '/')
1742 {
1743 while ((wc = *++pszFilename) == '/')
1744 /* betsuni */;
1745 pwszLast = pwszPath;
1746 }
1747#endif
1748 if (!wc)
1749 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1750 pwszPath++;
1751 }
1752}
1753
1754
1755
1756/**
1757 * Retains a new reference to the given module
1758 * @returns pMod
1759 * @param pMod The module to retain.
1760 */
1761static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1762{
1763 kHlpAssert(pMod->cRefs > 0);
1764 kHlpAssert(pMod->cRefs < 64);
1765 pMod->cRefs++;
1766 return pMod;
1767}
1768
1769
1770/**
1771 * Releases a module reference.
1772 *
1773 * @param pMod The module to release.
1774 */
1775static void kwLdrModuleRelease(PKWMODULE pMod)
1776{
1777 if (--pMod->cRefs == 0)
1778 {
1779 /* Unlink it from the hash table. */
1780 if (!pMod->fExe)
1781 {
1782 PKWMODULE pPrev = NULL;
1783 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1784 if (g_apModules[idx] == pMod)
1785 g_apModules[idx] = pMod->pNextHash;
1786 else
1787 {
1788 PKWMODULE pPrev = g_apModules[idx];
1789 kHlpAssert(pPrev != NULL);
1790 while (pPrev->pNextHash != pMod)
1791 {
1792 pPrev = pPrev->pNextHash;
1793 kHlpAssert(pPrev != NULL);
1794 }
1795 pPrev->pNextHash = pMod->pNextHash;
1796 }
1797 }
1798
1799 /* Unlink it from the list. */
1800 if (pMod != g_pModuleHead)
1801 {
1802 PKWMODULE pPrev = g_pModuleHead;
1803 while (pPrev)
1804 {
1805 if (pPrev->pNextList == pMod)
1806 {
1807 pPrev->pNextList = pMod->pNextList;
1808 if (!pMod->pNextList)
1809 g_ppModuleNext = &pPrev->pNextList;
1810 break;
1811 }
1812 pPrev = pPrev->pNextList;
1813 }
1814 kHlpAssert(pPrev != NULL);
1815 }
1816 else
1817 {
1818 g_pModuleHead = pMod->pNextList;
1819 if (!pMod->pNextList)
1820 g_ppModuleNext = &g_pModuleHead;
1821 }
1822
1823 /* Release import modules. */
1824 if (!pMod->fNative)
1825 {
1826 KSIZE idx = pMod->u.Manual.cImpMods;
1827 while (idx-- > 0)
1828 if (pMod->u.Manual.apImpMods[idx])
1829 {
1830 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1831 pMod->u.Manual.apImpMods[idx] = NULL;
1832 }
1833 }
1834
1835 /* Free our resources. */
1836 kLdrModClose(pMod->pLdrMod);
1837 pMod->pLdrMod = NULL;
1838
1839 if (!pMod->fNative)
1840 {
1841 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1842 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1843 }
1844
1845 if (pMod->iCrtSlot != KU8_MAX)
1846 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1847
1848 if (pMod->pszMsPdbSrvEndpoint)
1849 {
1850 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1851 pMod->pszMsPdbSrvEndpoint = NULL;
1852 }
1853
1854 kHlpFree(pMod);
1855 }
1856 else
1857 kHlpAssert(pMod->cRefs < 64);
1858}
1859
1860
1861/**
1862 * Links the module into the module hash table.
1863 *
1864 * @returns pMod
1865 * @param pMod The module to link.
1866 */
1867static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1868{
1869 if (!pMod->fExe)
1870 {
1871 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1872 pMod->pNextHash = g_apModules[idx];
1873 g_apModules[idx] = pMod;
1874 }
1875
1876 pMod->pNextList = NULL;
1877 *g_ppModuleNext = pMod;
1878 g_ppModuleNext = &pMod->pNextList;
1879
1880 return pMod;
1881}
1882
1883
1884/**
1885 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1886 *
1887 * @param pMod The natively loaded module to process.
1888 */
1889static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1890{
1891 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1892 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1893 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1894 IMAGE_NT_HEADERS const *pNtHdrs;
1895 IMAGE_DATA_DIRECTORY const *pDirEnt;
1896
1897 kHlpAssert(pMod->fNative);
1898
1899 /*
1900 * Locate the export descriptors.
1901 */
1902 /* MZ header. */
1903 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1904 {
1905 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1906 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1907 }
1908 else
1909 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1910
1911 /* Check PE header. */
1912 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1913 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
1914
1915 /* Locate the import descriptor array. */
1916 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
1917 if ( pDirEnt->Size > 0
1918 && pDirEnt->VirtualAddress != 0)
1919 {
1920 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
1921 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
1922 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
1923 KU8 *pbProtRange = NULL;
1924 SIZE_T cbProtRange = 0;
1925 DWORD fOldProt = 0;
1926 KU32 const cbPage = 0x1000;
1927 BOOL fRc;
1928
1929
1930 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
1931 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
1932 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
1933
1934 /*
1935 * Walk the import descriptor array.
1936 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
1937 */
1938 while ( cLeft-- > 0
1939 && pImpDesc->Name > 0
1940 && pImpDesc->FirstThunk > 0)
1941 {
1942 KU32 iThunk;
1943 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
1944 PKWMODULE pImportMod = NULL;
1945 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
1946 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
1947 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
1948 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
1949 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
1950 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
1951 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
1952
1953 /* Iterate the thunks. */
1954 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
1955 {
1956 KUPTR const off = paOrgThunks[iThunk].u1.Function;
1957 kHlpAssertReturnVoid(off < cbImage);
1958 if (!IMAGE_SNAP_BY_ORDINAL(off))
1959 {
1960 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
1961 KSIZE const cchSymbol = kHlpStrLen(pName->Name);
1962 KU32 i = g_cSandboxNativeReplacements;
1963 while (i-- > 0)
1964 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
1965 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
1966 {
1967 if ( !g_aSandboxNativeReplacements[i].pszModule
1968 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
1969 {
1970 KW_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
1971
1972 /* The .rdata section is normally read-only, so we need to make it writable first. */
1973 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
1974 {
1975 /* Restore previous .rdata page. */
1976 if (fOldProt)
1977 {
1978 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
1979 kHlpAssert(fRc);
1980 fOldProt = 0;
1981 }
1982
1983 /* Query attributes for the current .rdata page. */
1984 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
1985 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
1986 kHlpAssert(cbProtRange);
1987 if (cbProtRange)
1988 {
1989 switch (ProtInfo.Protect)
1990 {
1991 case PAGE_READWRITE:
1992 case PAGE_WRITECOPY:
1993 case PAGE_EXECUTE_READWRITE:
1994 case PAGE_EXECUTE_WRITECOPY:
1995 /* Already writable, nothing to do. */
1996 break;
1997
1998 default:
1999 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2000 case PAGE_READONLY:
2001 cbProtRange = cbPage;
2002 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2003 break;
2004
2005 case PAGE_EXECUTE:
2006 case PAGE_EXECUTE_READ:
2007 cbProtRange = cbPage;
2008 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2009 break;
2010 }
2011 kHlpAssertStmt(fRc, fOldProt = 0);
2012 }
2013 }
2014
2015 /*
2016 * Unslotted replacements are simple.
2017 */
2018 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2019 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2020 else
2021 {
2022 /*
2023 * Must find our module entry for this module, possibly creating one.
2024 */
2025 if (!pImportMod)
2026 {
2027 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/);
2028 if (!pImportMod)
2029 {
2030 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2031 pszImport, pMod->pszPath);
2032 break;
2033 }
2034 }
2035 paThunks[iThunk].u1.AddressOfData
2036 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2037 }
2038 break;
2039 }
2040 }
2041 }
2042 }
2043
2044
2045 /* Next import descriptor. */
2046 pImpDesc++;
2047 }
2048
2049
2050 if (fOldProt)
2051 {
2052 DWORD fIgnore = 0;
2053 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2054 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2055 }
2056 }
2057
2058}
2059
2060
2061/**
2062 * Creates a module from a native kLdr module handle.
2063 *
2064 * @returns Module w/ 1 reference on success, NULL on failure.
2065 * @param pLdrMod The native kLdr module.
2066 * @param pszPath The normalized path to the module.
2067 * @param cbPath The module path length with terminator.
2068 * @param uHashPath The module path hash.
2069 * @param fDoReplacements Whether to do import replacements on this
2070 * module.
2071 */
2072static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2073 KBOOL fDoReplacements)
2074{
2075 /*
2076 * Create the entry.
2077 */
2078 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2079 if (pMod)
2080 {
2081 pMod->pwszPath = (wchar_t *)(pMod + 1);
2082 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2083 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2084 pMod->uHashPath = uHashPath;
2085 pMod->cRefs = 1;
2086 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2087 pMod->fExe = K_FALSE;
2088 pMod->fNative = K_TRUE;
2089 pMod->pLdrMod = pLdrMod;
2090 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2091 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2092 pMod->iCrtSlot = KU8_MAX;
2093 pMod->fNeedReInit = K_FALSE;
2094 pMod->pszMsPdbSrvEndpoint = NULL;
2095 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2096
2097 if (fDoReplacements)
2098 {
2099 DWORD const dwSavedErr = GetLastError();
2100 kwLdrModuleDoNativeImportReplacements(pMod);
2101 SetLastError(dwSavedErr);
2102 }
2103
2104 KW_LOG(("New module: %p LB %#010x %s (native)\n",
2105 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
2106 g_cModules++;
2107 return kwLdrModuleLink(pMod);
2108 }
2109 return NULL;
2110}
2111
2112
2113
2114/**
2115 * Creates a module using the native loader.
2116 *
2117 * @returns Module w/ 1 reference on success, NULL on failure.
2118 * @param pszPath The normalized path to the module.
2119 * @param uHashPath The module path hash.
2120 * @param fDoReplacements Whether to do import replacements on this
2121 * module.
2122 */
2123static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2124{
2125 /*
2126 * Open the module and check the type.
2127 */
2128 PKLDRMOD pLdrMod;
2129 int rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2130 if (rc == 0)
2131 {
2132 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
2133 uHashPath, fDoReplacements);
2134 if (pMod)
2135 return pMod;
2136 kLdrModClose(pLdrMod);
2137 }
2138 return NULL;
2139}
2140
2141
2142/**
2143 * Sets up the quick zero & copy tables for the non-native module.
2144 *
2145 * This is a worker for kwLdrModuleCreateNonNative.
2146 *
2147 * @param pMod The module.
2148 */
2149static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2150{
2151 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2152 KU32 cSegs = pMod->pLdrMod->cSegments;
2153 KU32 iSeg;
2154
2155 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2156 pMod->u.Manual.cQuickCopyChunks = 0;
2157 pMod->u.Manual.cQuickZeroChunks = 0;
2158
2159 for (iSeg = 0; iSeg < cSegs; iSeg++)
2160 switch (paSegs[iSeg].enmProt)
2161 {
2162 case KPROT_READWRITE:
2163 case KPROT_WRITECOPY:
2164 case KPROT_EXECUTE_READWRITE:
2165 case KPROT_EXECUTE_WRITECOPY:
2166 if (paSegs[iSeg].cbMapped)
2167 {
2168 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2169 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2170 {
2171 /*
2172 * Check for trailing zero words.
2173 */
2174 KSIZE cbTrailingZeros;
2175 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2176 && (paSegs[iSeg].cbMapped & 7) == 0
2177 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2178 {
2179 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2180 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2181 KSIZE idxFirstZero = cNatural;
2182 while (idxFirstZero > 0)
2183 if (pauNatural[--idxFirstZero] == 0)
2184 { /* likely */ }
2185 else
2186 {
2187 idxFirstZero++;
2188 break;
2189 }
2190 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2191 if (cbTrailingZeros < 128)
2192 cbTrailingZeros = 0;
2193 }
2194 else
2195 cbTrailingZeros = 0;
2196
2197 /*
2198 * Add quick copy entry.
2199 */
2200 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2201 {
2202 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2203 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2204 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2205 pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
2206 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2207 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2208 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2209 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2210 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2211 }
2212
2213 /*
2214 * Add quick zero entry.
2215 */
2216 if (cbTrailingZeros)
2217 {
2218 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2219 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2220 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2221 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2222 pMod->u.Manual.cQuickZeroChunks = iZero + 1;
2223 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2224 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2225 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2226 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2227 }
2228 }
2229 else
2230 {
2231 /*
2232 * We're out of quick copy table entries, so just copy the whole darn thing.
2233 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2234 */
2235 kHlpAssertFailed();
2236 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2237 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2238 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2239 pMod->u.Manual.cQuickCopyChunks = 1;
2240 KWLDR_LOG(("Quick copy not possible!\n"));
2241 return;
2242 }
2243 }
2244 break;
2245
2246 default:
2247 break;
2248 }
2249}
2250
2251
2252/**
2253 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2254 *
2255 * @param hDll The DLL handle.
2256 * @param idxTls The allocated TLS index.
2257 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2258 */
2259__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2260{
2261 /*
2262 * Do the module initialization thing first.
2263 */
2264 PKWMODULE pMod = g_pModPendingTlsAlloc;
2265 if (pMod)
2266 {
2267 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2268 LIST_ENTRY *pHead;
2269 LIST_ENTRY *pCur;
2270
2271 pMod->u.Manual.idxTls = idxTls;
2272 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2273
2274 /*
2275 * Try sabotage the DLL name so we can load this module again.
2276 */
2277 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2278 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2279 {
2280 LDR_DATA_TABLE_ENTRY *pMte;
2281 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2282 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2283 {
2284 PUNICODE_STRING pStr = &pMte->FullDllName;
2285 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2286 pStr->Buffer[--off]++;
2287 pStr->Buffer[--off]++;
2288 pStr->Buffer[--off]++;
2289 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2290 break;
2291 }
2292 }
2293 }
2294}
2295
2296
2297/**
2298 * Allocates and initializes TLS variables.
2299 *
2300 * @returns 0 on success, non-zero failure.
2301 * @param pMod The module.
2302 */
2303static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2304{
2305 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2306 IMAGE_NT_HEADERS const *pNtHdrs;
2307 IMAGE_DATA_DIRECTORY const *pTlsDir;
2308
2309 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2310 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2311 else
2312 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2313 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2314
2315 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2316 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2317 {
2318 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2319 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2320 KU32 iEntry;
2321 KUPTR offIndex;
2322 KUPTR offCallbacks;
2323 KUPTR const *puCallbacks;
2324 KSIZE cbData;
2325 const wchar_t *pwszTlsDll;
2326 HMODULE hmodTlsDll;
2327
2328 /*
2329 * Check and log.
2330 */
2331 for (iEntry = 0; iEntry < cEntries; iEntry++)
2332 {
2333 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2334 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2335 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2336 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2337 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2338 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2339 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2340
2341 if (offIndex >= pMod->cbImage)
2342 {
2343 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2344 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2345 return -1;
2346 }
2347 if (offCallbacks >= pMod->cbImage)
2348 {
2349 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2350 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2351 return -1;
2352 }
2353 while (*puCallbacks != 0)
2354 {
2355 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2356 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2357 puCallbacks++;
2358 }
2359 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2360 {
2361 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2362 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2363 return -1;
2364 }
2365 }
2366
2367 if (cEntries > 1)
2368 {
2369 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2370 return -1;
2371 }
2372
2373 /*
2374 * Make the allocation by loading a new instance of one of the TLS dlls.
2375 * The DLL will make a call to
2376 */
2377 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2378 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2379 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2380 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2381 if (cbData <= 1024)
2382 pwszTlsDll = L"kWorkerTls1K.dll";
2383 else if (cbData <= 65536)
2384 pwszTlsDll = L"kWorkerTls64K.dll";
2385 else if (cbData <= 524288)
2386 pwszTlsDll = L"kWorkerTls512K.dll";
2387 else
2388 {
2389 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2390 return -1;
2391 }
2392
2393 pMod->u.Manual.idxTls = KU32_MAX;
2394 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2395 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2396 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2397 pMod->u.Manual.cTlsCallbacks = 0;
2398 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2399 pMod->u.Manual.cTlsCallbacks++;
2400 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2401
2402 g_pModPendingTlsAlloc = pMod;
2403 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2404 g_pModPendingTlsAlloc = NULL;
2405 if (hmodTlsDll == NULL)
2406 {
2407 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2408 return -1;
2409 }
2410 if (pMod->u.Manual.idxTls == KU32_MAX)
2411 {
2412 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2413 return -1;
2414 }
2415
2416 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2417 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2418 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2419 }
2420 return 0;
2421}
2422
2423
2424/**
2425 * Creates a module using the our own loader.
2426 *
2427 * @returns Module w/ 1 reference on success, NULL on failure.
2428 * @param pszPath The normalized path to the module.
2429 * @param uHashPath The module path hash.
2430 * @param fExe K_TRUE if this is an executable image, K_FALSE
2431 * if not. Executable images does not get entered
2432 * into the global module table.
2433 * @param pExeMod The executable module of the process (for
2434 * resolving imports). NULL if fExe is set.
2435 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2436 */
2437static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2438 PKWMODULE pExeMod, const char *pszSearchPath)
2439{
2440 /*
2441 * Open the module and check the type.
2442 */
2443 PKLDRMOD pLdrMod;
2444 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2445 if (rc == 0)
2446 {
2447 switch (pLdrMod->enmType)
2448 {
2449 case KLDRTYPE_EXECUTABLE_FIXED:
2450 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2451 case KLDRTYPE_EXECUTABLE_PIC:
2452 if (!fExe)
2453 rc = KERR_GENERAL_FAILURE;
2454 break;
2455
2456 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2457 case KLDRTYPE_SHARED_LIBRARY_PIC:
2458 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2459 if (fExe)
2460 rc = KERR_GENERAL_FAILURE;
2461 break;
2462
2463 default:
2464 rc = KERR_GENERAL_FAILURE;
2465 break;
2466 }
2467 if (rc == 0)
2468 {
2469 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2470 if (cImports >= 0)
2471 {
2472 /*
2473 * Create the entry.
2474 */
2475 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2476 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2477 + sizeof(pMod) * cImports
2478 + cbPath
2479 + cbPath * 2 * sizeof(wchar_t));
2480 if (pMod)
2481 {
2482 KBOOL fFixed;
2483
2484 pMod->cRefs = 1;
2485 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2486 pMod->uHashPath = uHashPath;
2487 pMod->fExe = fExe;
2488 pMod->fNative = K_FALSE;
2489 pMod->pLdrMod = pLdrMod;
2490 pMod->iCrtSlot = KU8_MAX;
2491 pMod->fNeedReInit = K_FALSE;
2492 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2493 pMod->pszMsPdbSrvEndpoint = NULL;
2494 pMod->u.Manual.cImpMods = (KU32)cImports;
2495#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2496 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2497#endif
2498 pMod->u.Manual.fUseLdBuf = K_FALSE;
2499 pMod->u.Manual.fCanDoQuick = K_FALSE;
2500 pMod->u.Manual.cQuickZeroChunks = 0;
2501 pMod->u.Manual.cQuickCopyChunks = 0;
2502 pMod->u.Manual.idxTls = KU32_MAX;
2503 pMod->u.Manual.offTlsInitData = KU32_MAX;
2504 pMod->u.Manual.cbTlsInitData = 0;
2505 pMod->u.Manual.cbTlsAlloc = 0;
2506 pMod->u.Manual.cTlsCallbacks = 0;
2507 pMod->u.Manual.offTlsCallbacks = 0;
2508 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2509 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2510 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2511
2512 /*
2513 * Figure out where to load it and get memory there.
2514 */
2515 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2516 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2517 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2518 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2519 if ( !fFixed
2520 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2521 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2522 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2523 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2524 else
2525 pMod->u.Manual.fUseLdBuf = K_TRUE;
2526 if (rc == 0)
2527 {
2528 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2529 if (rc == 0)
2530 {
2531 KI32 iImp;
2532
2533 /*
2534 * Link the module (unless it's an executable image) and process the imports.
2535 */
2536 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2537 kwLdrModuleLink(pMod);
2538 KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2539 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2540 KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2541 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2542
2543 for (iImp = 0; iImp < cImports; iImp++)
2544 {
2545 char szName[1024];
2546 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2547 if (rc == 0)
2548 {
2549 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2550 &pMod->u.Manual.apImpMods[iImp]);
2551 if (rc == 0)
2552 continue;
2553 }
2554 break;
2555 }
2556
2557 if (rc == 0)
2558 {
2559 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2560 kwLdrModuleGetImportCallback, pMod);
2561 if (rc == 0)
2562 {
2563#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2564 /*
2565 * Find the function table. No validation here because the
2566 * loader did that already, right...
2567 */
2568 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2569 IMAGE_NT_HEADERS const *pNtHdrs;
2570 IMAGE_DATA_DIRECTORY const *pXcptDir;
2571 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2572 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2573 else
2574 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2575 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2576 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2577 if (pXcptDir->Size > 0)
2578 {
2579 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2580 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2581 == pXcptDir->Size);
2582 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2583 }
2584 else
2585 {
2586 pMod->u.Manual.cFunctions = 0;
2587 pMod->u.Manual.paFunctions = NULL;
2588 }
2589#endif
2590
2591 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2592
2593 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2594 if (rc == 0)
2595 {
2596 /*
2597 * Final finish.
2598 */
2599 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2600 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2601 g_cModules++;
2602 g_cNonNativeModules++;
2603 return pMod;
2604 }
2605 }
2606 else
2607 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2608 }
2609
2610 kwLdrModuleRelease(pMod);
2611 return NULL;
2612 }
2613
2614 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2615 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2616 }
2617 else if (fFixed)
2618 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2619 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2620 else
2621 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2622 }
2623 }
2624 }
2625 kLdrModClose(pLdrMod);
2626 }
2627 else
2628 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2629 return NULL;
2630}
2631
2632
2633/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2634static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2635 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2636{
2637 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2638 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2639 int rc;
2640 K_NOREF(pMod);
2641
2642 if (pImpMod->fNative)
2643 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2644 iSymbol, pchSymbol, cchSymbol, pszVersion,
2645 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2646 puValue, pfKind);
2647 else
2648 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2649 iSymbol, pchSymbol, cchSymbol, pszVersion,
2650 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2651 puValue, pfKind);
2652 if (rc == 0)
2653 {
2654 KU32 i = g_cSandboxReplacements;
2655 while (i-- > 0)
2656 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2657 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2658 {
2659 if ( !g_aSandboxReplacements[i].pszModule
2660 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2661 {
2662 if ( pCurMod->fExe
2663 || !g_aSandboxReplacements[i].fOnlyExe)
2664 {
2665 KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2666 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2667 }
2668 break;
2669 }
2670 }
2671 }
2672
2673 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2674 KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2675 return rc;
2676
2677}
2678
2679
2680/**
2681 * Gets the main entrypoint for a module.
2682 *
2683 * @returns 0 on success, KERR on failure
2684 * @param pMod The module.
2685 * @param puAddrMain Where to return the address.
2686 */
2687static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2688{
2689 KLDRADDR uLdrAddrMain;
2690 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2691 if (rc == 0)
2692 {
2693 *puAddrMain = (KUPTR)uLdrAddrMain;
2694 return 0;
2695 }
2696 return rc;
2697}
2698
2699
2700/**
2701 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2702 *
2703 * @returns K_TRUE/K_FALSE.
2704 * @param pszFilename The filename (no path).
2705 * @param enmLocation The location.
2706 */
2707static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2708{
2709 if (enmLocation != KWLOCATION_SYSTEM32)
2710 return K_TRUE;
2711 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2712 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2713 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2714}
2715
2716
2717/**
2718 * Lazily initializes the g_pWinSys32 variable.
2719 */
2720static PKFSDIR kwLdrResolveWinSys32(void)
2721{
2722 KFSLOOKUPERROR enmError;
2723 PKFSDIR pWinSys32;
2724
2725 /* Get the path first. */
2726 char szSystem32[MAX_PATH];
2727 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2728 {
2729 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2730 strcpy(szSystem32, "C:\\Windows\\System32");
2731 }
2732
2733 /* Look it up and verify it. */
2734 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2735 if (pWinSys32)
2736 {
2737 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2738 {
2739 g_pWinSys32 = pWinSys32;
2740 return pWinSys32;
2741 }
2742
2743 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2744 }
2745 else
2746 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2747 return NULL;
2748}
2749
2750
2751/**
2752 * Whether we can load this DLL natively or not.
2753 *
2754 * @returns K_TRUE/K_FALSE.
2755 * @param pszFilename The filename (no path).
2756 * @param enmLocation The location.
2757 * @param pszFullPath The full filename and path.
2758 */
2759static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2760{
2761 if (enmLocation == KWLOCATION_SYSTEM32)
2762 return K_TRUE;
2763 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2764 return K_TRUE;
2765
2766 /* If the location is unknown, we must check if it's some dynamic loading
2767 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
2768 if (enmLocation == KWLOCATION_UNKNOWN)
2769 {
2770 PKFSDIR pWinSys32 = g_pWinSys32;
2771 if (!pWinSys32)
2772 pWinSys32 = kwLdrResolveWinSys32();
2773 if (pWinSys32)
2774 {
2775 KFSLOOKUPERROR enmError;
2776 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2777 if (pFsObj)
2778 {
2779 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2780 kFsCacheObjRelease(g_pFsCache, pFsObj);
2781 if (fInWinSys32)
2782 return K_TRUE;
2783 }
2784 }
2785 }
2786
2787 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2788 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2789 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2790}
2791
2792
2793/**
2794 * Check if the path leads to a regular file (that exists).
2795 *
2796 * @returns K_TRUE / K_FALSE
2797 * @param pszPath Path to the file to check out.
2798 */
2799static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2800{
2801 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2802 KSIZE cchPath = kHlpStrLen(pszPath);
2803 if ( cchPath > 3
2804 && pszPath[cchPath - 4] == '.'
2805 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2806 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2807 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2808 {
2809 KFSLOOKUPERROR enmError;
2810 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2811 if (pFsObj)
2812 {
2813 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2814 kFsCacheObjRelease(g_pFsCache, pFsObj);
2815 return fRc;
2816 }
2817 }
2818 else
2819 {
2820 BirdStat_T Stat;
2821 int rc = birdStatFollowLink(pszPath, &Stat);
2822 if (rc == 0)
2823 {
2824 if (S_ISREG(Stat.st_mode))
2825 return K_TRUE;
2826 }
2827 }
2828 return K_FALSE;
2829}
2830
2831
2832/**
2833 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2834 *
2835 * If the file exists, we consult the module hash table before trying to load it
2836 * off the disk.
2837 *
2838 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2839 * failure.
2840 * @param pszPath The name of the import module.
2841 * @param enmLocation The location we're searching. This is used in
2842 * the heuristics for determining if we can use the
2843 * native loader or need to sandbox the DLL.
2844 * @param pExe The executable (optional).
2845 * @param pszSearchPath The PATH to search (optional).
2846 */
2847static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2848{
2849 /*
2850 * Does the file exists and is it a regular file?
2851 */
2852 if (kwLdrModuleIsRegularFile(pszPath))
2853 {
2854 /*
2855 * Yes! Normalize it and look it up in the hash table.
2856 */
2857 char szNormPath[1024];
2858 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2859 if (rc == 0)
2860 {
2861 const char *pszName;
2862 KU32 const uHashPath = kwStrHash(szNormPath);
2863 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2864 PKWMODULE pMod = g_apModules[idxHash];
2865 if (pMod)
2866 {
2867 do
2868 {
2869 if ( pMod->uHashPath == uHashPath
2870 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2871 return kwLdrModuleRetain(pMod);
2872 pMod = pMod->pNextHash;
2873 } while (pMod);
2874 }
2875
2876 /*
2877 * Not in the hash table, so we have to load it from scratch.
2878 */
2879 pszName = kHlpGetFilename(szNormPath);
2880 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2881 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2882 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2883 else
2884 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2885 if (pMod)
2886 return pMod;
2887 return (PKWMODULE)~(KUPTR)0;
2888 }
2889 }
2890 return NULL;
2891}
2892
2893
2894/**
2895 * Gets a reference to the module by the given name.
2896 *
2897 * We must do the search path thing, as our hash table may multiple DLLs with
2898 * the same base name due to different tools version and similar. We'll use a
2899 * modified search sequence, though. No point in searching the current
2900 * directory for instance.
2901 *
2902 * @returns 0 on success, KERR on failure.
2903 * @param pszName The name of the import module.
2904 * @param pExe The executable (optional).
2905 * @param pImporter The module doing the importing (optional).
2906 * @param pszSearchPath The PATH to search (optional).
2907 * @param ppMod Where to return the module pointer w/ reference.
2908 */
2909static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2910 const char *pszSearchPath, PKWMODULE *ppMod)
2911{
2912 KSIZE const cchName = kHlpStrLen(pszName);
2913 char szPath[1024];
2914 char *psz;
2915 PKWMODULE pMod = NULL;
2916 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2917 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
2918
2919
2920 /* The import path. */
2921 if (pMod == NULL && pImporter != NULL)
2922 {
2923 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2924 return KERR_BUFFER_OVERFLOW;
2925
2926 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2927 if (fNeedSuffix)
2928 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2929 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2930 }
2931
2932 /* Application directory first. */
2933 if (pMod == NULL && pExe != NULL && pExe != pImporter)
2934 {
2935 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2936 return KERR_BUFFER_OVERFLOW;
2937 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2938 if (fNeedSuffix)
2939 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2940 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2941 }
2942
2943 /* The windows directory. */
2944 if (pMod == NULL)
2945 {
2946 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2947 if ( cchDir <= 2
2948 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2949 return KERR_BUFFER_OVERFLOW;
2950 szPath[cchDir++] = '\\';
2951 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2952 if (fNeedSuffix)
2953 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2954 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2955 }
2956
2957 /* The path. */
2958 if ( pMod == NULL
2959 && pszSearchPath)
2960 {
2961 const char *pszCur = pszSearchPath;
2962 while (*pszCur != '\0')
2963 {
2964 /* Find the end of the component */
2965 KSIZE cch = 0;
2966 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2967 cch++;
2968
2969 if ( cch > 0 /* wrong, but whatever */
2970 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2971 {
2972 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2973 if ( szPath[cch - 1] != ':'
2974 && szPath[cch - 1] != '/'
2975 && szPath[cch - 1] != '\\')
2976 *pszDst++ = '\\';
2977 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2978 if (fNeedSuffix)
2979 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2980 *pszDst = '\0';
2981
2982 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2983 if (pMod)
2984 break;
2985 }
2986
2987 /* Advance */
2988 pszCur += cch;
2989 while (*pszCur == ';')
2990 pszCur++;
2991 }
2992 }
2993
2994 /* Return. */
2995 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
2996 {
2997 *ppMod = pMod;
2998 return 0;
2999 }
3000 *ppMod = NULL;
3001 return KERR_GENERAL_FAILURE;
3002}
3003
3004
3005/**
3006 * Creates a CRT slot for the given module.
3007 *
3008 * @returns 0 on success, non-zero on failure.
3009 * @param pModule The module.
3010 */
3011static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3012{
3013 KSIZE iSlot;
3014 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3015 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3016 if (g_aCrtSlots[iSlot].pModule == NULL)
3017 {
3018 KLDRADDR uAddr;
3019 int rc;
3020
3021 /* Do the linking: */
3022 g_aCrtSlots[iSlot].pModule = pModule;
3023 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3024 pModule->iCrtSlot = (KU8)iSlot;
3025
3026 /* resolve symbols: */
3027 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3028 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3029 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3030 if (rc != 0)
3031 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3032
3033 return 0;
3034 }
3035 kwErrPrintf("Out of CRT slots!\n");
3036 return KERR_NO_MEMORY;
3037}
3038
3039
3040/**
3041 * Locates the module structure for an already loaded native module.
3042 *
3043 * This will create a module structure if needed.
3044 *
3045 * @returns Pointer to the module structure on success, NULL on failure.
3046 * @param pszName The name of the module.
3047 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3048 */
3049static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot)
3050{
3051 /*
3052 * Locate the module and get a normalized path for it.
3053 */
3054 HANDLE hModule = GetModuleHandleA(pszName);
3055 if (hModule)
3056 {
3057 char szModPath[1024];
3058 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3059 {
3060 char szNormPath[1024];
3061 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3062 if (rc == 0)
3063 {
3064 /*
3065 * Hash the path and look it up.
3066 */
3067 KU32 uHashPath;
3068 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3069 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3070 PKWMODULE pMod = g_apModules[idxHash];
3071 if (pMod)
3072 {
3073 do
3074 {
3075 if ( pMod->uHashPath == uHashPath
3076 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3077 {
3078 kwLdrModuleRetain(pMod);
3079 break;
3080 }
3081 pMod = pMod->pNextHash;
3082 } while (pMod);
3083 }
3084
3085 /*
3086 * If not in the hash table, so create a module entry.
3087 */
3088 if (!pMod)
3089 {
3090 PKLDRMOD pLdrMod;
3091 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3092 if (rc == 0)
3093 {
3094 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3095 K_FALSE /*fDoReplacements*/);
3096 if (!pMod)
3097 {
3098 kLdrModClose(pLdrMod);
3099 kwErrPrintf("out of memory\n");
3100 }
3101 }
3102 else
3103 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
3104 }
3105 if (pMod)
3106 {
3107 /*
3108 * Create a CRT slot for the module if necessary.
3109 */
3110 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3111 return pMod;
3112 rc = kwLdrModuleCreateCrtSlot(pMod);
3113 if (rc == 0)
3114 return pMod;
3115 kwLdrModuleRelease(pMod);
3116 }
3117 }
3118 else
3119 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszName, GetLastError());
3120 }
3121 else
3122 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszName, GetLastError());
3123 }
3124 else
3125 kwErrPrintf("Module '%s' was not found by GetModuleHandleA!\n", pszName);
3126 return NULL;
3127}
3128
3129
3130/**
3131 * Does the TLS memory initialization for a module on the current thread.
3132 *
3133 * @returns 0 on success, error on failure.
3134 * @param pMod The module.
3135 */
3136static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3137{
3138 if (pMod->u.Manual.idxTls != KU32_MAX)
3139 {
3140 PTEB pTeb = NtCurrentTeb();
3141 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3142 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3143 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3144 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3145 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3146 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3147 if (pMod->u.Manual.cbTlsInitData)
3148 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3149 }
3150 return 0;
3151}
3152
3153
3154/**
3155 * Does the TLS callbacks for a module.
3156 *
3157 * @param pMod The module.
3158 * @param dwReason The callback reason.
3159 */
3160static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3161{
3162 if (pMod->u.Manual.cTlsCallbacks)
3163 {
3164 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3165 do
3166 {
3167 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3168 (*pCallback)(pMod->hOurMod, dwReason, 0);
3169 } while (*++pCallback);
3170 }
3171}
3172
3173
3174/**
3175 * Does module initialization starting at @a pMod.
3176 *
3177 * This is initially used on the executable. Later it is used by the
3178 * LoadLibrary interceptor.
3179 *
3180 * @returns 0 on success, error on failure.
3181 * @param pMod The module to initialize.
3182 */
3183static int kwLdrModuleInitTree(PKWMODULE pMod)
3184{
3185 int rc = 0;
3186 if (!pMod->fNative)
3187 {
3188 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3189 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3190
3191 /*
3192 * Need to copy bits?
3193 */
3194 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3195 {
3196 if (pMod->u.Manual.fUseLdBuf)
3197 {
3198#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3199 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3200 {
3201 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3202 kHlpAssert(fRc); K_NOREF(fRc);
3203 }
3204#endif
3205 g_pModPrevInLdBuf = g_pModInLdBuf;
3206 g_pModInLdBuf = pMod;
3207 }
3208
3209 /* Do quick zeroing and copying when we can. */
3210 pMod->u.Manual.fCanDoQuick = K_FALSE;
3211 if ( pMod->u.Manual.fCanDoQuick
3212 && ( !pMod->u.Manual.fUseLdBuf
3213 || g_pModPrevInLdBuf == pMod))
3214 {
3215 /* Zero first. */
3216 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3217 switch (pMod->u.Manual.cQuickZeroChunks)
3218 {
3219 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3220 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3221 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3222 case 0: break;
3223 }
3224
3225 /* Then copy. */
3226 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3227 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3228 switch (pMod->u.Manual.cQuickCopyChunks)
3229 {
3230 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3231 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3232 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3233 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3234 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3235 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3236 case 0: break;
3237 }
3238 }
3239 /* Must copy the whole image. */
3240 else
3241 {
3242 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3243 pMod->u.Manual.fCanDoQuick = K_TRUE;
3244 }
3245 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3246 }
3247
3248#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3249 /*
3250 * Need to register function table?
3251 */
3252 if ( !pMod->u.Manual.fRegisteredFunctionTable
3253 && pMod->u.Manual.cFunctions > 0)
3254 {
3255 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3256 pMod->u.Manual.cFunctions,
3257 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3258 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3259 }
3260#endif
3261
3262
3263 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3264 {
3265 /*
3266 * Must do imports first, but mark our module as being initialized to avoid
3267 * endless recursion should there be a dependency loop.
3268 */
3269 KSIZE iImp;
3270 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3271
3272 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3273 {
3274 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3275 if (rc != 0)
3276 return rc;
3277 }
3278
3279 /* Do TLS allocations for module init? */
3280 rc = kwLdrCallTlsAllocateAndInit(pMod);
3281 if (rc != 0)
3282 return rc;
3283 if (pMod->u.Manual.cTlsCallbacks > 0)
3284 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3285
3286 /* Finally call the entry point. */
3287 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3288 if (rc == 0)
3289 pMod->u.Manual.enmState = KWMODSTATE_READY;
3290 else
3291 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3292 }
3293 }
3294 /*
3295 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3296 * _MSPDBSRV_ENDPOINT_ changes value.
3297 */
3298 else if (pMod->fNeedReInit)
3299 {
3300 int rc2;
3301 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3302 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3303 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3304 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3305 if (!rc && !rc2)
3306 { /* likely */ }
3307 else
3308 {
3309 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3310 if (rc2 && !rc)
3311 rc = rc2;
3312 }
3313 pMod->fNeedReInit = K_FALSE;
3314 }
3315 return rc;
3316}
3317
3318
3319/**
3320 * Looks up a module handle for a tool.
3321 *
3322 * @returns Referenced loader module on success, NULL on if not found.
3323 * @param pTool The tool.
3324 * @param hmod The module handle.
3325 */
3326static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3327{
3328 KUPTR const uHMod = (KUPTR)hmod;
3329 PKWMODULE *papMods;
3330 KU32 iEnd;
3331 KU32 i;
3332 PKWDYNLOAD pDynLoad;
3333
3334 /* The executable. */
3335 if ( hmod == NULL
3336 || pTool->u.Sandboxed.pExe->hOurMod == hmod)
3337 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3338
3339 /*
3340 * Binary lookup using the module table.
3341 */
3342 papMods = pTool->u.Sandboxed.papModules;
3343 iEnd = pTool->u.Sandboxed.cModules;
3344 if (iEnd)
3345 {
3346 KU32 iStart = 0;
3347 i = iEnd / 2;
3348 for (;;)
3349 {
3350 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3351 if (uHMod < uHModThis)
3352 {
3353 iEnd = i--;
3354 if (iStart <= i)
3355 { }
3356 else
3357 break;
3358 }
3359 else if (uHMod != uHModThis)
3360 {
3361 iStart = ++i;
3362 if (i < iEnd)
3363 { }
3364 else
3365 break;
3366 }
3367 else
3368 return kwLdrModuleRetain(papMods[i]);
3369
3370 i = iStart + (iEnd - iStart) / 2;
3371 }
3372
3373#ifndef NDEBUG
3374 iStart = pTool->u.Sandboxed.cModules;
3375 while (--iStart > 0)
3376 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3377 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3378 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3379#endif
3380 }
3381
3382 /*
3383 * Dynamically loaded images.
3384 */
3385 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3386 if (pDynLoad->hmod == hmod)
3387 {
3388 if (pDynLoad->pMod)
3389 return kwLdrModuleRetain(pDynLoad->pMod);
3390 KWFS_TODO();
3391 return NULL;
3392 }
3393
3394 return NULL;
3395}
3396
3397/**
3398 * Adds the given module to the tool import table.
3399 *
3400 * @returns 0 on success, non-zero on failure.
3401 * @param pTool The tool.
3402 * @param pMod The module.
3403 */
3404static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3405{
3406 /*
3407 * Binary lookup. Locating the right slot for it, return if already there.
3408 */
3409 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3410 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3411 KU32 iEnd = pTool->u.Sandboxed.cModules;
3412 KU32 i;
3413 if (iEnd)
3414 {
3415 KU32 iStart = 0;
3416 i = iEnd / 2;
3417 for (;;)
3418 {
3419 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3420 if (uHMod < uHModThis)
3421 {
3422 iEnd = i;
3423 if (iStart < i)
3424 { }
3425 else
3426 break;
3427 }
3428 else if (uHMod != uHModThis)
3429 {
3430 iStart = ++i;
3431 if (i < iEnd)
3432 { }
3433 else
3434 break;
3435 }
3436 else
3437 {
3438 /* Already there in the table. */
3439 return 0;
3440 }
3441
3442 i = iStart + (iEnd - iStart) / 2;
3443 }
3444#ifndef NDEBUG
3445 iStart = pTool->u.Sandboxed.cModules;
3446 while (--iStart > 0)
3447 {
3448 kHlpAssert(papMods[iStart] != pMod);
3449 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3450 }
3451 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3452 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3453#endif
3454 }
3455 else
3456 i = 0;
3457
3458 /*
3459 * Grow the table?
3460 */
3461 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3462 {
3463 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3464 if (!pvNew)
3465 return KERR_NO_MEMORY;
3466 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3467 }
3468
3469 /* Insert it. */
3470 if (i != pTool->u.Sandboxed.cModules)
3471 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3472 papMods[i] = kwLdrModuleRetain(pMod);
3473 pTool->u.Sandboxed.cModules++;
3474 KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3475 return 0;
3476}
3477
3478
3479/**
3480 * Adds the given module and all its imports to the
3481 *
3482 * @returns 0 on success, non-zero on failure.
3483 * @param pTool The tool.
3484 * @param pMod The module.
3485 */
3486static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3487{
3488 int rc = kwToolAddModule(pTool, pMod);
3489 if (!pMod->fNative && rc == 0)
3490 {
3491 KSIZE iImp = pMod->u.Manual.cImpMods;
3492 while (iImp-- > 0)
3493 {
3494 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3495 if (rc == 0)
3496 { }
3497 else
3498 break;
3499 }
3500 }
3501 return 0;
3502}
3503
3504
3505/**
3506 * Creates a tool entry and inserts it.
3507 *
3508 * @returns Pointer to the tool entry. NULL on failure.
3509 * @param pToolFsObj The file object of the tool. The created tool
3510 * will be associated with it.
3511 *
3512 * A reference is donated by the caller and must be
3513 * released.
3514 * @param pszSearchPath The PATH environment variable value, or NULL.
3515 */
3516static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3517{
3518 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3519 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3520 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3521 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3522 if (pTool)
3523 {
3524 KBOOL fRc;
3525 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3526 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3527 kHlpAssert(fRc); K_NOREF(fRc);
3528
3529 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3530 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3531 kHlpAssert(fRc);
3532
3533 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3534 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3535 NULL /*pEexeMod*/, pszSearchPath);
3536 if (pTool->u.Sandboxed.pExe)
3537 {
3538 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3539 if (rc == 0)
3540 {
3541 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3542 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3543 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3544 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3545 else
3546 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3547 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3548 }
3549 else
3550 {
3551 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3552 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3553 pTool->u.Sandboxed.pExe = NULL;
3554 pTool->enmType = KWTOOLTYPE_EXEC;
3555 }
3556 }
3557 else
3558 pTool->enmType = KWTOOLTYPE_EXEC;
3559
3560 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3561 g_cTools++;
3562 return pTool;
3563 }
3564 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3565 return NULL;
3566}
3567
3568
3569/**
3570 * Looks up the given tool, creating a new tool table entry if necessary.
3571 *
3572 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3573 * @param pszExe The executable for the tool (not normalized).
3574 * @param cEnvVars Number of environment varibles.
3575 * @param papszEnvVars Environment variables. For getting the PATH.
3576 */
3577static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3578{
3579 /*
3580 * We associate the tools instances with the file system objects.
3581 *
3582 * We'd like to do the lookup without invaliding the volatile parts of the
3583 * cache, thus the double lookup here. The cache gets invalidate later on.
3584 */
3585 KFSLOOKUPERROR enmError;
3586 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3587 if ( !pToolFsObj
3588 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3589 {
3590 kFsCacheInvalidateCustomBoth(g_pFsCache);
3591 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3592 }
3593 if (pToolFsObj)
3594 {
3595 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3596 {
3597 const char *pszSearchPath;
3598 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3599 if (pTool)
3600 {
3601 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3602 return pTool;
3603 }
3604
3605 /*
3606 * Need to create a new tool.
3607 */
3608 pszSearchPath = NULL;
3609 while (cEnvVars-- > 0)
3610 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3611 {
3612 pszSearchPath = &papszEnvVars[cEnvVars][5];
3613 break;
3614 }
3615
3616 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3617 if (pTool)
3618 return pTool;
3619
3620 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3621 }
3622 else
3623 {
3624 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3625 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3626 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3627 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3628 }
3629 }
3630 else
3631 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3632 return NULL;
3633}
3634
3635
3636
3637/*
3638 *
3639 * File system cache.
3640 * File system cache.
3641 * File system cache.
3642 *
3643 */
3644
3645
3646/**
3647 * This is for kDep.
3648 */
3649int kwFsPathExists(const char *pszPath)
3650{
3651 BirdTimeSpec_T TsIgnored;
3652 KFSLOOKUPERROR enmError;
3653 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3654 if (pFsObj)
3655 {
3656 kFsCacheObjRelease(g_pFsCache, pFsObj);
3657 return 1;
3658 }
3659 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3660}
3661
3662
3663/* duplicated in dir-nt-bird.c */
3664void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3665{
3666 KFSLOOKUPERROR enmError;
3667 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3668 if (pPathObj)
3669 {
3670 KSIZE off = pPathObj->cchParent;
3671 if (off > 0)
3672 {
3673 KSIZE offEnd = off + pPathObj->cchName;
3674 if (offEnd < cbFull)
3675 {
3676 PKFSDIR pAncestor;
3677
3678 pszFull[off + pPathObj->cchName] = '\0';
3679 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3680
3681 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3682 {
3683 kHlpAssert(off > 1);
3684 kHlpAssert(pAncestor != NULL);
3685 kHlpAssert(pAncestor->Obj.cchName > 0);
3686 pszFull[--off] = '/';
3687 off -= pAncestor->Obj.cchName;
3688 kHlpAssert(pAncestor->Obj.cchParent == off);
3689 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3690 }
3691 kFsCacheObjRelease(g_pFsCache, pPathObj);
3692 return;
3693 }
3694 }
3695 else
3696 {
3697 if ((size_t)pPathObj->cchName + 1 < cbFull)
3698 {
3699 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3700 pszFull[pPathObj->cchName] = '/';
3701 pszFull[pPathObj->cchName + 1] = '\0';
3702
3703 kFsCacheObjRelease(g_pFsCache, pPathObj);
3704 return;
3705 }
3706 }
3707
3708 /* do fallback. */
3709 kHlpAssertFailed();
3710 kFsCacheObjRelease(g_pFsCache, pPathObj);
3711 }
3712
3713 nt_fullpath(pszPath, pszFull, cbFull);
3714}
3715
3716
3717/**
3718 * Helper for getting the extension of a UTF-16 path.
3719 *
3720 * @returns Pointer to the extension or the terminator.
3721 * @param pwszPath The path.
3722 * @param pcwcExt Where to return the length of the extension.
3723 */
3724static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3725{
3726 wchar_t const *pwszName = pwszPath;
3727 wchar_t const *pwszExt = NULL;
3728 for (;;)
3729 {
3730 wchar_t const wc = *pwszPath++;
3731 if (wc == '.')
3732 pwszExt = pwszPath;
3733 else if (wc == '/' || wc == '\\' || wc == ':')
3734 {
3735 pwszName = pwszPath;
3736 pwszExt = NULL;
3737 }
3738 else if (wc == '\0')
3739 {
3740 if (pwszExt)
3741 {
3742 *pcwcExt = pwszPath - pwszExt - 1;
3743 return pwszExt;
3744 }
3745 *pcwcExt = 0;
3746 return pwszPath - 1;
3747 }
3748 }
3749}
3750
3751
3752
3753/**
3754 * Parses the argument string passed in as pszSrc.
3755 *
3756 * @returns size of the processed arguments.
3757 * @param pszSrc Pointer to the commandline that's to be parsed.
3758 * @param pcArgs Where to return the number of arguments.
3759 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3760 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3761 *
3762 * @remarks Lifted from startuphacks-win.c
3763 */
3764static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3765{
3766 int bs;
3767 char chQuote;
3768 char *pfFlags;
3769 int cbArgs;
3770 int cArgs;
3771
3772#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3773#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3774#define WHITE(c) ((c) == ' ' || (c) == '\t')
3775
3776#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3777#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3778#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3779#define _ARG_ENV 0x08 /* Argument from environment */
3780#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3781
3782 cArgs = 0;
3783 cbArgs = 0;
3784
3785#if 0
3786 /* argv[0] */
3787 PUTC((char)_ARG_NONZERO);
3788 PUTV;
3789 for (;;)
3790 {
3791 PUTC(*pszSrc);
3792 if (*pszSrc == 0)
3793 break;
3794 ++pszSrc;
3795 }
3796 ++pszSrc;
3797#endif
3798
3799 for (;;)
3800 {
3801 while (WHITE(*pszSrc))
3802 ++pszSrc;
3803 if (*pszSrc == 0)
3804 break;
3805 pfFlags = pchPool;
3806 PUTC((char)_ARG_NONZERO);
3807 PUTV;
3808 bs = 0; chQuote = 0;
3809 for (;;)
3810 {
3811 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3812 {
3813 while (bs >= 2)
3814 {
3815 PUTC('\\');
3816 bs -= 2;
3817 }
3818 if (bs & 1)
3819 PUTC(*pszSrc);
3820 else
3821 {
3822 chQuote = chQuote ? 0 : *pszSrc;
3823 if (pfFlags != NULL)
3824 *pfFlags |= _ARG_DQUOTE;
3825 }
3826 bs = 0;
3827 }
3828 else if (*pszSrc == '\\')
3829 ++bs;
3830 else
3831 {
3832 while (bs != 0)
3833 {
3834 PUTC('\\');
3835 --bs;
3836 }
3837 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3838 break;
3839 PUTC(*pszSrc);
3840 }
3841 ++pszSrc;
3842 }
3843 PUTC(0);
3844 }
3845
3846 *pcArgs = cArgs;
3847 return cbArgs;
3848}
3849
3850
3851
3852
3853/*
3854 *
3855 * Process and thread related APIs.
3856 * Process and thread related APIs.
3857 * Process and thread related APIs.
3858 *
3859 */
3860
3861/** Common worker for ExitProcess(), exit() and friends. */
3862static void WINAPI kwSandboxDoExit(int uExitCode)
3863{
3864 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3865 {
3866 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3867
3868 g_Sandbox.rcExitCode = (int)uExitCode;
3869
3870 /* Before we jump, restore the TIB as we're not interested in any
3871 exception chain stuff installed by the sandboxed executable. */
3872 *pTib = g_Sandbox.TibMainThread;
3873 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3874
3875 longjmp(g_Sandbox.JmpBuf, 1);
3876 }
3877 KWFS_TODO();
3878}
3879
3880
3881/** ExitProcess replacement. */
3882static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3883{
3884 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3885 kwSandboxDoExit((int)uExitCode);
3886}
3887
3888
3889/** ExitProcess replacement. */
3890static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3891{
3892 if (hProcess == GetCurrentProcess())
3893 kwSandboxDoExit(uExitCode);
3894 KWFS_TODO();
3895 return TerminateProcess(hProcess, uExitCode);
3896}
3897
3898
3899/** Normal CRT exit(). */
3900static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3901{
3902 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3903 kwSandboxDoExit(rcExitCode);
3904}
3905
3906
3907/** Quick CRT _exit(). */
3908static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3909{
3910 /* Quick. */
3911 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3912 kwSandboxDoExit(rcExitCode);
3913}
3914
3915
3916/** Return to caller CRT _cexit(). */
3917static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3918{
3919 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3920 kwSandboxDoExit(rcExitCode);
3921}
3922
3923
3924/** Quick return to caller CRT _c_exit(). */
3925static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3926{
3927 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3928 kwSandboxDoExit(rcExitCode);
3929}
3930
3931
3932/** Runtime error and exit _amsg_exit(). */
3933static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3934{
3935 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3936 kwSandboxDoExit(255);
3937}
3938
3939
3940/** CRT - terminate(). */
3941static void __cdecl kwSandbox_msvcrt_terminate(void)
3942{
3943 KW_LOG(("\nRuntime - terminate!\n"));
3944 kwSandboxDoExit(254);
3945}
3946
3947
3948/** CRT - _onexit */
3949static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3950{
3951 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3952 {
3953 PKWEXITCALLACK pCallback;
3954 KW_LOG(("_onexit(%p)\n", pfnFunc));
3955 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3956
3957 pCallback = kHlpAlloc(sizeof(*pCallback));
3958 if (pCallback)
3959 {
3960 pCallback->pfnCallback = pfnFunc;
3961 pCallback->fAtExit = K_FALSE;
3962 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3963 g_Sandbox.pExitCallbackHead = pCallback;
3964 return pfnFunc;
3965 }
3966 return NULL;
3967 }
3968 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3969 return pfnFunc;
3970}
3971
3972
3973/** CRT - atexit */
3974static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3975{
3976 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3977 {
3978 PKWEXITCALLACK pCallback;
3979 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3980 KW_LOG(("atexit(%p)\n", pfnFunc));
3981
3982 pCallback = kHlpAlloc(sizeof(*pCallback));
3983 if (pCallback)
3984 {
3985 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3986 pCallback->fAtExit = K_TRUE;
3987 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3988 g_Sandbox.pExitCallbackHead = pCallback;
3989 return 0;
3990 }
3991 return -1;
3992 }
3993 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
3994 return 0;
3995}
3996
3997
3998/** Kernel32 - SetConsoleCtrlHandler(). */
3999static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4000{
4001 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4002 return TRUE;
4003}
4004
4005
4006/** The CRT internal __getmainargs() API. */
4007static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4008 int dowildcard, int const *piNewMode)
4009{
4010 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4011 *pargc = g_Sandbox.cArgs;
4012 *pargv = g_Sandbox.papszArgs;
4013 *penvp = g_Sandbox.environ;
4014
4015 /** @todo startinfo points at a newmode (setmode) value. */
4016 return 0;
4017}
4018
4019
4020/** The CRT internal __wgetmainargs() API. */
4021static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4022 int dowildcard, int const *piNewMode)
4023{
4024 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4025 *pargc = g_Sandbox.cArgs;
4026 *pargv = g_Sandbox.papwszArgs;
4027 *penvp = g_Sandbox.wenviron;
4028
4029 /** @todo startinfo points at a newmode (setmode) value. */
4030 return 0;
4031}
4032
4033
4034
4035/** Kernel32 - GetCommandLineA() */
4036static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4037{
4038 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4039 return g_Sandbox.pszCmdLine;
4040}
4041
4042
4043/** Kernel32 - GetCommandLineW() */
4044static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4045{
4046 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4047 return g_Sandbox.pwszCmdLine;
4048}
4049
4050
4051/** Kernel32 - GetStartupInfoA() */
4052static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4053{
4054 KW_LOG(("GetStartupInfoA\n"));
4055 GetStartupInfoA(pStartupInfo);
4056 pStartupInfo->lpReserved = NULL;
4057 pStartupInfo->lpTitle = NULL;
4058 pStartupInfo->lpReserved2 = NULL;
4059 pStartupInfo->cbReserved2 = 0;
4060}
4061
4062
4063/** Kernel32 - GetStartupInfoW() */
4064static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4065{
4066 KW_LOG(("GetStartupInfoW\n"));
4067 GetStartupInfoW(pStartupInfo);
4068 pStartupInfo->lpReserved = NULL;
4069 pStartupInfo->lpTitle = NULL;
4070 pStartupInfo->lpReserved2 = NULL;
4071 pStartupInfo->cbReserved2 = 0;
4072}
4073
4074
4075/** CRT - __p___argc(). */
4076static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4077{
4078 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4079 return &g_Sandbox.cArgs;
4080}
4081
4082
4083/** CRT - __p___argv(). */
4084static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4085{
4086 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4087 return &g_Sandbox.papszArgs;
4088}
4089
4090
4091/** CRT - __p___sargv(). */
4092static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4093{
4094 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4095 return &g_Sandbox.papwszArgs;
4096}
4097
4098
4099/** CRT - __p__acmdln(). */
4100static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4101{
4102 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4103 return (char **)&g_Sandbox.pszCmdLine;
4104}
4105
4106
4107/** CRT - __p__acmdln(). */
4108static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4109{
4110 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4111 return &g_Sandbox.pwszCmdLine;
4112}
4113
4114
4115/** CRT - __p__pgmptr(). */
4116static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4117{
4118 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4119 return &g_Sandbox.pgmptr;
4120}
4121
4122
4123/** CRT - __p__wpgmptr(). */
4124static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4125{
4126 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4127 return &g_Sandbox.wpgmptr;
4128}
4129
4130
4131/** CRT - _get_pgmptr(). */
4132static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4133{
4134 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4135 *ppszValue = g_Sandbox.pgmptr;
4136 return 0;
4137}
4138
4139
4140/** CRT - _get_wpgmptr(). */
4141static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4142{
4143 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4144 *ppwszValue = g_Sandbox.wpgmptr;
4145 return 0;
4146}
4147
4148/** Just in case. */
4149static void kwSandbox_msvcrt__wincmdln(void)
4150{
4151 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4152 KWFS_TODO();
4153}
4154
4155
4156/** Just in case. */
4157static void kwSandbox_msvcrt__wwincmdln(void)
4158{
4159 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4160 KWFS_TODO();
4161}
4162
4163/** CreateThread interceptor. */
4164static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4165 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4166 DWORD fFlags, PDWORD pidThread)
4167{
4168 HANDLE hThread = NULL;
4169 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4170 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4171 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4172 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4173 {
4174 /* Allow link::DbgThread. */
4175 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4176 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4177 }
4178 else
4179 KWFS_TODO();
4180 return hThread;
4181}
4182
4183
4184/** _beginthread - create a new thread. */
4185static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4186{
4187 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4188 KWFS_TODO();
4189 return 0;
4190}
4191
4192
4193/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4194static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4195 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4196 unsigned fCreate, unsigned *pidThread)
4197{
4198 /*
4199 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4200 * whatever it needs to.
4201 */
4202 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4203 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4204 pfnThreadProc, pvUser, fCreate, pidThread));
4205 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4206 {
4207 uintptr_t rcRet;
4208 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4209 if (!s_pfnReal)
4210 {
4211 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4212 if (!s_pfnReal)
4213 {
4214 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4215 __debugbreak();
4216 }
4217 }
4218 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4219 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4220 return rcRet;
4221 }
4222
4223 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4224 KWFS_TODO();
4225 return 0;
4226}
4227
4228
4229/** _beginthreadex - create a new thread. */
4230static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
4231 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4232 unsigned fCreate, unsigned *pidThread)
4233{
4234 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4235 KWFS_TODO();
4236 return 0;
4237}
4238
4239
4240/*
4241 *
4242 * Environment related APIs.
4243 * Environment related APIs.
4244 * Environment related APIs.
4245 *
4246 */
4247
4248/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4249static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4250{
4251 char *pszzEnv;
4252 char *pszCur;
4253 KSIZE cbNeeded = 1;
4254 KSIZE iVar = 0;
4255
4256 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4257
4258 /* Figure how space much we need first. */
4259 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4260 cbNeeded += kHlpStrLen(pszCur) + 1;
4261
4262 /* Allocate it. */
4263 pszzEnv = kHlpAlloc(cbNeeded);
4264 if (pszzEnv)
4265 {
4266 char *psz = pszzEnv;
4267 iVar = 0;
4268 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4269 {
4270 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4271 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4272 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4273 }
4274 *psz++ = '\0';
4275 kHlpAssert(psz - pszzEnv == cbNeeded);
4276 }
4277
4278 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4279#if 0
4280 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4281 pszCur = pszzEnv;
4282 iVar = 0;
4283 while (*pszCur)
4284 {
4285 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4286 iVar++;
4287 pszCur += kHlpStrLen(pszCur) + 1;
4288 }
4289 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4290 pszCur++;
4291 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4292#endif
4293 return pszzEnv;
4294}
4295
4296
4297/** Kernel32 - GetEnvironmentStrings */
4298static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4299{
4300 KW_LOG(("GetEnvironmentStrings!\n"));
4301 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4302}
4303
4304
4305/** Kernel32 - GetEnvironmentStringsW */
4306static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4307{
4308 wchar_t *pwszzEnv;
4309 wchar_t *pwszCur;
4310 KSIZE cwcNeeded = 1;
4311 KSIZE iVar = 0;
4312
4313 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4314
4315 /* Figure how space much we need first. */
4316 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4317 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4318
4319 /* Allocate it. */
4320 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4321 if (pwszzEnv)
4322 {
4323 wchar_t *pwsz = pwszzEnv;
4324 iVar = 0;
4325 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4326 {
4327 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4328 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4329 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4330 }
4331 *pwsz++ = '\0';
4332 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
4333 }
4334
4335 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4336 return pwszzEnv;
4337}
4338
4339
4340/** Kernel32 - FreeEnvironmentStringsA */
4341static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4342{
4343 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4344 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4345 kHlpFree(pszzEnv);
4346 return TRUE;
4347}
4348
4349
4350/** Kernel32 - FreeEnvironmentStringsW */
4351static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4352{
4353 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4354 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4355 kHlpFree(pwszzEnv);
4356 return TRUE;
4357}
4358
4359
4360/**
4361 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4362 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4363 *
4364 * @returns 0 on success, non-zero on failure.
4365 * @param pSandbox The sandbox.
4366 * @param cMin Minimum size, including terminator.
4367 */
4368static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4369{
4370 void *pvNew;
4371 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4372 KSIZE cNew = cOld + 256;
4373 while (cNew < cMin)
4374 cNew += 256;
4375
4376 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4377 if (pvNew)
4378 {
4379 pSandbox->environ = (char **)pvNew;
4380 pSandbox->environ[cOld] = NULL;
4381
4382 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4383 if (pvNew)
4384 {
4385 pSandbox->papszEnvVars = (char **)pvNew;
4386 pSandbox->papszEnvVars[cOld] = NULL;
4387
4388 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4389 if (pvNew)
4390 {
4391 pSandbox->wenviron = (wchar_t **)pvNew;
4392 pSandbox->wenviron[cOld] = NULL;
4393
4394 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4395 if (pvNew)
4396 {
4397 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4398 pSandbox->papwszEnvVars[cOld] = NULL;
4399
4400 pSandbox->cEnvVarsAllocated = cNew;
4401 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4402 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4403 return 0;
4404 }
4405 }
4406 }
4407 }
4408 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4409 return KERR_NO_MEMORY;
4410}
4411
4412
4413/**
4414 * Sets an environment variable, ANSI style.
4415 *
4416 * @returns 0 on success, non-zero on failure.
4417 * @param pSandbox The sandbox.
4418 * @param pchVar The variable name.
4419 * @param cchVar The length of the name.
4420 * @param pszValue The value.
4421 */
4422static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4423{
4424 /* Allocate and construct the new strings. */
4425 KSIZE cchTmp = kHlpStrLen(pszValue);
4426 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4427 if (pszNew)
4428 {
4429 wchar_t *pwszNew;
4430 kHlpMemCopy(pszNew, pchVar, cchVar);
4431 pszNew[cchVar] = '=';
4432 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4433 cchTmp += cchVar + 1;
4434 pszNew[cchTmp] = '\0';
4435
4436 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4437 if (pwszNew)
4438 {
4439 /* Look it up. */
4440 KSIZE iVar = 0;
4441 char *pszEnv;
4442 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4443 {
4444 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4445 && pszEnv[cchVar] == '=')
4446 {
4447 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4448 " iVar=%d: %p='%s' and %p='%ls'\n",
4449 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4450 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4451 iVar, pszNew, pszNew, pwszNew, pwszNew));
4452
4453 kHlpFree(pSandbox->papszEnvVars[iVar]);
4454 pSandbox->papszEnvVars[iVar] = pszNew;
4455 pSandbox->environ[iVar] = pszNew;
4456
4457 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4458 pSandbox->papwszEnvVars[iVar] = pwszNew;
4459 pSandbox->wenviron[iVar] = pwszNew;
4460 return 0;
4461 }
4462 iVar++;
4463 }
4464
4465 /* Not found, do we need to grow the table first? */
4466 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4467 kwSandboxGrowEnv(pSandbox, iVar + 2);
4468 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4469 {
4470 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4471
4472 pSandbox->papszEnvVars[iVar + 1] = NULL;
4473 pSandbox->papszEnvVars[iVar] = pszNew;
4474 pSandbox->environ[iVar + 1] = NULL;
4475 pSandbox->environ[iVar] = pszNew;
4476
4477 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4478 pSandbox->papwszEnvVars[iVar] = pwszNew;
4479 pSandbox->wenviron[iVar + 1] = NULL;
4480 pSandbox->wenviron[iVar] = pwszNew;
4481 return 0;
4482 }
4483
4484 kHlpFree(pwszNew);
4485 }
4486 kHlpFree(pszNew);
4487 }
4488 KW_LOG(("Out of memory!\n"));
4489 return 0;
4490}
4491
4492
4493/**
4494 * Sets an environment variable, UTF-16 style.
4495 *
4496 * @returns 0 on success, non-zero on failure.
4497 * @param pSandbox The sandbox.
4498 * @param pwcVar The variable name.
4499 * @param cwcVar The length of the name.
4500 * @param pwszValue The value.
4501 */
4502static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4503{
4504 /* Allocate and construct the new strings. */
4505 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4506 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4507 if (pwszNew)
4508 {
4509 char *pszNew;
4510 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4511 pwszNew[cwcVar] = '=';
4512 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4513 cwcTmp += cwcVar + 1;
4514 pwszNew[cwcVar] = '\0';
4515
4516 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4517 if (pszNew)
4518 {
4519 /* Look it up. */
4520 KSIZE iVar = 0;
4521 wchar_t *pwszEnv;
4522 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4523 {
4524 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4525 && pwszEnv[cwcVar] == '=')
4526 {
4527 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4528 " iVar=%d: %p='%s' and %p='%ls'\n",
4529 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4530 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4531 iVar, pszNew, pszNew, pwszNew, pwszNew));
4532
4533 kHlpFree(pSandbox->papszEnvVars[iVar]);
4534 pSandbox->papszEnvVars[iVar] = pszNew;
4535 pSandbox->environ[iVar] = pszNew;
4536
4537 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4538 pSandbox->papwszEnvVars[iVar] = pwszNew;
4539 pSandbox->wenviron[iVar] = pwszNew;
4540 return 0;
4541 }
4542 iVar++;
4543 }
4544
4545 /* Not found, do we need to grow the table first? */
4546 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4547 kwSandboxGrowEnv(pSandbox, iVar + 2);
4548 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4549 {
4550 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4551
4552 pSandbox->papszEnvVars[iVar + 1] = NULL;
4553 pSandbox->papszEnvVars[iVar] = pszNew;
4554 pSandbox->environ[iVar + 1] = NULL;
4555 pSandbox->environ[iVar] = pszNew;
4556
4557 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4558 pSandbox->papwszEnvVars[iVar] = pwszNew;
4559 pSandbox->wenviron[iVar + 1] = NULL;
4560 pSandbox->wenviron[iVar] = pwszNew;
4561 return 0;
4562 }
4563
4564 kHlpFree(pwszNew);
4565 }
4566 kHlpFree(pszNew);
4567 }
4568 KW_LOG(("Out of memory!\n"));
4569 return 0;
4570}
4571
4572
4573/** ANSI unsetenv worker. */
4574static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4575{
4576 KSIZE iVar = 0;
4577 char *pszEnv;
4578 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4579 {
4580 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4581 && pszEnv[cchVar] == '=')
4582 {
4583 KSIZE cVars = iVar;
4584 while (pSandbox->papszEnvVars[cVars])
4585 cVars++;
4586 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4587 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4588
4589 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4590 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4591 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4592
4593 kHlpFree(pSandbox->papszEnvVars[iVar]);
4594 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4595 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4596 pSandbox->papszEnvVars[cVars] = NULL;
4597 pSandbox->environ[cVars] = NULL;
4598
4599 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4600 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4601 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4602 pSandbox->papwszEnvVars[cVars] = NULL;
4603 pSandbox->wenviron[cVars] = NULL;
4604 return 0;
4605 }
4606 iVar++;
4607 }
4608 return KERR_ENVVAR_NOT_FOUND;
4609}
4610
4611
4612/** UTF-16 unsetenv worker. */
4613static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4614{
4615 KSIZE iVar = 0;
4616 wchar_t *pwszEnv;
4617 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4618 {
4619 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4620 && pwszEnv[cwcVar] == '=')
4621 {
4622 KSIZE cVars = iVar;
4623 while (pSandbox->papwszEnvVars[cVars])
4624 cVars++;
4625 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4626 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4627
4628 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4629 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4630 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4631
4632 kHlpFree(pSandbox->papszEnvVars[iVar]);
4633 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4634 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4635 pSandbox->papszEnvVars[cVars] = NULL;
4636 pSandbox->environ[cVars] = NULL;
4637
4638 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4639 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4640 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4641 pSandbox->papwszEnvVars[cVars] = NULL;
4642 pSandbox->wenviron[cVars] = NULL;
4643 return 0;
4644 }
4645 iVar++;
4646 }
4647 return KERR_ENVVAR_NOT_FOUND;
4648}
4649
4650
4651
4652/** ANSI getenv worker. */
4653static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4654{
4655 KSIZE iVar = 0;
4656 char *pszEnv;
4657 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4658 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4659 && pszEnv[cchVar] == '=')
4660 return &pszEnv[cchVar + 1];
4661 return NULL;
4662}
4663
4664
4665/** UTF-16 getenv worker. */
4666static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4667{
4668 KSIZE iVar = 0;
4669 wchar_t *pwszEnv;
4670 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4671 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4672 && pwszEnv[cwcVar] == '=')
4673 return &pwszEnv[cwcVar + 1];
4674 return NULL;
4675}
4676
4677
4678/** Kernel32 - GetEnvironmentVariableA() */
4679static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4680{
4681 char *pszFoundValue;
4682 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4683
4684 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4685 if (pszFoundValue)
4686 {
4687 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4688 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4689 return cchRet;
4690 }
4691 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4692 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4693 return 0;
4694}
4695
4696
4697/** Kernel32 - GetEnvironmentVariableW() */
4698static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4699{
4700 wchar_t *pwszFoundValue;
4701 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4702
4703 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4704 if (pwszFoundValue)
4705 {
4706 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4707 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4708 return cchRet;
4709 }
4710 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4711 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4712 return 0;
4713}
4714
4715
4716/** Kernel32 - SetEnvironmentVariableA() */
4717static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4718{
4719 int rc;
4720 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4721
4722 if (pszValue)
4723 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4724 else
4725 {
4726 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4727 rc = 0; //??
4728 }
4729 if (rc == 0)
4730 {
4731 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4732 return TRUE;
4733 }
4734 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4735 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4736 return FALSE;
4737}
4738
4739
4740/** Kernel32 - SetEnvironmentVariableW() */
4741static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4742{
4743 int rc;
4744 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4745
4746 if (pwszValue)
4747 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4748 else
4749 {
4750 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4751 rc = 0; //??
4752 }
4753 if (rc == 0)
4754 {
4755 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4756 return TRUE;
4757 }
4758 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4759 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4760 return FALSE;
4761}
4762
4763
4764/** Kernel32 - ExpandEnvironmentStringsA() */
4765static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4766{
4767 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4768 KWFS_TODO();
4769 return 0;
4770}
4771
4772
4773/** Kernel32 - ExpandEnvironmentStringsW() */
4774static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4775{
4776 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4777 KWFS_TODO();
4778 return 0;
4779}
4780
4781
4782/** CRT - _putenv(). */
4783static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4784{
4785 int rc;
4786 char const *pszEqual;
4787 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4788
4789 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4790 if (pszEqual)
4791 {
4792 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4793 if (rc == 0)
4794 { }
4795 else
4796 rc = -1;
4797 }
4798 else
4799 {
4800 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4801 rc = 0;
4802 }
4803 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4804 return rc;
4805}
4806
4807
4808/** CRT - _wputenv(). */
4809static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4810{
4811 int rc;
4812 wchar_t const *pwszEqual;
4813 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4814
4815 pwszEqual = wcschr(pwszVarEqualValue, '=');
4816 if (pwszEqual)
4817 {
4818 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4819 if (rc == 0)
4820 { }
4821 else
4822 rc = -1;
4823 }
4824 else
4825 {
4826 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4827 rc = 0;
4828 }
4829 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4830 return rc;
4831}
4832
4833
4834/** CRT - _putenv_s(). */
4835static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4836{
4837 char const *pszEqual;
4838 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4839
4840 pszEqual = kHlpStrChr(pszVar, '=');
4841 if (pszEqual == NULL)
4842 {
4843 if (pszValue)
4844 {
4845 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4846 if (rc == 0)
4847 {
4848 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4849 return 0;
4850 }
4851 }
4852 else
4853 {
4854 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4855 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4856 return 0;
4857 }
4858 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4859 return ENOMEM;
4860 }
4861 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4862 return EINVAL;
4863}
4864
4865
4866/** CRT - _wputenv_s(). */
4867static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4868{
4869 wchar_t const *pwszEqual;
4870 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4871
4872 pwszEqual = wcschr(pwszVar, '=');
4873 if (pwszEqual == NULL)
4874 {
4875 if (pwszValue)
4876 {
4877 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4878 if (rc == 0)
4879 {
4880 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4881 return 0;
4882 }
4883 }
4884 else
4885 {
4886 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4887 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4888 return 0;
4889 }
4890 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4891 return ENOMEM;
4892 }
4893 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4894 return EINVAL;
4895}
4896
4897
4898/** CRT - get pointer to the __initenv variable (initial environment). */
4899static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4900{
4901 KW_LOG(("__p___initenv\n"));
4902 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4903 KWFS_TODO();
4904 return &g_Sandbox.initenv;
4905}
4906
4907
4908/** CRT - get pointer to the __winitenv variable (initial environment). */
4909static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4910{
4911 KW_LOG(("__p___winitenv\n"));
4912 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4913 KWFS_TODO();
4914 return &g_Sandbox.winitenv;
4915}
4916
4917
4918/** CRT - get pointer to the _environ variable (current environment). */
4919static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4920{
4921 KW_LOG(("__p__environ\n"));
4922 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4923 return &g_Sandbox.environ;
4924}
4925
4926
4927/** CRT - get pointer to the _wenviron variable (current environment). */
4928static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4929{
4930 KW_LOG(("__p__wenviron\n"));
4931 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4932 return &g_Sandbox.wenviron;
4933}
4934
4935
4936/** CRT - get the _environ variable (current environment).
4937 * @remarks Not documented or prototyped? */
4938static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4939{
4940 KWFS_TODO(); /** @todo check the callers expectations! */
4941 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4942 *ppapszEnviron = g_Sandbox.environ;
4943 return 0;
4944}
4945
4946
4947/** CRT - get the _wenviron variable (current environment).
4948 * @remarks Not documented or prototyped? */
4949static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4950{
4951 KWFS_TODO(); /** @todo check the callers expectations! */
4952 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4953 *ppapwszEnviron = g_Sandbox.wenviron;
4954 return 0;
4955}
4956
4957
4958/** CRT - _wdupenv_s() (see _tdupenv_s(). */
4959static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
4960 PKWCRTSLOT pSlot)
4961{
4962 errno_t rc;
4963 wchar_t *pwszValue;
4964 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4965
4966 if (ppwszValue)
4967 {
4968 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
4969 if (pwszValue)
4970 {
4971 size_t cwcValue = wcslen(pwszValue);
4972 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
4973 if (pwszDst)
4974 {
4975 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
4976 pwszDst[cwcValue] = '\0';
4977 *ppwszValue = pwszDst;
4978 if (pcwcValue)
4979 *pcwcValue = cwcValue;
4980 rc = 0;
4981 }
4982 else
4983 {
4984 *ppwszValue = NULL;
4985 if (pcwcValue)
4986 *pcwcValue = 0;
4987 rc = ENOMEM;
4988 }
4989 }
4990 else
4991 {
4992 *ppwszValue = NULL;
4993 if (pcwcValue)
4994 *pcwcValue = 0;
4995 rc = 0;
4996 }
4997 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
4998 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
4999 }
5000 else
5001 {
5002 /*
5003 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5004 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5005 * was set to a value.
5006 */
5007 if (pcwcValue)
5008 *pcwcValue = 0;
5009 rc = EINVAL;
5010 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5011 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5012 }
5013 return rc;
5014}
5015CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5016 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5017 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5018
5019
5020
5021/*
5022 *
5023 * Loader related APIs
5024 * Loader related APIs
5025 * Loader related APIs
5026 *
5027 */
5028
5029/**
5030 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5031 */
5032static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5033{
5034 /* Load it first. */
5035 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5036 if (hmod)
5037 {
5038 pDynLoad->hmod = hmod;
5039 pDynLoad->pMod = NULL; /* indicates special */
5040
5041 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5042 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5043 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5044 }
5045 else
5046 kHlpFree(pDynLoad);
5047 return hmod;
5048}
5049
5050
5051/**
5052 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5053 */
5054static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5055{
5056 HMODULE hmod;
5057 PKWMODULE pMod;
5058 KU32 uHashPath;
5059 KSIZE idxHash;
5060 char szNormPath[256];
5061 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5062
5063 /*
5064 * Lower case it.
5065 */
5066 if (cbFilename <= sizeof(szNormPath))
5067 {
5068 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5069 _strlwr(szNormPath);
5070 }
5071 else
5072 {
5073 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5074 return NULL;
5075 }
5076
5077 /*
5078 * Check if it has already been loaded so we don't create an unnecessary
5079 * loader module for it.
5080 */
5081 uHashPath = kwStrHash(szNormPath);
5082 idxHash = uHashPath % K_ELEMENTS(g_apModules);
5083 pMod = g_apModules[idxHash];
5084 if (pMod)
5085 {
5086 do
5087 {
5088 if ( pMod->uHashPath == uHashPath
5089 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
5090 {
5091 pDynLoad->pMod = kwLdrModuleRetain(pMod);
5092 pDynLoad->hmod = pMod->hOurMod;
5093
5094 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5095 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5096 KW_LOG(("LoadLibraryExA(%s,,) -> %p [already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
5097 return pDynLoad->hmod;
5098 }
5099 pMod = pMod->pNextHash;
5100 } while (pMod);
5101 }
5102
5103
5104 /*
5105 * Try load it and make a kLdr module for it.
5106 */
5107 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
5108 if (hmod)
5109 {
5110 PKLDRMOD pLdrMod;
5111 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
5112 if (rc == 0)
5113 {
5114 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
5115 K_FALSE /*fDoReplacements*/);
5116 if (pMod)
5117 {
5118 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5119
5120 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cbFilename + cbFilename * sizeof(wchar_t));
5121 if (pDynLoad)
5122 {
5123 pDynLoad->pMod = pMod;
5124 pDynLoad->hmod = hmod;
5125
5126 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5127 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5128 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5129 return hmod;
5130 }
5131
5132 KWFS_TODO();
5133 }
5134 else
5135 KWFS_TODO();
5136 }
5137 else
5138 KWFS_TODO();
5139 }
5140 kHlpFree(pDynLoad);
5141 return hmod;
5142}
5143
5144
5145/** Kernel32 - LoadLibraryExA() */
5146static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5147{
5148 KSIZE cchFilename = kHlpStrLen(pszFilename);
5149 const char *pszSearchPath;
5150 PKWDYNLOAD pDynLoad;
5151 PKWMODULE pMod;
5152 int rc;
5153 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5154
5155 /*
5156 * Deal with a couple of extremely unlikely special cases right away.
5157 */
5158 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5159 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5160 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5161 { /* likely */ }
5162 else
5163 {
5164 KWFS_TODO();
5165 return LoadLibraryExA(pszFilename, hFile, fFlags);
5166 }
5167
5168 /*
5169 * Check if we've already got a dynload entry for this one.
5170 */
5171 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5172 if ( pDynLoad->cchRequest == cchFilename
5173 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5174 {
5175 if (pDynLoad->pMod)
5176 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5177 else
5178 rc = 0;
5179 if (rc == 0)
5180 {
5181 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5182 return pDynLoad->hmod;
5183 }
5184 SetLastError(ERROR_DLL_INIT_FAILED);
5185 return NULL;
5186 }
5187
5188 /*
5189 * Allocate a dynload entry for the request.
5190 */
5191 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5192 if (pDynLoad)
5193 {
5194 pDynLoad->cchRequest = cchFilename;
5195 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5196 }
5197 else
5198 {
5199 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
5200 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5201 return NULL;
5202 }
5203
5204 /*
5205 * Deal with resource / data DLLs.
5206 */
5207 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5208 | LOAD_LIBRARY_AS_DATAFILE
5209 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5210 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5211 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5212
5213 /*
5214 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5215 */
5216 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
5217 && kHlpIsFilenameOnly(pszFilename))
5218 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5219
5220 /*
5221 * Normal library loading.
5222 * We start by being very lazy and reusing the code for resolving imports.
5223 */
5224 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5225 if (!kHlpIsFilenameOnly(pszFilename))
5226 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5227 else
5228 {
5229 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5230 if (rc != 0)
5231 pMod = NULL;
5232 }
5233 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5234 {
5235 /* Enter it into the tool module table and dynamic link request cache. */
5236 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5237
5238 pDynLoad->pMod = pMod;
5239 pDynLoad->hmod = pMod->hOurMod;
5240
5241 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5242 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5243
5244 /*
5245 * Make sure it's initialized (need to link it first since DllMain may
5246 * use loader APIs).
5247 */
5248 rc = kwLdrModuleInitTree(pMod);
5249 if (rc == 0)
5250 {
5251 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5252 return pDynLoad->hmod;
5253 }
5254
5255 SetLastError(ERROR_DLL_INIT_FAILED);
5256 }
5257 else
5258 {
5259 KWFS_TODO();
5260 kHlpFree(pDynLoad);
5261 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5262 }
5263 return NULL;
5264}
5265
5266
5267/** Kernel32 - LoadLibraryExA() for native overloads */
5268static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5269{
5270 char szPath[1024];
5271 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5272
5273 /*
5274 * We may have to help resolved unqualified DLLs living in the executable directory.
5275 */
5276 if (kHlpIsFilenameOnly(pszFilename))
5277 {
5278 KSIZE cchFilename = kHlpStrLen(pszFilename);
5279 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5280 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5281 {
5282 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5283 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5284 if (kwFsPathExists(szPath))
5285 {
5286 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5287 pszFilename = szPath;
5288 }
5289 }
5290
5291 if (pszFilename != szPath)
5292 {
5293 KSIZE cchSuffix = 0;
5294 KBOOL fNeedSuffix = K_FALSE;
5295 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5296 while (*pszCur != '\0')
5297 {
5298 /* Find the end of the component */
5299 KSIZE cch = 0;
5300 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5301 cch++;
5302
5303 if ( cch > 0 /* wrong, but whatever */
5304 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5305 {
5306 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5307 if ( szPath[cch - 1] != ':'
5308 && szPath[cch - 1] != '/'
5309 && szPath[cch - 1] != '\\')
5310 *pszDst++ = '\\';
5311 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5312 if (fNeedSuffix)
5313 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5314 *pszDst = '\0';
5315
5316 if (kwFsPathExists(szPath))
5317 {
5318 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5319 pszFilename = szPath;
5320 break;
5321 }
5322 }
5323
5324 /* Advance */
5325 pszCur += cch;
5326 while (*pszCur == ';')
5327 pszCur++;
5328 }
5329 }
5330 }
5331
5332 return LoadLibraryExA(pszFilename, hFile, fFlags);
5333}
5334
5335
5336/** Kernel32 - LoadLibraryExW() */
5337static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5338{
5339 char szTmp[4096];
5340 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5341 if (cchTmp < sizeof(szTmp))
5342 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5343
5344 KWFS_TODO();
5345 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5346 return NULL;
5347}
5348
5349/** Kernel32 - LoadLibraryA() */
5350static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5351{
5352 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5353}
5354
5355
5356/** Kernel32 - LoadLibraryW() */
5357static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5358{
5359 char szTmp[4096];
5360 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5361 if (cchTmp < sizeof(szTmp))
5362 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5363 KWFS_TODO();
5364 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5365 return NULL;
5366}
5367
5368
5369/** Kernel32 - FreeLibrary() */
5370static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5371{
5372 /* Ignored, we like to keep everything loaded. */
5373 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5374 return TRUE;
5375}
5376
5377
5378/** Kernel32 - GetModuleHandleA() */
5379static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5380{
5381 KSIZE i;
5382 KSIZE cchModule;
5383 PKWDYNLOAD pDynLoad;
5384 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5385
5386 /*
5387 * The executable.
5388 */
5389 if (pszModule == NULL)
5390 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5391
5392 /*
5393 * Cache of system modules we've seen queried.
5394 */
5395 cchModule = kHlpStrLen(pszModule);
5396 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5397 if ( g_aGetModuleHandleCache[i].cchName == cchModule
5398 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5399 {
5400 if (g_aGetModuleHandleCache[i].hmod != NULL)
5401 return g_aGetModuleHandleCache[i].hmod;
5402 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleA(pszModule);
5403 }
5404
5405 /*
5406 * Modules we've dynamically loaded.
5407 */
5408 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5409 if ( pDynLoad->pMod
5410 && ( stricmp(pDynLoad->pMod->pszPath, pszModule) == 0
5411 || stricmp(&pDynLoad->pMod->pszPath[pDynLoad->pMod->offFilename], pszModule) == 0) )
5412 {
5413 if ( pDynLoad->pMod->fNative
5414 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5415 {
5416 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5417 return pDynLoad->hmod;
5418 }
5419 SetLastError(ERROR_MOD_NOT_FOUND);
5420 return NULL;
5421 }
5422
5423 kwErrPrintf("pszModule=%s\n", pszModule);
5424 KWFS_TODO();
5425 SetLastError(ERROR_MOD_NOT_FOUND);
5426 return NULL;
5427}
5428
5429
5430/** Kernel32 - GetModuleHandleW() */
5431static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5432{
5433 KSIZE i;
5434 KSIZE cwcModule;
5435 PKWDYNLOAD pDynLoad;
5436 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5437
5438 /*
5439 * The executable.
5440 */
5441 if (pwszModule == NULL)
5442 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5443
5444 /*
5445 * Cache of system modules we've seen queried.
5446 */
5447 cwcModule = kwUtf16Len(pwszModule);
5448 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5449 if ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5450 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5451 {
5452 if (g_aGetModuleHandleCache[i].hmod != NULL)
5453 return g_aGetModuleHandleCache[i].hmod;
5454 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleW(pwszModule);
5455 }
5456
5457 /*
5458 * Modules we've dynamically loaded.
5459 */
5460 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5461 if ( pDynLoad->pMod
5462 && ( _wcsicmp(pDynLoad->pMod->pwszPath, pwszModule) == 0
5463 || _wcsicmp(&pDynLoad->pMod->pwszPath[pDynLoad->pMod->offFilename], pwszModule) == 0) ) /** @todo wrong offset */
5464 {
5465 if ( pDynLoad->pMod->fNative
5466 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5467 {
5468 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5469 return pDynLoad->hmod;
5470 }
5471 SetLastError(ERROR_MOD_NOT_FOUND);
5472 return NULL;
5473 }
5474
5475 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5476 KWFS_TODO();
5477 SetLastError(ERROR_MOD_NOT_FOUND);
5478 return NULL;
5479}
5480
5481
5482/** Used to debug dynamically resolved procedures. */
5483static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5484{
5485#ifdef _MSC_VER
5486 __debugbreak();
5487#else
5488 KWFS_TODO();
5489#endif
5490 return -1;
5491}
5492
5493
5494#ifndef NDEBUG
5495/*
5496 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5497 */
5498# if K_ARCH == K_ARCH_X86_32
5499static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5500# else
5501static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5502# endif
5503typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5504typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5505typedef struct KWCXINTERCEPTORENTRY
5506{
5507 PFNINVOKECOMPILERPASSW pfnOrg;
5508 PKWMODULE pModule;
5509 PFNINVOKECOMPILERPASSW pfnWrap;
5510} KWCXINTERCEPTORENTRY;
5511
5512static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5513 KWCXINTERCEPTORENTRY *pEntry)
5514{
5515 int i;
5516 KIPTR rcExit;
5517 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5518 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5519 for (i = 0; i < cArgs; i++)
5520 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5521
5522 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5523
5524 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5525 return rcExit;
5526}
5527
5528static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5529static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5530static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5531
5532static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5533{
5534 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5535 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5536 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5537};
5538
5539static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5540{
5541 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5542}
5543
5544static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5545{
5546 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5547}
5548
5549static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5550{
5551 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5552}
5553
5554#endif /* !NDEBUG */
5555
5556
5557/** Kernel32 - GetProcAddress() */
5558static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5559{
5560 KSIZE i;
5561 PKWMODULE pMod;
5562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5563
5564 /*
5565 * Try locate the module.
5566 */
5567 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5568 if (pMod)
5569 {
5570 KLDRADDR uValue;
5571 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5572 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5573 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5574 KU32_MAX /*iSymbol*/,
5575 pszProc,
5576 kHlpStrLen(pszProc),
5577 NULL /*pszVersion*/,
5578 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5579 &uValue,
5580 NULL /*pfKind*/);
5581 if (rc == 0)
5582 {
5583 //static int s_cDbgGets = 0;
5584 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5585 KU32 i = g_cSandboxGetProcReplacements;
5586 while (i-- > 0)
5587 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5588 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5589 {
5590 if ( !g_aSandboxGetProcReplacements[i].pszModule
5591 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5592 {
5593 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5594 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5595 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5596 {
5597 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5598 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5599 }
5600 kwLdrModuleRelease(pMod);
5601 return (FARPROC)(KUPTR)uValue;
5602 }
5603 }
5604
5605#ifndef NDEBUG
5606 /* Intercept the compiler pass method, dumping arguments. */
5607 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5608 {
5609 KU32 i;
5610 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5611 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5612 {
5613 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5614 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5615 break;
5616 }
5617 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5618 while (i-- > 0)
5619 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5620 {
5621 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5622 g_aCxInterceptorEntries[i].pModule = pMod;
5623 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5624 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5625 break;
5626 }
5627 }
5628#endif
5629 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5630 kwLdrModuleRelease(pMod);
5631 //s_cDbgGets++;
5632 //if (s_cGets >= 3)
5633 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5634 return (FARPROC)(KUPTR)uValue;
5635 }
5636
5637 KWFS_TODO();
5638 SetLastError(ERROR_PROC_NOT_FOUND);
5639 kwLdrModuleRelease(pMod);
5640 return NULL;
5641 }
5642
5643 /*
5644 * Hmm... could be a cached module-by-name.
5645 */
5646 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5647 if (g_aGetModuleHandleCache[i].hmod == hmod)
5648 return GetProcAddress(hmod, pszProc);
5649
5650 KWFS_TODO();
5651 return GetProcAddress(hmod, pszProc);
5652}
5653
5654
5655/** Kernel32 - GetModuleFileNameA() */
5656static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5657{
5658 PKWMODULE pMod;
5659 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5660
5661 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5662 if (pMod != NULL)
5663 {
5664 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5665 kwLdrModuleRelease(pMod);
5666 return cbRet;
5667 }
5668 KWFS_TODO();
5669 return 0;
5670}
5671
5672
5673/** Kernel32 - GetModuleFileNameW() */
5674static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5675{
5676 PKWMODULE pMod;
5677 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5678
5679 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5680 if (pMod)
5681 {
5682 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5683 kwLdrModuleRelease(pMod);
5684 return cwcRet;
5685 }
5686
5687 KWFS_TODO();
5688 return 0;
5689}
5690
5691
5692/** NtDll - RtlPcToFileHeader
5693 * This is necessary for msvcr100.dll!CxxThrowException. */
5694static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5695{
5696 PVOID pvRet;
5697
5698 /*
5699 * Do a binary lookup of the module table for the current tool.
5700 * This will give us a
5701 */
5702 if (g_Sandbox.fRunning)
5703 {
5704 KUPTR const uPC = (KUPTR)pvPC;
5705 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5706 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5707 KU32 i;
5708 if (iEnd)
5709 {
5710 KU32 iStart = 0;
5711 i = iEnd / 2;
5712 for (;;)
5713 {
5714 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5715 if (uPC < uHModThis)
5716 {
5717 iEnd = i;
5718 if (iStart < i)
5719 { }
5720 else
5721 break;
5722 }
5723 else if (uPC != uHModThis)
5724 {
5725 iStart = ++i;
5726 if (i < iEnd)
5727 { }
5728 else
5729 break;
5730 }
5731 else
5732 {
5733 /* This isn't supposed to happen. */
5734 break;
5735 }
5736
5737 i = iStart + (iEnd - iStart) / 2;
5738 }
5739
5740 /* For reasons of simplicity (= copy & paste), we end up with the
5741 module after the one we're interested in here. */
5742 i--;
5743 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5744 && papMods[i]->pLdrMod)
5745 {
5746 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5747 if (uRvaPC < papMods[i]->cbImage)
5748 {
5749 *ppvImageBase = papMods[i]->hOurMod;
5750 pvRet = papMods[i]->hOurMod;
5751 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5752 return pvRet;
5753 }
5754 }
5755 }
5756 else
5757 i = 0;
5758 }
5759
5760 /*
5761 * Call the regular API.
5762 */
5763 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5764 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5765 return pvRet;
5766}
5767
5768
5769/*
5770 *
5771 * File access APIs (for speeding them up).
5772 * File access APIs (for speeding them up).
5773 * File access APIs (for speeding them up).
5774 *
5775 */
5776
5777
5778/**
5779 * Converts a lookup error to a windows error code.
5780 *
5781 * @returns The windows error code.
5782 * @param enmError The lookup error.
5783 */
5784static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5785{
5786 switch (enmError)
5787 {
5788 case KFSLOOKUPERROR_NOT_FOUND:
5789 case KFSLOOKUPERROR_NOT_DIR:
5790 return ERROR_FILE_NOT_FOUND;
5791
5792 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5793 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5794 return ERROR_PATH_NOT_FOUND;
5795
5796 case KFSLOOKUPERROR_PATH_TOO_LONG:
5797 return ERROR_FILENAME_EXCED_RANGE;
5798
5799 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5800 return ERROR_NOT_ENOUGH_MEMORY;
5801
5802 default:
5803 return ERROR_PATH_NOT_FOUND;
5804 }
5805}
5806
5807#ifdef WITH_TEMP_MEMORY_FILES
5808
5809/**
5810 * Checks for a cl.exe temporary file.
5811 *
5812 * There are quite a bunch of these. They seems to be passing data between the
5813 * first and second compiler pass. Since they're on disk, they get subjected to
5814 * AV software screening and normal file consistency rules. So, not necessarily
5815 * a very efficient way of handling reasonably small amounts of data.
5816 *
5817 * We make the files live in virtual memory by intercepting their opening,
5818 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5819 *
5820 * @returns K_TRUE / K_FALSE
5821 * @param pwszFilename The file name being accessed.
5822 */
5823static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5824{
5825 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5826 if (pwszName)
5827 {
5828 /* The name starts with _CL_... */
5829 if ( pwszName[0] == '_'
5830 && pwszName[1] == 'C'
5831 && pwszName[2] == 'L'
5832 && pwszName[3] == '_' )
5833 {
5834 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5835 this check by just checking that it's alpha numerical ascii from here on. */
5836 wchar_t wc;
5837 pwszName += 4;
5838 while ((wc = *pwszName++) != '\0')
5839 {
5840 if (wc < 127 && iswalnum(wc))
5841 { /* likely */ }
5842 else
5843 return K_FALSE;
5844 }
5845 return K_TRUE;
5846 }
5847 }
5848 return K_FALSE;
5849}
5850
5851
5852/**
5853 * Creates a handle to a temporary file.
5854 *
5855 * @returns The handle on success.
5856 * INVALID_HANDLE_VALUE and SetLastError on failure.
5857 * @param pTempFile The temporary file.
5858 * @param dwDesiredAccess The desired access to the handle.
5859 * @param fMapping Whether this is a mapping (K_TRUE) or file
5860 * (K_FALSE) handle type.
5861 */
5862static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5863{
5864 /*
5865 * Create a handle to the temporary file.
5866 */
5867 HANDLE hFile = INVALID_HANDLE_VALUE;
5868 HANDLE hProcSelf = GetCurrentProcess();
5869 if (DuplicateHandle(hProcSelf, hProcSelf,
5870 hProcSelf, &hFile,
5871 SYNCHRONIZE, FALSE,
5872 0 /*dwOptions*/))
5873 {
5874 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5875 if (pHandle)
5876 {
5877 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
5878 pHandle->cRefs = 1;
5879 pHandle->offFile = 0;
5880 pHandle->hHandle = hFile;
5881 pHandle->dwDesiredAccess = dwDesiredAccess;
5882 pHandle->u.pTempFile = pTempFile;
5883 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
5884 {
5885 pTempFile->cActiveHandles++;
5886 kHlpAssert(pTempFile->cActiveHandles >= 1);
5887 kHlpAssert(pTempFile->cActiveHandles <= 2);
5888 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
5889 return hFile;
5890 }
5891
5892 kHlpFree(pHandle);
5893 }
5894 else
5895 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
5896 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5897 }
5898 else
5899 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
5900 return INVALID_HANDLE_VALUE;
5901}
5902
5903
5904static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
5905{
5906 HANDLE hFile;
5907 DWORD dwErr;
5908
5909 /*
5910 * Check if we've got an existing temp file.
5911 * ASSUME exact same path for now.
5912 */
5913 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
5914 PKWFSTEMPFILE pTempFile;
5915 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
5916 {
5917 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
5918 if ( pTempFile->cwcPath == cwcFilename
5919 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
5920 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
5921 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
5922 break;
5923 }
5924
5925 /*
5926 * Create a new temporary file instance if not found.
5927 */
5928 if (pTempFile == NULL)
5929 {
5930 KSIZE cbFilename;
5931
5932 switch (dwCreationDisposition)
5933 {
5934 case CREATE_ALWAYS:
5935 case OPEN_ALWAYS:
5936 dwErr = NO_ERROR;
5937 break;
5938
5939 case CREATE_NEW:
5940 kHlpAssertFailed();
5941 SetLastError(ERROR_ALREADY_EXISTS);
5942 return INVALID_HANDLE_VALUE;
5943
5944 case OPEN_EXISTING:
5945 case TRUNCATE_EXISTING:
5946 kHlpAssertFailed();
5947 SetLastError(ERROR_FILE_NOT_FOUND);
5948 return INVALID_HANDLE_VALUE;
5949
5950 default:
5951 kHlpAssertFailed();
5952 SetLastError(ERROR_INVALID_PARAMETER);
5953 return INVALID_HANDLE_VALUE;
5954 }
5955
5956 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
5957 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
5958 if (pTempFile)
5959 {
5960 pTempFile->cwcPath = (KU16)cwcFilename;
5961 pTempFile->cbFile = 0;
5962 pTempFile->cbFileAllocated = 0;
5963 pTempFile->cActiveHandles = 0;
5964 pTempFile->cMappings = 0;
5965 pTempFile->cSegs = 0;
5966 pTempFile->paSegs = NULL;
5967 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
5968
5969 pTempFile->pNext = g_Sandbox.pTempFileHead;
5970 g_Sandbox.pTempFileHead = pTempFile;
5971 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
5972 }
5973 else
5974 {
5975 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
5976 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5977 return INVALID_HANDLE_VALUE;
5978 }
5979 }
5980 else
5981 {
5982 switch (dwCreationDisposition)
5983 {
5984 case OPEN_EXISTING:
5985 dwErr = NO_ERROR;
5986 break;
5987 case OPEN_ALWAYS:
5988 dwErr = ERROR_ALREADY_EXISTS ;
5989 break;
5990
5991 case TRUNCATE_EXISTING:
5992 case CREATE_ALWAYS:
5993 kHlpAssertFailed();
5994 pTempFile->cbFile = 0;
5995 dwErr = ERROR_ALREADY_EXISTS;
5996 break;
5997
5998 case CREATE_NEW:
5999 kHlpAssertFailed();
6000 SetLastError(ERROR_FILE_EXISTS);
6001 return INVALID_HANDLE_VALUE;
6002
6003 default:
6004 kHlpAssertFailed();
6005 SetLastError(ERROR_INVALID_PARAMETER);
6006 return INVALID_HANDLE_VALUE;
6007 }
6008 }
6009
6010 /*
6011 * Create a handle to the temporary file.
6012 */
6013 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6014 if (hFile != INVALID_HANDLE_VALUE)
6015 SetLastError(dwErr);
6016 return hFile;
6017}
6018
6019#endif /* WITH_TEMP_MEMORY_FILES */
6020
6021/**
6022 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6023 *
6024 * @returns K_TRUE if cacheable, K_FALSE if not.
6025 * @param wcFirst The first extension character.
6026 * @param wcSecond The second extension character.
6027 * @param wcThird The third extension character.
6028 * @param fAttrQuery Set if it's for an attribute query, clear if for
6029 * file creation.
6030 */
6031static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6032{
6033 /* C++ header without an extension or a directory. */
6034 if (wcFirst == '\0')
6035 {
6036 /** @todo exclude temporary files... */
6037 return K_TRUE;
6038 }
6039
6040 /* C Header: .h */
6041 if (wcFirst == 'h' || wcFirst == 'H')
6042 {
6043 if (wcSecond == '\0')
6044 return K_TRUE;
6045
6046 /* C++ Header: .hpp, .hxx */
6047 if ( (wcSecond == 'p' || wcSecond == 'P')
6048 && (wcThird == 'p' || wcThird == 'P'))
6049 return K_TRUE;
6050 if ( (wcSecond == 'x' || wcSecond == 'X')
6051 && (wcThird == 'x' || wcThird == 'X'))
6052 return K_TRUE;
6053 }
6054 /* Misc starting with i. */
6055 else if (wcFirst == 'i' || wcFirst == 'I')
6056 {
6057 if (wcSecond != '\0')
6058 {
6059 if (wcSecond == 'n' || wcSecond == 'N')
6060 {
6061 /* C++ inline header: .inl */
6062 if (wcThird == 'l' || wcThird == 'L')
6063 return K_TRUE;
6064
6065 /* Assembly include file: .inc */
6066 if (wcThird == 'c' || wcThird == 'C')
6067 return K_TRUE;
6068 }
6069 }
6070 }
6071 /* Assembly header: .mac */
6072 else if (wcFirst == 'm' || wcFirst == 'M')
6073 {
6074 if (wcSecond == 'a' || wcSecond == 'A')
6075 {
6076 if (wcThird == 'c' || wcThird == 'C')
6077 return K_TRUE;
6078 }
6079 }
6080#ifdef WITH_PCH_CACHING
6081 /* Precompiled header: .pch */
6082 else if (wcFirst == 'p' || wcFirst == 'P')
6083 {
6084 if (wcSecond == 'c' || wcSecond == 'C')
6085 {
6086 if (wcThird == 'h' || wcThird == 'H')
6087 return !g_Sandbox.fNoPchCaching;
6088 }
6089 }
6090#endif
6091#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6092 /* Linker - Object file: .obj */
6093 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6094 {
6095 if (wcSecond == 'b' || wcSecond == 'B')
6096 {
6097 if (wcThird == 'j' || wcThird == 'J')
6098 return K_TRUE;
6099 }
6100 }
6101#endif
6102 else if (fAttrQuery)
6103 {
6104 /* Dynamic link library: .dll */
6105 if (wcFirst == 'd' || wcFirst == 'D')
6106 {
6107 if (wcSecond == 'l' || wcSecond == 'L')
6108 {
6109 if (wcThird == 'l' || wcThird == 'L')
6110 return K_TRUE;
6111 }
6112 }
6113 /* Executable file: .exe */
6114 else if (wcFirst == 'e' || wcFirst == 'E')
6115 {
6116 if (wcSecond == 'x' || wcSecond == 'X')
6117 {
6118 if (wcThird == 'e' || wcThird == 'E')
6119 return K_TRUE;
6120 }
6121 }
6122 /* Response file: .rsp */
6123 else if (wcFirst == 'r' || wcFirst == 'R')
6124 {
6125 if (wcSecond == 's' || wcSecond == 'S')
6126 {
6127 if (wcThird == 'p' || wcThird == 'P')
6128 return !g_Sandbox.fNoPchCaching;
6129 }
6130 }
6131 /* Linker: */
6132 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6133 {
6134 /* Object file: .obj */
6135 if (wcFirst == 'o' || wcFirst == 'O')
6136 {
6137 if (wcSecond == 'b' || wcSecond == 'B')
6138 {
6139 if (wcThird == 'j' || wcThird == 'J')
6140 return K_TRUE;
6141 }
6142 }
6143 /* Library file: .lib */
6144 else if (wcFirst == 'l' || wcFirst == 'L')
6145 {
6146 if (wcSecond == 'i' || wcSecond == 'I')
6147 {
6148 if (wcThird == 'b' || wcThird == 'B')
6149 return K_TRUE;
6150 }
6151 }
6152 /* Linker definition file: .def */
6153 else if (wcFirst == 'd' || wcFirst == 'D')
6154 {
6155 if (wcSecond == 'e' || wcSecond == 'E')
6156 {
6157 if (wcThird == 'f' || wcThird == 'F')
6158 return K_TRUE;
6159 }
6160 }
6161 }
6162 }
6163
6164 return K_FALSE;
6165}
6166
6167
6168/**
6169 * Checks if the file extension indicates that the file/dir is something we
6170 * ought to cache.
6171 *
6172 * @returns K_TRUE if cachable, K_FALSE if not.
6173 * @param pszExt The kHlpGetExt result.
6174 * @param fAttrQuery Set if it's for an attribute query, clear if for
6175 * file creation.
6176 */
6177static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6178{
6179 wchar_t const wcFirst = *pszExt;
6180 if (wcFirst)
6181 {
6182 wchar_t const wcSecond = pszExt[1];
6183 if (wcSecond)
6184 {
6185 wchar_t const wcThird = pszExt[2];
6186 if (pszExt[3] == '\0')
6187 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6188 return K_FALSE;
6189 }
6190 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6191 }
6192 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6193}
6194
6195
6196/**
6197 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6198 * should be cached.
6199 *
6200 * @returns K_TRUE if cachable, K_FALSE if not.
6201 * @param pwszPath The UTF-16 path to examine.
6202 * @param fAttrQuery Set if it's for an attribute query, clear if for
6203 * file creation.
6204 */
6205static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6206{
6207 KSIZE cwcExt;
6208 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6209 switch (cwcExt)
6210 {
6211 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6212 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6213 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6214 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6215 }
6216 return K_FALSE;
6217}
6218
6219
6220
6221/**
6222 * Creates a new
6223 *
6224 * @returns
6225 * @param pFsObj .
6226 * @param pwszFilename .
6227 */
6228static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6229{
6230 HANDLE hFile;
6231 MY_IO_STATUS_BLOCK Ios;
6232 MY_OBJECT_ATTRIBUTES ObjAttr;
6233 MY_UNICODE_STRING UniStr;
6234 MY_NTSTATUS rcNt;
6235 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6236
6237 /*
6238 * Open the file relative to the parent directory.
6239 */
6240 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6241 kHlpAssert(pFsObj->pParent);
6242 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6243
6244 Ios.Information = -1;
6245 Ios.u.Status = -1;
6246
6247 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6248 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6249 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6250
6251 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6252
6253 rcNt = g_pfnNtCreateFile(&hFile,
6254 GENERIC_READ | SYNCHRONIZE,
6255 &ObjAttr,
6256 &Ios,
6257 NULL, /*cbFileInitialAlloc */
6258 FILE_ATTRIBUTE_NORMAL,
6259 FILE_SHARE_READ,
6260 FILE_OPEN,
6261 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6262 NULL, /*pEaBuffer*/
6263 0); /*cbEaBuffer*/
6264 if (MY_NT_SUCCESS(rcNt))
6265 {
6266 /*
6267 * Read the whole file into memory.
6268 */
6269 LARGE_INTEGER cbFile;
6270 if (GetFileSizeEx(hFile, &cbFile))
6271 {
6272 if ( cbFile.QuadPart >= 0
6273#ifdef WITH_PCH_CACHING
6274 && ( cbFile.QuadPart < 16*1024*1024
6275 || ( cbFile.QuadPart < 96*1024*1024
6276 && pFsObj->cchName > 4
6277 && !g_Sandbox.fNoPchCaching
6278 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6279#endif
6280 )
6281 {
6282 KU32 cbCache = (KU32)cbFile.QuadPart;
6283 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6284 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6285 if (hMapping != NULL)
6286 {
6287 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6288 if (pbCache)
6289 {
6290 /*
6291 * Create the cached file object.
6292 */
6293 PKFSWCACHEDFILE pCachedFile;
6294 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6295 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6296 sizeof(*pCachedFile) + cbPath);
6297 if (pCachedFile)
6298 {
6299 pCachedFile->hCached = hFile;
6300 pCachedFile->hSection = hMapping;
6301 pCachedFile->cbCached = cbCache;
6302 pCachedFile->pbCached = pbCache;
6303 pCachedFile->pFsObj = pFsObj;
6304 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6305 kFsCacheObjRetain(pFsObj);
6306
6307 g_cReadCachedFiles++;
6308 g_cbReadCachedFiles += cbCache;
6309
6310 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6311 return pCachedFile;
6312 }
6313
6314 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6315 }
6316 else
6317 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6318 CloseHandle(hMapping);
6319 }
6320 else
6321 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6322 }
6323 else
6324 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6325 }
6326 else
6327 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6328 g_pfnNtClose(hFile);
6329 }
6330 else
6331 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6332 return NULL;
6333}
6334
6335
6336/**
6337 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6338 */
6339static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6340 KBOOL fIsFileHandle, HANDLE *phFile)
6341{
6342 HANDLE hProcSelf = GetCurrentProcess();
6343 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6344 hProcSelf, phFile,
6345 dwDesiredAccess, fInheritHandle,
6346 0 /*dwOptions*/))
6347 {
6348 /*
6349 * Create handle table entry for the duplicate handle.
6350 */
6351 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6352 if (pHandle)
6353 {
6354 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6355 pHandle->cRefs = 1;
6356 pHandle->offFile = 0;
6357 pHandle->hHandle = *phFile;
6358 pHandle->dwDesiredAccess = dwDesiredAccess;
6359 pHandle->u.pCachedFile = pCachedFile;
6360 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6361 return K_TRUE;
6362
6363 kHlpFree(pHandle);
6364 }
6365 else
6366 KWFS_LOG(("Out of memory for handle!\n"));
6367
6368 CloseHandle(*phFile);
6369 *phFile = INVALID_HANDLE_VALUE;
6370 }
6371 else
6372 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6373 return K_FALSE;
6374}
6375
6376
6377/**
6378 * Kernel32 - Common code for CreateFileW and CreateFileA.
6379 */
6380static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6381{
6382 *phFile = INVALID_HANDLE_VALUE;
6383
6384 /*
6385 * At the moment we only handle existing files.
6386 */
6387 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6388 {
6389 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6390 kHlpAssert(pFsObj->fHaveStats);
6391 if ( pCachedFile != NULL
6392 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6393 {
6394 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6395 return K_TRUE;
6396 }
6397 }
6398 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6399
6400 /* Do fallback, please. */
6401 return K_FALSE;
6402}
6403
6404
6405/** Kernel32 - CreateFileA */
6406static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6407 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6408 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6409{
6410 HANDLE hFile;
6411
6412 /*
6413 * Check for include files and similar that we do read-only caching of.
6414 */
6415 if (dwCreationDisposition == FILE_OPEN_IF)
6416 {
6417 if ( dwDesiredAccess == GENERIC_READ
6418 || dwDesiredAccess == FILE_GENERIC_READ)
6419 {
6420 if (dwShareMode & FILE_SHARE_READ)
6421 {
6422 if ( !pSecAttrs
6423 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6424 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6425 {
6426 const char *pszExt = kHlpGetExt(pszFilename);
6427 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6428 {
6429 KFSLOOKUPERROR enmError;
6430 PKFSOBJ pFsObj;
6431 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6432
6433 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6434 if (pFsObj)
6435 {
6436 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6437 &hFile);
6438 kFsCacheObjRelease(g_pFsCache, pFsObj);
6439 if (fRc)
6440 {
6441 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6442 return hFile;
6443 }
6444 }
6445 /* These are for nasm and yasm header searching. Cache will already
6446 have checked the directories for the file, no need to call
6447 CreateFile to do it again. */
6448 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6449 {
6450 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6451 return INVALID_HANDLE_VALUE;
6452 }
6453 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6454 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6455 {
6456 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6457 return INVALID_HANDLE_VALUE;
6458 }
6459
6460 /* fallback */
6461 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6462 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6463 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6464 return hFile;
6465 }
6466 }
6467 }
6468 }
6469 }
6470
6471 /*
6472 * Okay, normal.
6473 */
6474 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6475 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6476 if (hFile != INVALID_HANDLE_VALUE)
6477 {
6478 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6479 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6480 }
6481 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6482 return hFile;
6483}
6484
6485
6486/** Kernel32 - CreateFileW */
6487static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6488 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6489 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6490{
6491 HANDLE hFile;
6492
6493#ifdef WITH_TEMP_MEMORY_FILES
6494 /*
6495 * Check for temporary files (cl.exe only).
6496 */
6497 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6498 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6499 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6500 && kwFsIsClTempFileW(pwszFilename))
6501 {
6502 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6503 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6504 return hFile;
6505 }
6506#endif
6507
6508 /*
6509 * Check for include files and similar that we do read-only caching of.
6510 */
6511 if (dwCreationDisposition == FILE_OPEN_IF)
6512 {
6513 if ( dwDesiredAccess == GENERIC_READ
6514 || dwDesiredAccess == FILE_GENERIC_READ)
6515 {
6516 if (dwShareMode & FILE_SHARE_READ)
6517 {
6518 if ( !pSecAttrs
6519 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6520 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6521 {
6522 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6523 {
6524 KFSLOOKUPERROR enmError;
6525 PKFSOBJ pFsObj;
6526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6527
6528 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6529 if (pFsObj)
6530 {
6531 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6532 &hFile);
6533 kFsCacheObjRelease(g_pFsCache, pFsObj);
6534 if (fRc)
6535 {
6536 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6537 return hFile;
6538 }
6539 }
6540 /* These are for nasm and yasm style header searching. Cache will
6541 already have checked the directories for the file, no need to call
6542 CreateFile to do it again. */
6543 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6544 {
6545 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6546 return INVALID_HANDLE_VALUE;
6547 }
6548 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6549 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6550 {
6551 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6552 return INVALID_HANDLE_VALUE;
6553 }
6554
6555 /* fallback */
6556 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6557 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6558 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6559 return hFile;
6560 }
6561 }
6562 else
6563 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6564 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6565 }
6566 else
6567 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6568 }
6569 else
6570 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6571 }
6572 else
6573 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6574
6575 /*
6576 * Okay, normal.
6577 */
6578 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6579 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6580 if (hFile != INVALID_HANDLE_VALUE)
6581 {
6582 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6583 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6584 }
6585 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6586 return hFile;
6587}
6588
6589
6590/** Kernel32 - SetFilePointer */
6591static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6592{
6593 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6594 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6595 if (idxHandle < g_Sandbox.cHandles)
6596 {
6597 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6598 if (pHandle != NULL)
6599 {
6600 KU32 cbFile;
6601 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6602 switch (pHandle->enmType)
6603 {
6604 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6605 cbFile = pHandle->u.pCachedFile->cbCached;
6606 break;
6607#ifdef WITH_TEMP_MEMORY_FILES
6608 case KWHANDLETYPE_TEMP_FILE:
6609 cbFile = pHandle->u.pTempFile->cbFile;
6610 break;
6611#endif
6612 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6613 case KWHANDLETYPE_OUTPUT_BUF:
6614 default:
6615 kHlpAssertFailed();
6616 SetLastError(ERROR_INVALID_FUNCTION);
6617 return INVALID_SET_FILE_POINTER;
6618 }
6619
6620 switch (dwMoveMethod)
6621 {
6622 case FILE_BEGIN:
6623 break;
6624 case FILE_CURRENT:
6625 offMove += pHandle->offFile;
6626 break;
6627 case FILE_END:
6628 offMove += cbFile;
6629 break;
6630 default:
6631 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6632 SetLastError(ERROR_INVALID_PARAMETER);
6633 return INVALID_SET_FILE_POINTER;
6634 }
6635 if (offMove >= 0)
6636 {
6637 if (offMove >= (KSSIZE)cbFile)
6638 {
6639 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6640 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6641 offMove = (KSSIZE)cbFile;
6642 /* For writable files, seeking beyond the end is fine, but check that we've got
6643 the type range for the request. */
6644 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6645 {
6646 kHlpAssertMsgFailed(("%#llx\n", offMove));
6647 SetLastError(ERROR_SEEK);
6648 return INVALID_SET_FILE_POINTER;
6649 }
6650 }
6651 pHandle->offFile = (KU32)offMove;
6652 }
6653 else
6654 {
6655 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6656 SetLastError(ERROR_NEGATIVE_SEEK);
6657 return INVALID_SET_FILE_POINTER;
6658 }
6659 if (pcbMoveHi)
6660 *pcbMoveHi = (KU64)offMove >> 32;
6661 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6662 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6663 SetLastError(NO_ERROR);
6664 return (KU32)offMove;
6665 }
6666 }
6667 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6668 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6669}
6670
6671
6672/** Kernel32 - SetFilePointerEx */
6673static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6674 DWORD dwMoveMethod)
6675{
6676 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6677 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6678 if (idxHandle < g_Sandbox.cHandles)
6679 {
6680 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6681 if (pHandle != NULL)
6682 {
6683 KI64 offMyMove = offMove.QuadPart;
6684 KU32 cbFile;
6685 switch (pHandle->enmType)
6686 {
6687 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6688 cbFile = pHandle->u.pCachedFile->cbCached;
6689 break;
6690#ifdef WITH_TEMP_MEMORY_FILES
6691 case KWHANDLETYPE_TEMP_FILE:
6692 cbFile = pHandle->u.pTempFile->cbFile;
6693 break;
6694#endif
6695 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6696 case KWHANDLETYPE_OUTPUT_BUF:
6697 default:
6698 kHlpAssertFailed();
6699 SetLastError(ERROR_INVALID_FUNCTION);
6700 return INVALID_SET_FILE_POINTER;
6701 }
6702
6703 switch (dwMoveMethod)
6704 {
6705 case FILE_BEGIN:
6706 break;
6707 case FILE_CURRENT:
6708 offMyMove += pHandle->offFile;
6709 break;
6710 case FILE_END:
6711 offMyMove += cbFile;
6712 break;
6713 default:
6714 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6715 SetLastError(ERROR_INVALID_PARAMETER);
6716 return INVALID_SET_FILE_POINTER;
6717 }
6718 if (offMyMove >= 0)
6719 {
6720 if (offMyMove >= (KSSIZE)cbFile)
6721 {
6722 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6723 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6724 offMyMove = (KSSIZE)cbFile;
6725 /* For writable files, seeking beyond the end is fine, but check that we've got
6726 the type range for the request. */
6727 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6728 {
6729 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6730 SetLastError(ERROR_SEEK);
6731 return INVALID_SET_FILE_POINTER;
6732 }
6733 }
6734 pHandle->offFile = (KU32)offMyMove;
6735 }
6736 else
6737 {
6738 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6739 SetLastError(ERROR_NEGATIVE_SEEK);
6740 return INVALID_SET_FILE_POINTER;
6741 }
6742 if (poffNew)
6743 poffNew->QuadPart = offMyMove;
6744 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6745 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6746 return TRUE;
6747 }
6748 }
6749 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6750 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6751}
6752
6753
6754/** Kernel32 - ReadFile */
6755static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6756 LPOVERLAPPED pOverlapped)
6757{
6758 BOOL fRet;
6759 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6760 g_cReadFileCalls++;
6761 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6762 if (idxHandle < g_Sandbox.cHandles)
6763 {
6764 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6765 if (pHandle != NULL)
6766 {
6767 switch (pHandle->enmType)
6768 {
6769 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6770 {
6771 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6772 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6773 if (cbActually > cbToRead)
6774 cbActually = cbToRead;
6775
6776#ifdef WITH_HASH_MD5_CACHE
6777 if (g_Sandbox.pHashHead)
6778 {
6779 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6780 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6781 g_Sandbox.LastHashRead.cbRead = cbActually;
6782 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6783 }
6784#endif
6785
6786 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6787 pHandle->offFile += cbActually;
6788
6789 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6790 *pcbActuallyRead = cbActually;
6791
6792 g_cbReadFileFromReadCached += cbActually;
6793 g_cbReadFileTotal += cbActually;
6794 g_cReadFileFromReadCached++;
6795
6796 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6797 return TRUE;
6798 }
6799
6800#ifdef WITH_TEMP_MEMORY_FILES
6801 case KWHANDLETYPE_TEMP_FILE:
6802 {
6803 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6804 KU32 cbActually;
6805 if (pHandle->offFile < pTempFile->cbFile)
6806 {
6807 cbActually = pTempFile->cbFile - pHandle->offFile;
6808 if (cbActually > cbToRead)
6809 cbActually = cbToRead;
6810
6811 /* Copy the data. */
6812 if (cbActually > 0)
6813 {
6814 KU32 cbLeft;
6815 KU32 offSeg;
6816 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6817
6818 /* Locate the segment containing the byte at offFile. */
6819 KU32 iSeg = pTempFile->cSegs - 1;
6820 kHlpAssert(pTempFile->cSegs > 0);
6821 while (paSegs[iSeg].offData > pHandle->offFile)
6822 iSeg--;
6823
6824 /* Copy out the data. */
6825 cbLeft = cbActually;
6826 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6827 for (;;)
6828 {
6829 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6830 if (cbAvail >= cbLeft)
6831 {
6832 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6833 break;
6834 }
6835
6836 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6837 cbLeft -= cbAvail;
6838 offSeg = 0;
6839 iSeg++;
6840 kHlpAssert(iSeg < pTempFile->cSegs);
6841 }
6842
6843 /* Update the file offset. */
6844 pHandle->offFile += cbActually;
6845 }
6846 }
6847 /* Read does not commit file space, so return zero bytes. */
6848 else
6849 cbActually = 0;
6850
6851 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6852 *pcbActuallyRead = cbActually;
6853
6854 g_cbReadFileTotal += cbActually;
6855 g_cbReadFileFromInMemTemp += cbActually;
6856 g_cReadFileFromInMemTemp++;
6857
6858 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6859 return TRUE;
6860 }
6861#endif /* WITH_TEMP_MEMORY_FILES */
6862
6863 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6864 case KWHANDLETYPE_OUTPUT_BUF:
6865 default:
6866 kHlpAssertFailed();
6867 SetLastError(ERROR_INVALID_FUNCTION);
6868 *pcbActuallyRead = 0;
6869 return FALSE;
6870 }
6871 }
6872 }
6873
6874 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
6875 if (fRet && pcbActuallyRead)
6876 g_cbReadFileTotal += *pcbActuallyRead;
6877 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
6878 return fRet;
6879}
6880
6881
6882/** Kernel32 - ReadFileEx */
6883static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
6884 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
6885{
6886 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6887 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6888 if (idxHandle < g_Sandbox.cHandles)
6889 {
6890 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6891 if (pHandle != NULL)
6892 {
6893 kHlpAssertFailed();
6894 }
6895 }
6896
6897 KWFS_LOG(("ReadFile(%p)\n", hFile));
6898 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
6899}
6900
6901#ifdef WITH_STD_OUT_ERR_BUFFERING
6902
6903/**
6904 * Write something to a handle, making sure everything is actually written.
6905 *
6906 * @param hHandle Where to write it to.
6907 * @param pchBuf What to write
6908 * @param cchToWrite How much to write.
6909 */
6910static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
6911{
6912 if (cchToWrite > 0)
6913 {
6914 DWORD cchWritten = 0;
6915 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
6916 {
6917 if (cchWritten == cchToWrite)
6918 { /* likely */ }
6919 else
6920 {
6921 do
6922 {
6923 pchBuf += cchWritten;
6924 cchToWrite -= cchWritten;
6925 cchWritten = 0;
6926 } while ( cchToWrite > 0
6927 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
6928 }
6929 }
6930 else
6931 kHlpAssertFailed();
6932 }
6933}
6934
6935
6936/**
6937 * Worker for WriteFile when the output isn't going to the console.
6938 *
6939 * @param pSandbox The sandbox.
6940 * @param pOutBuf The output buffer.
6941 * @param pchBuffer What to write.
6942 * @param cchToWrite How much to write.
6943 */
6944static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
6945{
6946 if (pOutBuf->u.Fully.cchBufAlloc > 0)
6947 { /* likely */ }
6948 else
6949 {
6950 /* No realloc, max size is 64KB. */
6951 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
6952 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6953 if (!pOutBuf->u.Fully.pchBuf)
6954 {
6955 while ( !pOutBuf->u.Fully.pchBuf
6956 && pOutBuf->u.Fully.cchBufAlloc > 64)
6957 {
6958 pOutBuf->u.Fully.cchBufAlloc /= 2;
6959 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6960 }
6961 if (!pOutBuf->u.Fully.pchBuf)
6962 {
6963 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
6964 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
6965 }
6966 }
6967 }
6968
6969 /*
6970 * Special case: Output ends with newline and fits in the buffer.
6971 */
6972 if ( cchToWrite > 1
6973 && pchBuffer[cchToWrite - 1] == '\n'
6974 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6975 {
6976 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
6977 pOutBuf->u.Fully.cchBuf += cchToWrite;
6978 }
6979 else
6980 {
6981 /*
6982 * Work thru the text line by line, flushing the buffer when
6983 * appropriate. The buffer is not a line buffer here, it's a
6984 * full buffer.
6985 */
6986 while (cchToWrite > 0)
6987 {
6988 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
6989 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
6990 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6991 {
6992 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
6993 pOutBuf->u.Fully.cchBuf += cchLine;
6994 }
6995 /*
6996 * Option one: Flush the buffer and the current line.
6997 *
6998 * We choose this one when the line won't ever fit, or when we have
6999 * an incomplete line in the buffer.
7000 */
7001 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7002 || pOutBuf->u.Fully.cchBuf == 0
7003 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7004 {
7005 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7006 if (pOutBuf->u.Fully.cchBuf > 0)
7007 {
7008 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7009 pOutBuf->u.Fully.cchBuf = 0;
7010 }
7011 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7012 }
7013 /*
7014 * Option two: Only flush the lines in the buffer.
7015 */
7016 else
7017 {
7018 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7019 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7020 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7021 pOutBuf->u.Fully.cchBuf = cchLine;
7022 }
7023
7024 /* advance */
7025 pchBuffer += cchLine;
7026 cchToWrite -= cchLine;
7027 }
7028 }
7029}
7030
7031#endif /* WITH_STD_OUT_ERR_BUFFERING */
7032
7033#ifdef WITH_TEMP_MEMORY_FILES
7034static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7035{
7036 KU32 cbMinFile = offFile + cbNeeded;
7037 if (cbMinFile >= offFile)
7038 {
7039 /* Calc how much space we've already allocated and */
7040 if (cbMinFile <= pTempFile->cbFileAllocated)
7041 return K_TRUE;
7042
7043 /* Grow the file. */
7044 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7045 {
7046 int rc;
7047 KU32 cSegs = pTempFile->cSegs;
7048 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7049 do
7050 {
7051 /* grow the segment array? */
7052 if ((cSegs % 16) == 0)
7053 {
7054 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7055 if (!pvNew)
7056 return K_FALSE;
7057 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7058 }
7059
7060 /* Use page alloc here to simplify mapping later. */
7061 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7062 if (rc == 0)
7063 { /* likely */ }
7064 else
7065 {
7066 cbNewSeg = 64*1024;
7067 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7068 if (rc != 0)
7069 {
7070 kHlpAssertFailed();
7071 return K_FALSE;
7072 }
7073 }
7074 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7075 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7076 pTempFile->cbFileAllocated += cbNewSeg;
7077 pTempFile->cSegs = ++cSegs;
7078
7079 } while (pTempFile->cbFileAllocated < cbMinFile);
7080
7081 return K_TRUE;
7082 }
7083 }
7084
7085 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7086 return K_FALSE;
7087}
7088#endif /* WITH_TEMP_MEMORY_FILES */
7089
7090
7091#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7092/** Kernel32 - WriteFile */
7093static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7094 LPOVERLAPPED pOverlapped)
7095{
7096 BOOL fRet;
7097 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7098 g_cWriteFileCalls++;
7099 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7100 if (idxHandle < g_Sandbox.cHandles)
7101 {
7102 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7103 if (pHandle != NULL)
7104 {
7105 switch (pHandle->enmType)
7106 {
7107# ifdef WITH_TEMP_MEMORY_FILES
7108 case KWHANDLETYPE_TEMP_FILE:
7109 {
7110 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7111
7112 kHlpAssert(!pOverlapped);
7113 kHlpAssert(pcbActuallyWritten);
7114
7115 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7116 {
7117 KU32 cbLeft;
7118 KU32 offSeg;
7119
7120 /* Locate the segment containing the byte at offFile. */
7121 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7122 KU32 iSeg = pTempFile->cSegs - 1;
7123 kHlpAssert(pTempFile->cSegs > 0);
7124 while (paSegs[iSeg].offData > pHandle->offFile)
7125 iSeg--;
7126
7127 /* Copy in the data. */
7128 cbLeft = cbToWrite;
7129 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7130 for (;;)
7131 {
7132 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7133 if (cbAvail >= cbLeft)
7134 {
7135 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7136 break;
7137 }
7138
7139 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7140 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7141 cbLeft -= cbAvail;
7142 offSeg = 0;
7143 iSeg++;
7144 kHlpAssert(iSeg < pTempFile->cSegs);
7145 }
7146
7147 /* Update the file offset. */
7148 pHandle->offFile += cbToWrite;
7149 if (pHandle->offFile > pTempFile->cbFile)
7150 pTempFile->cbFile = pHandle->offFile;
7151
7152 *pcbActuallyWritten = cbToWrite;
7153
7154 g_cbWriteFileTotal += cbToWrite;
7155 g_cbWriteFileToInMemTemp += cbToWrite;
7156 g_cWriteFileToInMemTemp++;
7157
7158 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7159 return TRUE;
7160 }
7161
7162 kHlpAssertFailed();
7163 *pcbActuallyWritten = 0;
7164 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7165 return FALSE;
7166 }
7167# endif
7168
7169 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7170 kHlpAssertFailed();
7171 SetLastError(ERROR_ACCESS_DENIED);
7172 *pcbActuallyWritten = 0;
7173 return FALSE;
7174
7175# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7176 /*
7177 * Standard output & error.
7178 */
7179 case KWHANDLETYPE_OUTPUT_BUF:
7180 {
7181 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7182 if (pOutBuf->fIsConsole)
7183 {
7184 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7185 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7186 }
7187 else
7188 {
7189# ifdef WITH_STD_OUT_ERR_BUFFERING
7190 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7191 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7192 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7193# else
7194 kHlpAssertFailed();
7195# endif
7196 }
7197 if (pcbActuallyWritten)
7198 *pcbActuallyWritten = cbToWrite;
7199 g_cbWriteFileTotal += cbToWrite;
7200 return TRUE;
7201 }
7202# endif
7203
7204 default:
7205 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7206 kHlpAssertFailed();
7207 SetLastError(ERROR_INVALID_FUNCTION);
7208 *pcbActuallyWritten = 0;
7209 return FALSE;
7210 }
7211 }
7212 }
7213
7214 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7215 if (fRet && pcbActuallyWritten)
7216 g_cbWriteFileTotal += *pcbActuallyWritten;
7217 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7218 return fRet;
7219}
7220
7221
7222/** Kernel32 - WriteFileEx */
7223static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7224 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7225{
7226 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7227 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7228 if (idxHandle < g_Sandbox.cHandles)
7229 {
7230 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7231 if (pHandle != NULL)
7232 {
7233 kHlpAssertFailed();
7234 }
7235 }
7236
7237 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7238 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7239}
7240
7241#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7242
7243#ifdef WITH_TEMP_MEMORY_FILES
7244
7245/** Kernel32 - SetEndOfFile; */
7246static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7247{
7248 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7249 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7250 if (idxHandle < g_Sandbox.cHandles)
7251 {
7252 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7253 if (pHandle != NULL)
7254 {
7255 switch (pHandle->enmType)
7256 {
7257 case KWHANDLETYPE_TEMP_FILE:
7258 {
7259 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7260 if ( pHandle->offFile > pTempFile->cbFile
7261 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7262 {
7263 kHlpAssertFailed();
7264 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7265 return FALSE;
7266 }
7267
7268 pTempFile->cbFile = pHandle->offFile;
7269 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7270 return TRUE;
7271 }
7272
7273 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7274 kHlpAssertFailed();
7275 SetLastError(ERROR_ACCESS_DENIED);
7276 return FALSE;
7277
7278 case KWHANDLETYPE_OUTPUT_BUF:
7279 kHlpAssertFailed();
7280 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7281 return FALSE;
7282
7283 default:
7284 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7285 kHlpAssertFailed();
7286 SetLastError(ERROR_INVALID_FUNCTION);
7287 return FALSE;
7288 }
7289 }
7290 }
7291
7292 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7293 return SetEndOfFile(hFile);
7294}
7295
7296
7297/** Kernel32 - GetFileType */
7298static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7299{
7300 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7301 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7302 if (idxHandle < g_Sandbox.cHandles)
7303 {
7304 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7305 if (pHandle != NULL)
7306 {
7307 switch (pHandle->enmType)
7308 {
7309 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7310 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7311 return FILE_TYPE_DISK;
7312
7313 case KWHANDLETYPE_TEMP_FILE:
7314 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7315 return FILE_TYPE_DISK;
7316
7317 case KWHANDLETYPE_OUTPUT_BUF:
7318 {
7319 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7320 DWORD fRet;
7321 if (pOutBuf->fFileType != KU8_MAX)
7322 {
7323 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7324 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7325 }
7326 else
7327 {
7328 fRet = GetFileType(hFile);
7329 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7330 }
7331 return fRet;
7332 }
7333
7334 }
7335 }
7336 }
7337
7338 KWFS_LOG(("GetFileType(%p)\n", hFile));
7339 return GetFileType(hFile);
7340}
7341
7342
7343/** Kernel32 - GetFileSize */
7344static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7345{
7346 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7347 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7348 if (idxHandle < g_Sandbox.cHandles)
7349 {
7350 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7351 if (pHandle != NULL)
7352 {
7353 if (pcbHighDword)
7354 *pcbHighDword = 0;
7355 SetLastError(NO_ERROR);
7356 switch (pHandle->enmType)
7357 {
7358 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7359 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7360 return pHandle->u.pCachedFile->cbCached;
7361
7362 case KWHANDLETYPE_TEMP_FILE:
7363 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7364 return pHandle->u.pTempFile->cbFile;
7365
7366 case KWHANDLETYPE_OUTPUT_BUF:
7367 /* do default */
7368 break;
7369
7370 default:
7371 kHlpAssertFailed();
7372 SetLastError(ERROR_INVALID_FUNCTION);
7373 return INVALID_FILE_SIZE;
7374 }
7375 }
7376 }
7377
7378 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7379 return GetFileSize(hFile, pcbHighDword);
7380}
7381
7382
7383/** Kernel32 - GetFileSizeEx */
7384static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7385{
7386 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7387 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7388 if (idxHandle < g_Sandbox.cHandles)
7389 {
7390 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7391 if (pHandle != NULL)
7392 {
7393 switch (pHandle->enmType)
7394 {
7395 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7396 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7397 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7398 return TRUE;
7399
7400 case KWHANDLETYPE_TEMP_FILE:
7401 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7402 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7403 return TRUE;
7404
7405 case KWHANDLETYPE_OUTPUT_BUF:
7406 /* do default */
7407 break;
7408
7409 default:
7410 kHlpAssertFailed();
7411 SetLastError(ERROR_INVALID_FUNCTION);
7412 return INVALID_FILE_SIZE;
7413 }
7414 }
7415 }
7416
7417 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7418 return GetFileSizeEx(hFile, pcbFile);
7419}
7420
7421
7422/** Kernel32 - CreateFileMappingW */
7423static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7424 DWORD fProtect, DWORD dwMaximumSizeHigh,
7425 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7426{
7427 HANDLE hMapping;
7428 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7429 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7430 if (idxHandle < g_Sandbox.cHandles)
7431 {
7432 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7433 if (pHandle != NULL)
7434 {
7435 switch (pHandle->enmType)
7436 {
7437 case KWHANDLETYPE_TEMP_FILE:
7438 {
7439 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7440 if ( ( fProtect == PAGE_READONLY
7441 || fProtect == PAGE_EXECUTE_READ)
7442 && dwMaximumSizeHigh == 0
7443 && ( dwMaximumSizeLow == 0
7444 || dwMaximumSizeLow == pTempFile->cbFile)
7445 && pwszName == NULL)
7446 {
7447 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7448 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7449 return hMapping;
7450 }
7451 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7452 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7453 SetLastError(ERROR_ACCESS_DENIED);
7454 return INVALID_HANDLE_VALUE;
7455 }
7456
7457 /* moc.exe benefits from this. */
7458 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7459 {
7460 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7461 if ( ( fProtect == PAGE_READONLY
7462 || fProtect == PAGE_EXECUTE_READ)
7463 && dwMaximumSizeHigh == 0
7464 && ( dwMaximumSizeLow == 0
7465 || dwMaximumSizeLow == pCachedFile->cbCached)
7466 && pwszName == NULL)
7467 {
7468 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7469 K_FALSE /*fIsFileHandle*/, &hMapping))
7470 { /* likely */ }
7471 else
7472 hMapping = NULL;
7473 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7474 return hMapping;
7475 }
7476
7477 /* Do fallback (for .pch). */
7478 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7479 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7480 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7481
7482 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7483 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7484 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7485 return hMapping;
7486 }
7487
7488 /** @todo read cached memory mapped files too for moc. */
7489 }
7490 }
7491 }
7492
7493 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7494 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7495 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7496 return hMapping;
7497}
7498
7499
7500/** Kernel32 - MapViewOfFile */
7501static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7502 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7503{
7504 PVOID pvRet;
7505 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7506 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7507 if (idxHandle < g_Sandbox.cHandles)
7508 {
7509 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7510 if (pHandle != NULL)
7511 {
7512 KU32 idxMapping;
7513
7514 /*
7515 * Ensure one free entry in the mapping tracking table first,
7516 * since this is common to both temporary and cached files.
7517 */
7518 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7519 { /* likely */ }
7520 else
7521 {
7522 void *pvNew;
7523 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7524 if (cNew)
7525 cNew *= 2;
7526 else
7527 cNew = 32;
7528 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings));
7529 if (pvNew)
7530 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7531 else
7532 {
7533 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7534 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7535 return NULL;
7536 }
7537 g_Sandbox.cMemMappingsAlloc = cNew;
7538 }
7539
7540 /*
7541 * Type specific work.
7542 */
7543 switch (pHandle->enmType)
7544 {
7545 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7546 case KWHANDLETYPE_TEMP_FILE:
7547 case KWHANDLETYPE_OUTPUT_BUF:
7548 default:
7549 kHlpAssertFailed();
7550 SetLastError(ERROR_INVALID_OPERATION);
7551 return NULL;
7552
7553 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7554 {
7555 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7556 if ( dwDesiredAccess == FILE_MAP_READ
7557 && offFileHigh == 0
7558 && offFileLow == 0
7559 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7560 {
7561 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7562 if (pTempFile->cSegs != 1)
7563 {
7564 KU32 iSeg;
7565 KU32 cbLeft;
7566 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7567 KU8 *pbAll = NULL;
7568 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7569 if (rc != 0)
7570 {
7571 kHlpAssertFailed();
7572 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7573 return NULL;
7574 }
7575
7576 cbLeft = pTempFile->cbFile;
7577 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7578 {
7579 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7580 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7581 cbLeft -= cbToCopy;
7582 }
7583
7584 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7585 {
7586 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7587 pTempFile->paSegs[iSeg].pbData = NULL;
7588 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7589 }
7590
7591 pTempFile->cSegs = 1;
7592 pTempFile->cbFileAllocated = cbAll;
7593 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7594 pTempFile->paSegs[0].pbData = pbAll;
7595 pTempFile->paSegs[0].offData = 0;
7596 }
7597
7598 pTempFile->cMappings++;
7599 kHlpAssert(pTempFile->cMappings == 1);
7600
7601 pvRet = pTempFile->paSegs[0].pbData;
7602 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7603 break;
7604 }
7605
7606 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7607 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7608 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7609 return NULL;
7610 }
7611
7612 /*
7613 * This is simple in comparison to the above temporary file code.
7614 */
7615 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7616 {
7617 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7618 if ( dwDesiredAccess == FILE_MAP_READ
7619 && offFileHigh == 0
7620 && offFileLow == 0
7621 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7622 {
7623 pvRet = pCachedFile->pbCached;
7624 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7625 break;
7626 }
7627 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7628 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7629 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7630 return NULL;
7631 }
7632 }
7633
7634 /*
7635 * Insert into the mapping tracking table. This is common
7636 * and we should only get here with a non-NULL pvRet.
7637 *
7638 * Note! We could look for duplicates and do ref counting, but it's
7639 * easier to just append for now.
7640 */
7641 kHlpAssert(pvRet != NULL);
7642 idxMapping = g_Sandbox.cMemMappings;
7643 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7644
7645 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7646 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7647 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7648 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7649 g_Sandbox.cMemMappings++;
7650
7651 return pvRet;
7652 }
7653 }
7654
7655 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7656 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7657 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7658 return pvRet;
7659}
7660
7661
7662/** Kernel32 - MapViewOfFileEx */
7663static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7664 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7665{
7666 PVOID pvRet;
7667 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7668 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7669 if (idxHandle < g_Sandbox.cHandles)
7670 {
7671 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7672 if (pHandle != NULL)
7673 {
7674 switch (pHandle->enmType)
7675 {
7676 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7677 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7678 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7679 if (!pvMapAddr)
7680 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7681 kHlpAssertFailed();
7682 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7683 return NULL;
7684
7685 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7686 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7687 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7688 if (!pvMapAddr)
7689 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7690 /* We can use fallback here as the handle is an actual section handle. */
7691 break;
7692
7693 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7694 case KWHANDLETYPE_TEMP_FILE:
7695 case KWHANDLETYPE_OUTPUT_BUF:
7696 default:
7697 kHlpAssertFailed();
7698 SetLastError(ERROR_INVALID_OPERATION);
7699 return NULL;
7700 }
7701 }
7702 }
7703
7704 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7705 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7706 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7707 return pvRet;
7708
7709}
7710
7711/** Kernel32 - UnmapViewOfFile */
7712static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7713{
7714 /*
7715 * Consult the memory mapping tracker.
7716 */
7717 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7718 KU32 idxMapping = g_Sandbox.cMemMappings;
7719 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7720 while (idxMapping-- > 0)
7721 if (paMemMappings[idxMapping].pvMapping == pvBase)
7722 {
7723 /* Type specific stuff. */
7724 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7725 {
7726 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7727 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7728 }
7729 else
7730 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7731
7732 /* Deref and probably free it. */
7733 if (--paMemMappings[idxMapping].cRefs == 0)
7734 {
7735 g_Sandbox.cMemMappings--;
7736 if (idxMapping != g_Sandbox.cMemMappings)
7737 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7738 }
7739 return TRUE;
7740 }
7741
7742 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7743 return UnmapViewOfFile(pvBase);
7744}
7745
7746/** @todo UnmapViewOfFileEx */
7747
7748#endif /* WITH_TEMP_MEMORY_FILES */
7749
7750
7751/** Kernel32 - DuplicateHandle */
7752static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7753 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7754{
7755 BOOL fRet;
7756
7757 /*
7758 * We must catch our handles being duplicated.
7759 */
7760 if (hSrcProc == GetCurrentProcess())
7761 {
7762 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7763 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7764 if (idxHandle < g_Sandbox.cHandles)
7765 {
7766 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7767 if (pHandle)
7768 {
7769 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7770 if (fRet)
7771 {
7772 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7773 {
7774 pHandle->cRefs++;
7775 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7776 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7777 pHandle->enmType, pHandle->cRefs));
7778 }
7779 else
7780 {
7781 fRet = FALSE;
7782 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7783 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7784 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7785 }
7786 }
7787 else
7788 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7789 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7790 return fRet;
7791 }
7792 }
7793 }
7794
7795 /*
7796 * Not one of ours, just do what the caller asks and log it.
7797 */
7798 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7799 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7800 fInheritHandle, dwOptions, fRet, *phNew));
7801 return fRet;
7802}
7803
7804
7805/** Kernel32 - CloseHandle */
7806static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7807{
7808 BOOL fRet;
7809 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7810 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7811 if (idxHandle < g_Sandbox.cHandles)
7812 {
7813 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7814 if (pHandle)
7815 {
7816 /* Prevent the closing of the standard output and error handles. */
7817 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7818 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7819 {
7820 fRet = CloseHandle(hObject);
7821 if (fRet)
7822 {
7823 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7824 g_Sandbox.papHandles[idxHandle] = NULL;
7825 g_Sandbox.cActiveHandles--;
7826 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7827 if (--pHandle->cRefs == 0)
7828 {
7829#ifdef WITH_TEMP_MEMORY_FILES
7830 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7831 {
7832 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7833 pHandle->u.pTempFile->cActiveHandles--;
7834 }
7835#endif
7836 kHlpFree(pHandle);
7837 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7838 }
7839 else
7840 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7841 }
7842 else
7843 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7844 }
7845 else
7846 {
7847 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7848 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7849 fRet = TRUE;
7850 }
7851 return fRet;
7852 }
7853 }
7854
7855 fRet = CloseHandle(hObject);
7856 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7857 return fRet;
7858}
7859
7860
7861/** Kernel32 - GetFileAttributesA. */
7862static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7863{
7864 DWORD fRet;
7865 const char *pszExt = kHlpGetExt(pszFilename);
7866 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7867 {
7868 KFSLOOKUPERROR enmError;
7869 PKFSOBJ pFsObj;
7870 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7871
7872 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7873 if (pFsObj)
7874 {
7875 kHlpAssert(pFsObj->fHaveStats);
7876 fRet = pFsObj->Stats.st_attribs;
7877 kFsCacheObjRelease(g_pFsCache, pFsObj);
7878 }
7879 else
7880 {
7881 SetLastError(kwFsLookupErrorToWindowsError(enmError));
7882 fRet = INVALID_FILE_ATTRIBUTES;
7883 }
7884
7885 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
7886 return fRet;
7887 }
7888
7889 fRet = GetFileAttributesA(pszFilename);
7890 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
7891 return fRet;
7892}
7893
7894
7895/** Kernel32 - GetFileAttributesW. */
7896static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
7897{
7898 DWORD fRet;
7899 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
7900 {
7901 KFSLOOKUPERROR enmError;
7902 PKFSOBJ pFsObj;
7903 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7904
7905 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
7906 if (pFsObj)
7907 {
7908 kHlpAssert(pFsObj->fHaveStats);
7909 fRet = pFsObj->Stats.st_attribs;
7910 kFsCacheObjRelease(g_pFsCache, pFsObj);
7911 }
7912 else
7913 {
7914 SetLastError(kwFsLookupErrorToWindowsError(enmError));
7915 fRet = INVALID_FILE_ATTRIBUTES;
7916 }
7917
7918 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
7919 return fRet;
7920 }
7921
7922 fRet = GetFileAttributesW(pwszFilename);
7923 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
7924 return fRet;
7925}
7926
7927
7928/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
7929 * directory containing each include file. We cache the result to speed
7930 * things up a little. */
7931static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
7932{
7933 DWORD cwcRet;
7934 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
7935 {
7936 KFSLOOKUPERROR enmError;
7937 PKFSOBJ pObj;
7938 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7939
7940 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
7941 if (pObj)
7942 {
7943 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
7944 {
7945 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
7946 {
7947 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
7948
7949 /* Should preserve trailing slash on directory paths. */
7950 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
7951 {
7952 if ( cwcRet + 1 < cwcShortPath
7953 && pwszShortPath[cwcRet - 1] != '\\')
7954 {
7955 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
7956 if ( cwcIn > 0
7957 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
7958 {
7959 pwszShortPath[cwcRet++] = '\\';
7960 pwszShortPath[cwcRet] = '\0';
7961 }
7962 }
7963 }
7964
7965 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
7966 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7967 kFsCacheObjRelease(g_pFsCache, pObj);
7968 return cwcRet;
7969 }
7970
7971 /* fall back for complicated cases. */
7972 }
7973 kFsCacheObjRelease(g_pFsCache, pObj);
7974 }
7975 }
7976 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
7977 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
7978 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7979 return cwcRet;
7980}
7981
7982
7983#ifdef WITH_TEMP_MEMORY_FILES
7984/** Kernel32 - DeleteFileW
7985 * Skip deleting the in-memory files. */
7986static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
7987{
7988 BOOL fRc;
7989 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7990 && kwFsIsClTempFileW(pwszFilename))
7991 {
7992 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7993 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
7994 fRc = TRUE;
7995 }
7996 else
7997 {
7998 fRc = DeleteFileW(pwszFilename);
7999 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8000 }
8001 return fRc;
8002}
8003#endif /* WITH_TEMP_MEMORY_FILES */
8004
8005
8006
8007#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8008
8009/*
8010 *
8011 * Console output buffering.
8012 * Console output buffering.
8013 * Console output buffering.
8014 *
8015 */
8016
8017
8018/**
8019 * Write a wide char string to the console.
8020 *
8021 * @param pSandbox The sandbox which output buffer to flush.
8022 */
8023static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8024{
8025 if (cwcToWrite > 0)
8026 {
8027 DWORD cwcWritten = 0;
8028 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8029 {
8030 if (cwcWritten == cwcToWrite)
8031 { /* likely */ }
8032 else
8033 {
8034 DWORD off = 0;
8035 do
8036 {
8037 off += cwcWritten;
8038 cwcWritten = 0;
8039 } while ( off < cwcToWrite
8040 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8041 kHlpAssert(off == cwcWritten);
8042 }
8043 }
8044 else
8045 kHlpAssertFailed();
8046 pSandbox->Combined.cFlushes++;
8047 }
8048}
8049
8050
8051/**
8052 * Flushes the combined console output buffer.
8053 *
8054 * @param pSandbox The sandbox which output buffer to flush.
8055 */
8056static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8057{
8058 if (pSandbox->Combined.cwcBuf > 0)
8059 {
8060 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8061 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8062 pSandbox->Combined.cwcBuf = 0;
8063 }
8064}
8065
8066
8067/**
8068 * For handling combined buffer overflow cases line by line.
8069 *
8070 * @param pSandbox The sandbox.
8071 * @param pwcBuf What to add to the combined buffer. Usually a
8072 * line, unless we're really low on buffer space.
8073 * @param cwcBuf The length of what to add.
8074 * @param fBrokenLine Whether this is a broken line.
8075 */
8076static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8077{
8078 if (fBrokenLine)
8079 kwSandboxConsoleFlushCombined(pSandbox);
8080 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8081 {
8082 kwSandboxConsoleFlushCombined(pSandbox);
8083 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8084 }
8085 else
8086 {
8087 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8088 pSandbox->Combined.cwcBuf += cwcBuf;
8089 }
8090}
8091
8092
8093/**
8094 * Called to final flush a line buffer via the combined buffer (if applicable).
8095 *
8096 * @param pSandbox The sandbox.
8097 * @param pLineBuf The line buffer.
8098 * @param pszName The line buffer name (for logging)
8099 */
8100static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8101{
8102 if (pLineBuf->fIsConsole)
8103 {
8104 if (pLineBuf->u.Con.cwcBuf > 0)
8105 {
8106 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8107
8108 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8109 {
8110 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8111 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8112 }
8113 else
8114 {
8115 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8116 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8117 }
8118 pLineBuf->u.Con.cwcBuf = 0;
8119 }
8120 }
8121#ifdef WITH_STD_OUT_ERR_BUFFERING
8122 else if (pLineBuf->u.Fully.cchBuf > 0)
8123 {
8124 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8125
8126 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8127 pLineBuf->u.Fully.cchBuf = 0;
8128 }
8129#endif
8130}
8131
8132
8133/**
8134 * Called at the end of sandboxed execution to flush both stream buffers.
8135 *
8136 * @param pSandbox The sandbox.
8137 */
8138static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8139{
8140 /*
8141 * First do the cl.exe source file supression trick, if applicable.
8142 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8143 * handle.
8144 */
8145 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8146 && pSandbox->Combined.cFlushes == 0)
8147 {
8148 if ( pSandbox->StdOut.fIsConsole
8149 || pSandbox->StdErr.fIsConsole)
8150 {
8151 if ( pSandbox->Combined.cwcBuf >= 3
8152 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8153 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8154 {
8155 KI32 off = pSandbox->Combined.cwcBuf - 1;
8156 if (pSandbox->Combined.wszBuf[off] == '\n')
8157 {
8158 KBOOL fOk = K_TRUE;
8159 while (off-- > 0)
8160 {
8161 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8162 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8163 { /* likely */ }
8164 else
8165 {
8166 fOk = K_FALSE;
8167 break;
8168 }
8169 }
8170 if (fOk)
8171 {
8172 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8173 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8174 pSandbox->Combined.cwcBuf = 0;
8175 return;
8176 }
8177 }
8178 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8179 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8180 }
8181 }
8182#ifdef WITH_STD_OUT_ERR_BUFFERING
8183 /*
8184 * Otherwise, it goes to standard output (redirected).
8185 */
8186 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8187 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8188 {
8189 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8190 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8191 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8192
8193 if (pchBuf[off] == '\n')
8194 {
8195 KBOOL fOk = K_TRUE;
8196 if (pchBuf[off - 1] == '\r')
8197 off--;
8198 while (off-- > 0)
8199 {
8200 char const ch = pchBuf[off];
8201 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8202 { /* likely */ }
8203 else
8204 {
8205 fOk = K_FALSE;
8206 break;
8207 }
8208 }
8209 if (fOk)
8210 {
8211 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8212 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8213 pSandbox->StdOut.u.Fully.cchBuf = 0;
8214 return;
8215 }
8216 }
8217 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8218 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8219 }
8220#endif
8221 }
8222
8223 /*
8224 * Flush the two line buffer, then the combined buffer.
8225 */
8226 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8227 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8228 kwSandboxConsoleFlushCombined(pSandbox);
8229}
8230
8231
8232/**
8233 * Writes a string to the given output stream.
8234 *
8235 * @param pSandbox The sandbox.
8236 * @param pLineBuf The line buffer for the output stream.
8237 * @param pwcBuffer The buffer to write.
8238 * @param cwcToWrite The number of wchar_t's in the buffer.
8239 */
8240static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8241{
8242 kHlpAssert(pLineBuf->fIsConsole);
8243 if (cwcToWrite > 0)
8244 {
8245 /*
8246 * First, find the start of the last incomplete line so we can figure
8247 * out how much line buffering we need to do.
8248 */
8249 KU32 cchLastIncompleteLine;
8250 KU32 offLastIncompleteLine = cwcToWrite;
8251 while ( offLastIncompleteLine > 0
8252 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8253 offLastIncompleteLine--;
8254 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8255
8256 /* Was there anything to line buffer? */
8257 if (offLastIncompleteLine < cwcToWrite)
8258 {
8259 /* Need to grow the line buffer? */
8260 KU32 cwcNeeded = offLastIncompleteLine == 0
8261 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8262 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8263 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8264 {
8265 void *pvNew;
8266 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8267 while (cwcNew < cwcNeeded)
8268 cwcNew *= 2;
8269 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8270 if (pvNew)
8271 {
8272 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8273 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8274 }
8275 else
8276 {
8277 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8278 if (pvNew)
8279 {
8280 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8281 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8282 }
8283 else
8284 {
8285 /* This isn't perfect, but it will have to do for now. */
8286 if (pLineBuf->u.Con.cwcBuf > 0)
8287 {
8288 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8289 K_TRUE /*fBrokenLine*/);
8290 pLineBuf->u.Con.cwcBuf = 0;
8291 }
8292 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8293 return;
8294 }
8295 }
8296 }
8297
8298 /*
8299 * Handle the case where we only add to the line buffer.
8300 */
8301 if (offLastIncompleteLine == 0)
8302 {
8303 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8304 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8305 return;
8306 }
8307 }
8308
8309 /*
8310 * If there is sufficient combined buffer to handle this request, this is rather simple.
8311 */
8312 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8313 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8314 {
8315 if (pLineBuf->u.Con.cwcBuf > 0)
8316 {
8317 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8318 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8319 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8320 pLineBuf->u.Con.cwcBuf = 0;
8321 }
8322
8323 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8324 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
8325 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
8326 }
8327 else
8328 {
8329 /*
8330 * Do line-by-line processing of the input, flusing the combined buffer
8331 * when it becomes necessary. We may have to write lines
8332 */
8333 KU32 off = 0;
8334 KU32 offNextLine = 0;
8335
8336 /* If there are buffered chars, we handle the first line outside the
8337 main loop. We must try our best outputting it as a complete line. */
8338 if (pLineBuf->u.Con.cwcBuf > 0)
8339 {
8340 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
8341 offNextLine++;
8342 offNextLine++;
8343 kHlpAssert(offNextLine <= offLastIncompleteLine);
8344
8345 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8346 {
8347 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8348 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8349 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8350 pLineBuf->u.Con.cwcBuf = 0;
8351
8352 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
8353 pSandbox->Combined.cwcBuf += offNextLine;
8354 }
8355 else
8356 {
8357 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
8358 if (cwcLeft > 0)
8359 {
8360 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
8361 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
8362 pLineBuf->u.Con.cwcBuf += cwcCopy;
8363 off += cwcCopy;
8364 }
8365 if (pLineBuf->u.Con.cwcBuf > 0)
8366 {
8367 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8368 K_TRUE /*fBrokenLine*/);
8369 pLineBuf->u.Con.cwcBuf = 0;
8370 }
8371 if (off < offNextLine)
8372 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
8373 }
8374 off = offNextLine;
8375 }
8376
8377 /* Deal with the remaining lines */
8378 while (off < offLastIncompleteLine)
8379 {
8380 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
8381 offNextLine++;
8382 offNextLine++;
8383 kHlpAssert(offNextLine <= offLastIncompleteLine);
8384 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
8385 off = offNextLine;
8386 }
8387 }
8388
8389 /*
8390 * Buffer any remaining incomplete line chars.
8391 */
8392 if (cchLastIncompleteLine)
8393 {
8394 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
8395 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
8396 }
8397 }
8398}
8399
8400
8401/**
8402 * Worker for WriteConsoleA and WriteFile.
8403 *
8404 * @param pSandbox The sandbox.
8405 * @param pLineBuf The line buffer.
8406 * @param pchBuffer What to write.
8407 * @param cchToWrite How much to write.
8408 */
8409static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
8410{
8411 /*
8412 * Convert it to wide char and use the 'W' to do the work.
8413 */
8414 int cwcRet;
8415 KU32 cwcBuf = cchToWrite * 2 + 1;
8416 wchar_t *pwcBufFree = NULL;
8417 wchar_t *pwcBuf;
8418 kHlpAssert(pLineBuf->fIsConsole);
8419
8420 if (cwcBuf <= 4096)
8421 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
8422 else
8423 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
8424
8425 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
8426 if (cwcRet > 0)
8427 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
8428 else
8429 {
8430 DWORD cchWritten;
8431 kHlpAssertFailed();
8432
8433 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
8434 if (pLineBuf->u.Con.cwcBuf > 0)
8435 {
8436 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
8437 pLineBuf->u.Con.cwcBuf = 0;
8438 }
8439 kwSandboxConsoleFlushCombined(pSandbox);
8440
8441 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8442 {
8443 if (cchWritten >= cchToWrite)
8444 { /* likely */ }
8445 else
8446 {
8447 KU32 off = 0;
8448 do
8449 {
8450 off += cchWritten;
8451 cchWritten = 0;
8452 } while ( off < cchToWrite
8453 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8454 }
8455 }
8456 }
8457
8458 if (pwcBufFree)
8459 kHlpFree(pwcBufFree);
8460}
8461
8462
8463/** Kernel32 - WriteConsoleA */
8464BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8465 PVOID pvReserved)
8466{
8467 BOOL fRc;
8468 PKWOUTPUTSTREAMBUF pLineBuf;
8469 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8470
8471 if (hConOutput == g_Sandbox.StdErr.hOutput)
8472 pLineBuf = &g_Sandbox.StdErr;
8473 else
8474 pLineBuf = &g_Sandbox.StdOut;
8475 if (pLineBuf->fIsConsole)
8476 {
8477 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8478
8479 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8480 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8481 if (pcbWritten)
8482 *pcbWritten = cbToWrite;
8483 fRc = TRUE;
8484 }
8485 else
8486 {
8487 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8488 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8489 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8490 }
8491 return fRc;
8492}
8493
8494
8495/** Kernel32 - WriteConsoleW */
8496BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8497 PVOID pvReserved)
8498{
8499 BOOL fRc;
8500 PKWOUTPUTSTREAMBUF pLineBuf;
8501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8502
8503 if (hConOutput == g_Sandbox.StdErr.hOutput)
8504 pLineBuf = &g_Sandbox.StdErr;
8505 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8506 pLineBuf = &g_Sandbox.StdOut;
8507 else
8508 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8509 if (pLineBuf->fIsConsole)
8510 {
8511 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8512
8513 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8514 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8515 if (pcwcWritten)
8516 *pcwcWritten = cwcToWrite;
8517 fRc = TRUE;
8518 }
8519 else
8520 {
8521 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8522 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8523 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8524 }
8525 return fRc;
8526}
8527
8528#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8529
8530
8531
8532/*
8533 *
8534 * Virtual memory leak prevension.
8535 * Virtual memory leak prevension.
8536 * Virtual memory leak prevension.
8537 *
8538 */
8539
8540#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8541
8542/** For debug logging. */
8543# ifndef NDEBUG
8544static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8545{
8546 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8547 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8548 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8549 if (cbMemInfo != 0)
8550 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8551 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8552 MemInfo.BaseAddress,
8553 MemInfo.AllocationBase,
8554 MemInfo.RegionSize,
8555 MemInfo.State,
8556 MemInfo.Protect,
8557 MemInfo.Type));
8558}
8559# else
8560# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8561# endif
8562
8563/**
8564 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8565 *
8566 * @param idxFixed The fixed allocation index to "free".
8567 */
8568static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8569{
8570 BOOL fRc;
8571 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8572 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8573 kHlpAssert(fRc); K_NOREF(fRc);
8574 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8575 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8576}
8577
8578#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8579
8580
8581/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8582 * location (~78MB in 32-bit 2010 compiler). */
8583static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8584{
8585 PVOID pvMem;
8586 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8587 {
8588 KU32 idxPreAllocated = KU32_MAX;
8589
8590#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8591 /*
8592 * Look for a pre-reserved CL.exe heap allocation.
8593 */
8594 pvMem = NULL;
8595 if ( pvAddr != 0
8596 && (fAllocType & MEM_RESERVE))
8597 {
8598 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8599 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8600 while (idxFixed-- > 0)
8601 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8602 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8603 {
8604 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8605 {
8606 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8607 {
8608 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8609 pvMem = pvAddr;
8610 idxPreAllocated = idxFixed;
8611 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8612 pvAddr, cb, fAllocType, fProt, pvMem));
8613 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8614 SetLastError(NO_ERROR);
8615 break;
8616 }
8617 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8618 }
8619 else
8620 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8621 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8622 }
8623 }
8624 if (!pvMem)
8625#endif
8626 {
8627 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8628 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8629 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8630 if (pvAddr && pvAddr != pvMem)
8631 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8632 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8633 }
8634
8635 if (pvMem)
8636 {
8637 /*
8638 * Track it.
8639 */
8640 PKWVIRTALLOC pTracker;
8641 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8642
8643 pTracker = g_Sandbox.pVirtualAllocHead;
8644 while ( pTracker
8645 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8646 pTracker = pTracker->pNext;
8647 if (!pTracker)
8648 {
8649 DWORD dwErr = GetLastError();
8650 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8651 if (pTracker)
8652 {
8653 pTracker->pvAlloc = pvMem;
8654 pTracker->cbAlloc = cb;
8655 pTracker->idxPreAllocated = idxPreAllocated;
8656 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8657 g_Sandbox.pVirtualAllocHead = pTracker;
8658 }
8659 SetLastError(dwErr);
8660 }
8661 }
8662 }
8663 else
8664 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8665 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8666 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8667 return pvMem;
8668}
8669
8670
8671/** Kernel32 - VirtualFree. */
8672static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8673{
8674 BOOL fRc;
8675 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8676 {
8677 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8678 if (dwFreeType & MEM_RELEASE)
8679 {
8680 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8681 if (pTracker)
8682 {
8683 if (pTracker->pvAlloc == pvAddr)
8684 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8685 else
8686 {
8687 PKWVIRTALLOC pPrev;
8688 do
8689 {
8690 pPrev = pTracker;
8691 pTracker = pTracker->pNext;
8692 } while (pTracker && pTracker->pvAlloc != pvAddr);
8693 if (pTracker)
8694 pPrev->pNext = pTracker->pNext;
8695 }
8696 if (pTracker)
8697 {
8698#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8699 if (pTracker->idxPreAllocated != KU32_MAX)
8700 {
8701 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8702 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8703 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8704 kHlpFree(pTracker);
8705 return TRUE;
8706 }
8707#endif
8708
8709 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8710 if (fRc)
8711 kHlpFree(pTracker);
8712 else
8713 {
8714 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8715 g_Sandbox.pVirtualAllocHead = pTracker;
8716 }
8717 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8718 return fRc;
8719 }
8720
8721 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8722 }
8723 }
8724 }
8725
8726#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8727 /*
8728 * Protect our fixed allocations (this isn't just paranoia, btw.).
8729 */
8730 if (dwFreeType & MEM_RELEASE)
8731 {
8732 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8733 while (idxFixed-- > 0)
8734 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8735 {
8736 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8737 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8738 return TRUE;
8739 }
8740 }
8741#endif
8742
8743 /*
8744 * Not tracker or not actually free the virtual range.
8745 */
8746 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8747 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8748 return fRc;
8749}
8750
8751
8752/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8753HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8754{
8755 HANDLE hHeap;
8756 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8757
8758 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8759 if (hHeap != NULL)
8760 {
8761 DWORD dwErr = GetLastError();
8762 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8763 if (pTracker)
8764 {
8765 pTracker->hHeap = hHeap;
8766 pTracker->pNext = g_Sandbox.pHeapHead;
8767 g_Sandbox.pHeapHead = pTracker;
8768 }
8769
8770 SetLastError(dwErr);
8771 }
8772 return hHeap;
8773
8774}
8775
8776
8777/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
8778BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
8779{
8780 BOOL fRc = HeapDestroy(hHeap);
8781 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
8782 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8783 if (fRc)
8784 {
8785 PKWHEAP pTracker = g_Sandbox.pHeapHead;
8786 if (pTracker)
8787 {
8788 if (pTracker->hHeap == hHeap)
8789 g_Sandbox.pHeapHead = pTracker->pNext;
8790 else
8791 {
8792 PKWHEAP pPrev;
8793 do
8794 {
8795 pPrev = pTracker;
8796 pTracker = pTracker->pNext;
8797 } while (pTracker && pTracker->hHeap == hHeap);
8798 if (pTracker)
8799 pPrev->pNext = pTracker->pNext;
8800 }
8801 if (pTracker)
8802 kHlpFree(pTracker);
8803 else
8804 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
8805 }
8806 }
8807
8808 return fRc;
8809}
8810
8811
8812
8813/*
8814 *
8815 * Thread/Fiber local storage leak prevention.
8816 * Thread/Fiber local storage leak prevention.
8817 * Thread/Fiber local storage leak prevention.
8818 *
8819 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
8820 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
8821 * we're leaking these indexes, but more importantely we crash during
8822 * worker exit since the callback is triggered multiple times.
8823 */
8824
8825
8826/** Kernel32 - FlsAlloc */
8827DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
8828{
8829 DWORD idxFls = FlsAlloc(pfnCallback);
8830 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
8831 if (idxFls != FLS_OUT_OF_INDEXES)
8832 {
8833 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8834 if (pTracker)
8835 {
8836 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8837 pTracker->idx = idxFls;
8838 pTracker->pNext = g_Sandbox.pFlsAllocHead;
8839 g_Sandbox.pFlsAllocHead = pTracker;
8840 }
8841 }
8842
8843 return idxFls;
8844}
8845
8846/** Kernel32 - FlsFree */
8847BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
8848{
8849 BOOL fRc = FlsFree(idxFls);
8850 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
8851 if (fRc)
8852 {
8853 PKWLOCALSTORAGE pTracker;
8854 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8855
8856 pTracker = g_Sandbox.pFlsAllocHead;
8857 if (pTracker)
8858 {
8859 if (pTracker->idx == idxFls)
8860 g_Sandbox.pFlsAllocHead = pTracker->pNext;
8861 else
8862 {
8863 PKWLOCALSTORAGE pPrev;
8864 do
8865 {
8866 pPrev = pTracker;
8867 pTracker = pTracker->pNext;
8868 } while (pTracker && pTracker->idx != idxFls);
8869 if (pTracker)
8870 pPrev->pNext = pTracker->pNext;
8871 }
8872 if (pTracker)
8873 {
8874 pTracker->idx = FLS_OUT_OF_INDEXES;
8875 pTracker->pNext = NULL;
8876 kHlpFree(pTracker);
8877 }
8878 }
8879 }
8880 return fRc;
8881}
8882
8883
8884/** Kernel32 - TlsAlloc */
8885DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
8886{
8887 DWORD idxTls = TlsAlloc();
8888 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
8889 if (idxTls != TLS_OUT_OF_INDEXES)
8890 {
8891 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8892 if (pTracker)
8893 {
8894 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8895 pTracker->idx = idxTls;
8896 pTracker->pNext = g_Sandbox.pTlsAllocHead;
8897 g_Sandbox.pTlsAllocHead = pTracker;
8898 }
8899 }
8900
8901 return idxTls;
8902}
8903
8904/** Kernel32 - TlsFree */
8905BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
8906{
8907 BOOL fRc = TlsFree(idxTls);
8908 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
8909 if (fRc)
8910 {
8911 PKWLOCALSTORAGE pTracker;
8912 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8913
8914 pTracker = g_Sandbox.pTlsAllocHead;
8915 if (pTracker)
8916 {
8917 if (pTracker->idx == idxTls)
8918 g_Sandbox.pTlsAllocHead = pTracker->pNext;
8919 else
8920 {
8921 PKWLOCALSTORAGE pPrev;
8922 do
8923 {
8924 pPrev = pTracker;
8925 pTracker = pTracker->pNext;
8926 } while (pTracker && pTracker->idx != idxTls);
8927 if (pTracker)
8928 pPrev->pNext = pTracker->pNext;
8929 }
8930 if (pTracker)
8931 {
8932 pTracker->idx = TLS_OUT_OF_INDEXES;
8933 pTracker->pNext = NULL;
8934 kHlpFree(pTracker);
8935 }
8936 }
8937 }
8938 return fRc;
8939}
8940
8941
8942
8943/*
8944 *
8945 * Header file hashing.
8946 * Header file hashing.
8947 * Header file hashing.
8948 *
8949 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
8950 * indicated that ~12% of the time was spent doing MD5 caluclation when
8951 * rebuiling openssl. The hashing it done right after reading the source
8952 * via ReadFile, same buffers and sizes.
8953 */
8954
8955#ifdef WITH_HASH_MD5_CACHE
8956
8957/** AdvApi32 - CryptCreateHash */
8958static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
8959 HCRYPTHASH *phHash)
8960{
8961 BOOL fRc;
8962
8963 /*
8964 * Only do this for cl.exe when it request normal MD5.
8965 */
8966 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8967 {
8968 if (idAlg == CALG_MD5)
8969 {
8970 if (hKey == 0)
8971 {
8972 if (dwFlags == 0)
8973 {
8974 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
8975 if (pHash)
8976 {
8977 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8978 pHash->uMagic = KWHASHMD5_MAGIC;
8979 pHash->cbHashed = 0;
8980 pHash->fGoneBad = K_FALSE;
8981 pHash->fFallbackMode = K_FALSE;
8982 pHash->fFinal = K_FALSE;
8983
8984 /* link it. */
8985 pHash->pNext = g_Sandbox.pHashHead;
8986 g_Sandbox.pHashHead = pHash;
8987
8988 *phHash = (KUPTR)pHash;
8989 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
8990 hProv, *phHash, TRUE));
8991 return TRUE;
8992 }
8993
8994 kwErrPrintf("CryptCreateHash: out of memory!\n");
8995 }
8996 else
8997 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
8998 }
8999 else
9000 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9001 }
9002 else
9003 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9004 }
9005
9006 /*
9007 * Fallback.
9008 */
9009 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9010 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9011 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9012 return fRc;
9013}
9014
9015
9016/** AdvApi32 - CryptHashData */
9017static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9018{
9019 BOOL fRc;
9020 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9021 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9022 while (pHash && (KUPTR)pHash != hHash)
9023 pHash = pHash->pNext;
9024 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9025 hHash, pHash, pbData, cbData, dwFlags));
9026 if (pHash)
9027 {
9028 /*
9029 * Validate the state.
9030 */
9031 if ( pHash->uMagic == KWHASHMD5_MAGIC
9032 && !pHash->fFinal)
9033 {
9034 if (!pHash->fFallbackMode)
9035 {
9036 /*
9037 * Does this match the previous ReadFile call to a cached file?
9038 * If it doesn't, try falling back.
9039 */
9040 if ( g_Sandbox.LastHashRead.cbRead == cbData
9041 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9042 {
9043 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9044 if ( pCachedFile
9045 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9046 {
9047
9048 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9049 {
9050 if ( pHash->pCachedFile == NULL
9051 && pHash->cbHashed == 0)
9052 pHash->pCachedFile = pCachedFile;
9053 if (pHash->pCachedFile == pCachedFile)
9054 {
9055 pHash->cbHashed += cbData;
9056 g_Sandbox.LastHashRead.pCachedFile = NULL;
9057 g_Sandbox.LastHashRead.pvRead = NULL;
9058 g_Sandbox.LastHashRead.cbRead = 0;
9059 g_Sandbox.LastHashRead.offRead = 0;
9060 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9061 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9062 return TRUE;
9063 }
9064
9065 /* Note! it's possible to fall back here too, if necessary. */
9066 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9067 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9068 }
9069 else
9070 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9071 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9072 }
9073 else if (!pCachedFile)
9074 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9075 else
9076 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9077 }
9078 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9079 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9080 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9081 if (pHash->cbHashed == 0)
9082 pHash->fFallbackMode = K_TRUE;
9083 if (pHash->fFallbackMode)
9084 {
9085 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9086 pHash->fFallbackMode = K_TRUE;
9087 MD5Init(&pHash->Md5Ctx);
9088 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9089 pHash->cbHashed = cbData;
9090 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9091 hHash, pbData, cbData, dwFlags));
9092 return TRUE;
9093 }
9094 pHash->fGoneBad = K_TRUE;
9095 SetLastError(ERROR_INVALID_PARAMETER);
9096 fRc = FALSE;
9097 }
9098 else
9099 {
9100 /* fallback. */
9101 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9102 pHash->cbHashed += cbData;
9103 fRc = TRUE;
9104 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9105 hHash, pbData, cbData, dwFlags));
9106 }
9107 }
9108 /*
9109 * Bad handle state.
9110 */
9111 else
9112 {
9113 if (pHash->uMagic != KWHASHMD5_MAGIC)
9114 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9115 else
9116 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9117 SetLastError(NTE_BAD_HASH);
9118 fRc = FALSE;
9119 }
9120 }
9121 else
9122 {
9123
9124 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9125 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9126 }
9127 return fRc;
9128}
9129
9130
9131/** AdvApi32 - CryptGetHashParam */
9132static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9133 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9134{
9135 BOOL fRc;
9136 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9137 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9138 while (pHash && (KUPTR)pHash != hHash)
9139 pHash = pHash->pNext;
9140 if (pHash)
9141 {
9142 if (pHash->uMagic == KWHASHMD5_MAGIC)
9143 {
9144 if (dwFlags == 0)
9145 {
9146 DWORD cbRet;
9147 void *pvRet;
9148 union
9149 {
9150 DWORD dw;
9151 } uBuf;
9152
9153 switch (dwParam)
9154 {
9155 case HP_HASHVAL:
9156 {
9157 /* Check the hash progress. */
9158 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9159 if (pCachedFile)
9160 {
9161 if ( pCachedFile->cbCached == pHash->cbHashed
9162 && !pHash->fGoneBad)
9163 {
9164 if (pCachedFile->fValidMd5)
9165 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9166 else
9167 {
9168 MD5Init(&pHash->Md5Ctx);
9169 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9170 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9171 pCachedFile->fValidMd5 = K_TRUE;
9172 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9173 }
9174 pvRet = pCachedFile->abMd5Digest;
9175 }
9176 else
9177 {
9178 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9179 from what I can tell, so just deal with it. */
9180 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9181 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9182 pHash, pCachedFile, pCachedFile->szPath));
9183 pHash->fFallbackMode = K_TRUE;
9184 pHash->pCachedFile = NULL;
9185 MD5Init(&pHash->Md5Ctx);
9186 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9187 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9188 pvRet = pHash->abDigest;
9189 }
9190 pHash->fFinal = K_TRUE;
9191 cbRet = 16;
9192 break;
9193 }
9194 else if (pHash->fFallbackMode)
9195 {
9196 if (!pHash->fFinal)
9197 {
9198 pHash->fFinal = K_TRUE;
9199 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9200 }
9201 pvRet = pHash->abDigest;
9202 cbRet = 16;
9203 break;
9204 }
9205 else
9206 {
9207 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9208 SetLastError(ERROR_INVALID_SERVER_STATE);
9209 }
9210 return FALSE;
9211 }
9212
9213 case HP_HASHSIZE:
9214 uBuf.dw = 16;
9215 pvRet = &uBuf;
9216 cbRet = sizeof(DWORD);
9217 break;
9218
9219 case HP_ALGID:
9220 uBuf.dw = CALG_MD5;
9221 pvRet = &uBuf;
9222 cbRet = sizeof(DWORD);
9223 break;
9224
9225 default:
9226 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9227 SetLastError(NTE_BAD_TYPE);
9228 return FALSE;
9229 }
9230
9231 /*
9232 * Copy out cbRet from pvRet.
9233 */
9234 if (pbData)
9235 {
9236 if (*pcbData >= cbRet)
9237 {
9238 *pcbData = cbRet;
9239 kHlpMemCopy(pbData, pvRet, cbRet);
9240 if (cbRet == 4)
9241 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9242 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9243 else if (cbRet == 16)
9244 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",
9245 dwParam, pHash, pHash->pCachedFile, cbRet,
9246 pbData[0], pbData[1], pbData[2], pbData[3],
9247 pbData[4], pbData[5], pbData[6], pbData[7],
9248 pbData[8], pbData[9], pbData[10], pbData[11],
9249 pbData[12], pbData[13], pbData[14], pbData[15]));
9250 else
9251 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9252 dwParam, pHash, pHash->pCachedFile, cbRet));
9253 return TRUE;
9254 }
9255
9256 kHlpMemCopy(pbData, pvRet, *pcbData);
9257 }
9258 SetLastError(ERROR_MORE_DATA);
9259 *pcbData = cbRet;
9260 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9261 }
9262 else
9263 {
9264 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9265 SetLastError(NTE_BAD_FLAGS);
9266 }
9267 }
9268 else
9269 {
9270 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9271 SetLastError(NTE_BAD_HASH);
9272 }
9273 fRc = FALSE;
9274 }
9275 /*
9276 * Regular handle.
9277 */
9278 else
9279 {
9280 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9281 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9282 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9283 }
9284
9285 return fRc;
9286}
9287
9288
9289/** AdvApi32 - CryptDestroyHash */
9290static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9291{
9292 BOOL fRc;
9293 PKWHASHMD5 pPrev = NULL;
9294 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9295 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9296 while (pHash && (KUPTR)pHash != hHash)
9297 {
9298 pPrev = pHash;
9299 pHash = pHash->pNext;
9300 }
9301 if (pHash)
9302 {
9303 if (pHash->uMagic == KWHASHMD5_MAGIC)
9304 {
9305 pHash->uMagic = 0;
9306 if (!pPrev)
9307 g_Sandbox.pHashHead = pHash->pNext;
9308 else
9309 pPrev->pNext = pHash->pNext;
9310 kHlpFree(pHash);
9311 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9312 fRc = TRUE;
9313 }
9314 else
9315 {
9316 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
9317 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
9318 SetLastError(ERROR_INVALID_HANDLE);
9319 fRc = FALSE;
9320 }
9321 }
9322 /*
9323 * Regular handle.
9324 */
9325 else
9326 {
9327 fRc = CryptDestroyHash(hHash);
9328 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
9329 }
9330 return fRc;
9331}
9332
9333#endif /* WITH_HASH_MD5_CACHE */
9334
9335
9336/*
9337 *
9338 * Reuse crypt context.
9339 * Reuse crypt context.
9340 * Reuse crypt context.
9341 *
9342 *
9343 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
9344 *
9345 */
9346
9347#ifdef WITH_CRYPT_CTX_REUSE
9348
9349/** AdvApi32 - CryptAcquireContextW. */
9350static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
9351 DWORD dwProvType, DWORD dwFlags)
9352{
9353 BOOL fRet;
9354
9355 /*
9356 * Lookup reusable context based on the input.
9357 */
9358 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
9359 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
9360 KU32 iCtx = g_Sandbox.cCryptCtxs;
9361 while (iCtx-- > 0)
9362 {
9363 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
9364 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
9365 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
9366 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
9367 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
9368 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
9369 {
9370 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
9371 {
9372 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
9373 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
9374 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9375 return TRUE;
9376 }
9377 }
9378 }
9379
9380 /*
9381 * Create it and enter it into the reused array if possible.
9382 */
9383 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
9384 if (fRet)
9385 {
9386 iCtx = g_Sandbox.cCryptCtxs;
9387 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
9388 {
9389 /* Try duplicate the input strings. */
9390 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
9391 (cwcContainer + 1) * sizeof(wchar_t));
9392 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
9393 {
9394 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
9395 (cwcProvider + 1) * sizeof(wchar_t));
9396 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
9397 {
9398 /* Add a couple of references just to be on the safe side and all that. */
9399 HCRYPTPROV hProv = *phProv;
9400 if (CryptContextAddRef(hProv, NULL, 0))
9401 {
9402 if (CryptContextAddRef(hProv, NULL, 0))
9403 {
9404 /* Okay, finish the entry and return success */
9405 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
9406 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
9407 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
9408 g_Sandbox.cCryptCtxs = iCtx + 1;
9409
9410 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
9411 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9412 return TRUE;
9413 }
9414 CryptReleaseContext(hProv, 0);
9415 }
9416 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
9417
9418 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
9419 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
9420 }
9421 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
9422 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
9423 }
9424 }
9425 else
9426 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
9427 }
9428
9429 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
9430 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9431 return fRet;
9432}
9433
9434
9435/** AdvApi32 - CryptReleaseContext */
9436static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
9437{
9438 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
9439 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
9440 return fRet;
9441}
9442
9443
9444/** AdvApi32 - CryptContextAddRef */
9445static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9446{
9447 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9448 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9449 return fRet;
9450}
9451
9452#endif /* WITH_CRYPT_CTX_REUSE */
9453
9454/*
9455 *
9456 * Structured exception handling.
9457 * Structured exception handling.
9458 * Structured exception handling.
9459 *
9460 */
9461#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9462
9463# define EH_NONCONTINUABLE KU32_C(0x00000001)
9464# define EH_UNWINDING KU32_C(0x00000002)
9465# define EH_EXIT_UNWIND KU32_C(0x00000004)
9466# define EH_STACK_INVALID KU32_C(0x00000008)
9467# define EH_NESTED_CALL KU32_C(0x00000010)
9468
9469typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9470 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9471typedef struct _EXCEPTION_REGISTRATION_RECORD
9472{
9473 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9474 PFNXCPTHANDLER pfnXcptHandler;
9475};
9476
9477
9478/**
9479 * Calls @a pfnHandler.
9480 */
9481static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9482 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9483 PFNXCPTHANDLER pfnHandler)
9484{
9485# if 1
9486 /* This is a more robust version that isn't subject to calling
9487 convension cleanup disputes and such. */
9488 KU32 uSavedEdi;
9489 KU32 uSavedEsi;
9490 KU32 uSavedEbx;
9491 KU32 rcHandler;
9492
9493 __asm
9494 {
9495 mov [uSavedEdi], edi
9496 mov [uSavedEsi], esi
9497 mov [uSavedEbx], ebx
9498 mov esi, esp
9499 mov edi, esp
9500 mov edi, [pXcptRec]
9501 mov edx, [pRegRec]
9502 mov eax, [pXcptCtx]
9503 mov ebx, [ppRegRec]
9504 mov ecx, [pfnHandler]
9505 sub esp, 16
9506 and esp, 0fffffff0h
9507 mov [esp ], edi
9508 mov [esp + 4], edx
9509 mov [esp + 8], eax
9510 mov [esp + 12], ebx
9511 mov edi, esi
9512 call ecx
9513 mov esp, esi
9514 cmp esp, edi
9515 je stack_ok
9516 int 3
9517 stack_ok:
9518 mov edi, [uSavedEdi]
9519 mov esi, [uSavedEsi]
9520 mov ebx, [uSavedEbx]
9521 mov [rcHandler], eax
9522 }
9523 return rcHandler;
9524# else
9525 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9526# endif
9527}
9528
9529
9530/**
9531 * Vectored exception handler that emulates x86 chained exception handler.
9532 *
9533 * This is necessary because the RtlIsValidHandler check fails for self loaded
9534 * code and prevents cl.exe from working. (On AMD64 we can register function
9535 * tables, but on X86 cooking your own handling seems to be the only viabke
9536 * alternative.)
9537 *
9538 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9539 * @param pXcptPtrs The exception details.
9540 */
9541static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9542{
9543 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9544 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9545 if (g_Sandbox.fRunning)
9546 {
9547 HANDLE const hCurProc = GetCurrentProcess();
9548 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9549 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9550 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9551 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9552 {
9553 /* Read the exception record in a safe manner. */
9554 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9555 DWORD cbActuallyRead = 0;
9556 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9557 && cbActuallyRead == sizeof(RegRec))
9558 {
9559 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9560 KU32 rcHandler;
9561 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9562 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9563 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9564 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9565 if (rcHandler == ExceptionContinueExecution)
9566 {
9567 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9568 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9569 return EXCEPTION_CONTINUE_EXECUTION;
9570 }
9571
9572 if (rcHandler == ExceptionContinueSearch)
9573 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9574 else if (rcHandler == ExceptionNestedException)
9575 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9576 else
9577 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9578 }
9579 else
9580 {
9581 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9582 break;
9583 }
9584
9585 /*
9586 * Next.
9587 */
9588 pRegRec = RegRec.pPrevRegRec;
9589 }
9590 }
9591 return EXCEPTION_CONTINUE_SEARCH;
9592}
9593
9594
9595/** NtDll,Kernel32 - RtlUnwind */
9596static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9597 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9598{
9599 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9600 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9601 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9602 if (g_Sandbox.fRunning)
9603 {
9604 HANDLE const hCurProc = GetCurrentProcess();
9605 PCONTEXT pXcptCtx = NULL;
9606 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9607
9608 /*
9609 * Update / create an exception record.
9610 */
9611 if (pXcptRec)
9612 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9613 else
9614 {
9615 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9616 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9617 pXcptRec->ExceptionCode = STATUS_UNWIND;
9618 pXcptRec->ExceptionFlags = EH_UNWINDING;
9619 }
9620 if (!pStopXcptRec)
9621 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9622
9623 /*
9624 * Walk the chain till we find pStopXctpRec.
9625 */
9626 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9627 && pRegRec != NULL
9628 && pRegRec != pStopXcptRec)
9629 {
9630 /* Read the exception record in a safe manner. */
9631 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9632 DWORD cbActuallyRead = 0;
9633 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9634 && cbActuallyRead == sizeof(RegRec))
9635 {
9636 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9637 KU32 rcHandler;
9638 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9639 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9640 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9641 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9642
9643 if (rcHandler == ExceptionContinueSearch)
9644 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9645 else if (rcHandler == ExceptionCollidedUnwind)
9646 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9647 else
9648 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9649 }
9650 else
9651 {
9652 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9653 break;
9654 }
9655
9656 /*
9657 * Pop next.
9658 */
9659 pTib->ExceptionList = RegRec.pPrevRegRec;
9660 pRegRec = RegRec.pPrevRegRec;
9661 }
9662 return;
9663 }
9664
9665 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9666}
9667
9668#endif /* WINDOWS + X86 */
9669
9670
9671/*
9672 *
9673 * Misc function only intercepted while debugging.
9674 * Misc function only intercepted while debugging.
9675 * Misc function only intercepted while debugging.
9676 *
9677 */
9678
9679#ifndef NDEBUG
9680
9681/** CRT - memcpy */
9682static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9683{
9684 KU8 const *pbSrc = (KU8 const *)pvSrc;
9685 KU8 *pbDst = (KU8 *)pvDst;
9686 KSIZE cbLeft = cb;
9687 while (cbLeft-- > 0)
9688 *pbDst++ = *pbSrc++;
9689 return pvDst;
9690}
9691
9692
9693/** CRT - memset */
9694static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9695{
9696 KU8 *pbDst = (KU8 *)pvDst;
9697 KSIZE cbLeft = cb;
9698 while (cbLeft-- > 0)
9699 *pbDst++ = bFiller;
9700 return pvDst;
9701}
9702
9703#endif /* NDEBUG */
9704
9705
9706
9707/**
9708 * Functions that needs replacing for sandboxed execution.
9709 */
9710KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9711{
9712 /*
9713 * Kernel32.dll and friends.
9714 */
9715 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9716 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9717
9718 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9719 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9720 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9721 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9722 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9723 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9724 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9725 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9726 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9727 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9728 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9729
9730 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9731 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9732 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9733 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9734
9735 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9736
9737 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9738 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9739 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9740 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9741 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9742 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9743 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9744 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9745 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9746 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9747 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9748
9749 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9750 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9751 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9752 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9753#ifdef WITH_TEMP_MEMORY_FILES
9754 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9755 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9756 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9757 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9758 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9759 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9760 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9761 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9762 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9763 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9764#endif
9765 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9766 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9767 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9768 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9769 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9770 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9771 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9772#ifdef WITH_TEMP_MEMORY_FILES
9773 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9774#endif
9775
9776 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9777 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9778
9779 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
9780 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
9781
9782 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
9783 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
9784
9785 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9786 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
9787 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9788 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
9789
9790 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9791
9792#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9793 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
9794#endif
9795
9796#ifdef WITH_HASH_MD5_CACHE
9797 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9798 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
9799 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9800 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9801#endif
9802
9803#ifdef WITH_CRYPT_CTX_REUSE
9804 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
9805 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
9806 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
9807#endif
9808
9809 /*
9810 * MS Visual C++ CRTs.
9811 */
9812 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
9813 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
9814 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
9815 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
9816 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
9817 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
9818
9819 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9820 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9821 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
9822
9823 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
9824 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
9825 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
9826
9827 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
9828 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
9829 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
9830 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
9831 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
9832 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
9833 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
9834 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
9835 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
9836 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
9837 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
9838 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
9839 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
9840 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
9841 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
9842 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
9843 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
9844 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
9845 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
9846 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
9847
9848 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
9849 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
9850 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
9851 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
9852 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
9853 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
9854 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
9855 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
9856 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
9857 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
9858 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
9859 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
9860 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
9861 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
9862
9863#ifndef NDEBUG
9864 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
9865 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
9866#endif
9867};
9868/** Number of entries in g_aReplacements. */
9869KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
9870
9871
9872/**
9873 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
9874 * execution.
9875 */
9876KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
9877{
9878 /*
9879 * Kernel32.dll and friends.
9880 */
9881 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9882 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9883
9884#if 0
9885 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9886#endif
9887
9888 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9889 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9890 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9891 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9892#ifdef WITH_TEMP_MEMORY_FILES
9893 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9894 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9895 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9896 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9897 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9898 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9899 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9900 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9901 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9902 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9903#endif
9904 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9905 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9906 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9907 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9908 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9909 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9910 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9911#ifdef WITH_TEMP_MEMORY_FILES
9912 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9913#endif
9914 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9915 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
9916
9917 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9918 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9919
9920#ifdef WITH_HASH_MD5_CACHE
9921 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9922 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
9923 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9924 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9925#endif
9926
9927 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9928
9929#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9930 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
9931#endif
9932
9933 /*
9934 * MS Visual C++ CRTs.
9935 */
9936 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
9937 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
9938 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
9939 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
9940 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
9941 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
9942 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
9943
9944#if 0 /* used by mspdbXXX.dll */
9945 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
9946 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
9947#endif
9948};
9949/** Number of entries in g_aSandboxNativeReplacements. */
9950KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
9951
9952
9953/**
9954 * Functions that needs replacing when queried by GetProcAddress.
9955 */
9956KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
9957{
9958 /*
9959 * Kernel32.dll and friends.
9960 */
9961 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9962 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
9963 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9964 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
9965};
9966/** Number of entries in g_aSandboxGetProcReplacements. */
9967KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
9968
9969
9970/**
9971 * Control handler.
9972 *
9973 * @returns TRUE if handled, FALSE if not.
9974 * @param dwCtrlType The signal.
9975 */
9976static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
9977{
9978 DWORD cbIgn;
9979 int volatile rc; /* volatile for debugging */
9980 int volatile rcPrev;
9981 const char *pszMsg;
9982 switch (dwCtrlType)
9983 {
9984 case CTRL_C_EVENT:
9985 rc = 9;
9986 pszMsg = "kWorker: Ctrl-C\r\n";
9987 break;
9988
9989 case CTRL_BREAK_EVENT:
9990 rc = 10;
9991 pszMsg = "kWorker: Ctrl-Break\r\n";
9992 break;
9993
9994 case CTRL_CLOSE_EVENT:
9995 rc = 11;
9996 pszMsg = "kWorker: console closed\r\n";
9997 break;
9998
9999 case CTRL_LOGOFF_EVENT:
10000 rc = 11;
10001 pszMsg = "kWorker: logoff event\r\n";
10002 break;
10003
10004 case CTRL_SHUTDOWN_EVENT:
10005 rc = 11;
10006 pszMsg = "kWorker: shutdown event\r\n";
10007 break;
10008
10009 default:
10010 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10011 return TRUE;
10012 }
10013
10014 /*
10015 * Terminate the process after 5 seconds.
10016 * If we get here a second time we just terminate the process ourselves.
10017 *
10018 * Note! We do no try call exit() here as it turned out to deadlock a lot
10019 * flusing file descriptors (stderr back when we first wrote to it).
10020 */
10021 rcPrev = g_rcCtrlC;
10022 g_rcCtrlC = rc;
10023 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10024 if (rcPrev == 0)
10025 {
10026 int i;
10027 for (i = 0; i < 10; i++)
10028 {
10029 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10030 Sleep(500);
10031 }
10032 }
10033 TerminateProcess(GetCurrentProcess(), rc);
10034 return TRUE;
10035}
10036
10037
10038/**
10039 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10040 */
10041static void kwSandboxResetModuleVisited(void)
10042{
10043 PKWMODULE pMod = g_pModuleHead;
10044 while (pMod)
10045 {
10046 pMod->fVisited = K_FALSE;
10047 pMod = pMod->pNextList;
10048 }
10049}
10050
10051
10052/**
10053 * Used by kwSandboxExec to reset the state of the module tree.
10054 *
10055 * This is done recursively.
10056 *
10057 * @param pMod The root of the tree to consider.
10058 */
10059static void kwSandboxResetModuleState(PKWMODULE pMod)
10060{
10061 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10062 if (!pMod->fNative)
10063 {
10064 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10065 if (!pMod->fVisited) /* Avoid loops. */
10066 {
10067 KSIZE iImp;
10068 pMod->fVisited = K_TRUE;
10069 iImp = pMod->u.Manual.cImpMods;
10070 while (iImp-- > 0)
10071 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10072 }
10073 }
10074 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10075 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10076 {
10077 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10078 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10079 {
10080 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10081 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10082 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10083 pMod->pszPath, pszValue ? pszValue : "<null>"));
10084 }
10085 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10086 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10087 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10088 pMod->pszPath, pszValue ? pszValue : "<null>"));
10089 else
10090 {
10091 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10092 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10093 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10094 if (pszValue != NULL)
10095 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10096 else
10097 pMod->pszMsPdbSrvEndpoint = NULL;
10098 pMod->fNeedReInit = K_TRUE;
10099 }
10100 }
10101}
10102
10103static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10104{
10105#if K_ARCH == K_ARCH_X86_32
10106 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10107#elif K_ARCH == K_ARCH_AMD64
10108 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10109#else
10110# error "Port me!"
10111#endif
10112}
10113
10114
10115/**
10116 * Enters the given handle into the handle table.
10117 *
10118 * @returns K_TRUE on success, K_FALSE on failure.
10119 * @param pSandbox The sandbox.
10120 * @param pHandle The handle.
10121 * @param hHandle The handle value to enter it under (for the
10122 * duplicate handle API).
10123 */
10124static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10125{
10126 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10127 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10128
10129 /*
10130 * Grow handle table.
10131 */
10132 if (idxHandle >= pSandbox->cHandles)
10133 {
10134 void *pvNew;
10135 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10136 while (cHandles <= idxHandle)
10137 cHandles *= 2;
10138 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10139 if (!pvNew)
10140 {
10141 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10142 return K_FALSE;
10143 }
10144 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10145 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10146 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10147 pSandbox->cHandles = cHandles;
10148 }
10149
10150 /*
10151 * Check that the entry is unused then insert it.
10152 */
10153 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10154 pSandbox->papHandles[idxHandle] = pHandle;
10155 pSandbox->cActiveHandles++;
10156 return K_TRUE;
10157}
10158
10159
10160/**
10161 * Creates a correctly quoted ANSI command line string from the given argv.
10162 *
10163 * @returns Pointer to the command line.
10164 * @param cArgs Number of arguments.
10165 * @param papszArgs The argument vector.
10166 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10167 * @param pcbCmdLine Where to return the command line length,
10168 * including one terminator.
10169 */
10170static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10171{
10172 KU32 i;
10173 KSIZE cbCmdLine;
10174 char *pszCmdLine;
10175
10176 /* Make a copy of the argument vector that we'll be quoting. */
10177 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10178 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10179
10180 /* Quote the arguments that need it. */
10181 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10182
10183 /* figure out cmd line length. */
10184 cbCmdLine = 0;
10185 for (i = 0; i < cArgs; i++)
10186 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10187 *pcbCmdLine = cbCmdLine;
10188
10189 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10190 if (pszCmdLine)
10191 {
10192 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10193 if (papszQuotedArgs[0] != papszArgs[0])
10194 free(papszQuotedArgs[0]);
10195
10196 for (i = 1; i < cArgs; i++)
10197 {
10198 *psz++ = ' ';
10199 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10200 if (papszQuotedArgs[i] != papszArgs[i])
10201 free(papszQuotedArgs[i]);
10202 }
10203 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10204
10205 *psz++ = '\0';
10206 *psz++ = '\0';
10207 }
10208
10209 return pszCmdLine;
10210}
10211
10212
10213
10214static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10215 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10216 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10217{
10218 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10219 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10220 wchar_t *pwcPool;
10221 KSIZE cbStrings;
10222 KSIZE cwc;
10223 KSIZE cbCmdLine;
10224 KU32 i;
10225
10226 /* Simple stuff. */
10227 pSandbox->rcExitCode = 256;
10228 pSandbox->pTool = pTool;
10229 pSandbox->idMainThread = GetCurrentThreadId();
10230 pSandbox->pgmptr = (char *)pTool->pszPath;
10231 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10232#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10233 if (pSandbox->StdOut.fIsConsole)
10234 pSandbox->StdOut.u.Con.cwcBuf = 0;
10235 else
10236 pSandbox->StdOut.u.Fully.cchBuf = 0;
10237 if (pSandbox->StdErr.fIsConsole)
10238 pSandbox->StdErr.u.Con.cwcBuf = 0;
10239 else
10240 pSandbox->StdErr.u.Fully.cchBuf = 0;
10241 pSandbox->Combined.cwcBuf = 0;
10242 pSandbox->Combined.cFlushes = 0;
10243#endif
10244 pSandbox->fNoPchCaching = fNoPchCaching;
10245 pSandbox->cArgs = cArgs;
10246 pSandbox->papszArgs = (char **)papszArgs;
10247 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10248 if (!pSandbox->pszCmdLine)
10249 return KERR_NO_MEMORY;
10250
10251 /*
10252 * Convert command line and argv to UTF-16.
10253 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10254 */
10255 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10256 if (!pSandbox->papwszArgs)
10257 return KERR_NO_MEMORY;
10258 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10259 for (i = 0; i < cArgs; i++)
10260 {
10261 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10262 pSandbox->papwszArgs[i] = pwcPool;
10263 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
10264 pwcPool++;
10265 }
10266 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
10267 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
10268
10269 /*
10270 * Convert the commandline string to UTF-16, same pessimistic approach as above.
10271 */
10272 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
10273 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
10274 if (!pSandbox->pwszCmdLine)
10275 return KERR_NO_MEMORY;
10276 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
10277
10278 pSandbox->SavedCommandLine = pProcParams->CommandLine;
10279 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
10280 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
10281
10282 /*
10283 * Setup the environment.
10284 */
10285 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
10286 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
10287 {
10288 KU32 iDst = 0;
10289 for (i = 0; i < cEnvVars; i++)
10290 {
10291 const char *pszVar = papszEnvVars[i];
10292 KSIZE cchVar = kHlpStrLen(pszVar);
10293 const char *pszEqual;
10294 if ( cchVar > 0
10295 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
10296 {
10297 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
10298 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
10299 if (pszCopy && pwszCopy)
10300 {
10301 pSandbox->papszEnvVars[iDst] = pszCopy;
10302 pSandbox->environ[iDst] = pszCopy;
10303 pSandbox->papwszEnvVars[iDst] = pwszCopy;
10304 pSandbox->wenviron[iDst] = pwszCopy;
10305
10306 /* When we see the path, we must tell the system or native exec and module loading won't work . */
10307 if ( (pszEqual - pszVar) == 4
10308 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
10309 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
10310 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
10311 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
10312 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
10313 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
10314
10315 iDst++;
10316 }
10317 else
10318 {
10319 kHlpFree(pszCopy);
10320 kHlpFree(pwszCopy);
10321 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
10322 }
10323 }
10324 else
10325 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
10326 }
10327 pSandbox->papszEnvVars[iDst] = NULL;
10328 pSandbox->environ[iDst] = NULL;
10329 pSandbox->papwszEnvVars[iDst] = NULL;
10330 pSandbox->wenviron[iDst] = NULL;
10331 }
10332 else
10333 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
10334
10335 /*
10336 * Invalidate the volatile parts of cache (kBuild output directory,
10337 * temporary directory, whatever).
10338 */
10339 kFsCacheInvalidateCustomBoth(g_pFsCache);
10340
10341#ifdef WITH_HISTORY
10342 /*
10343 * Record command line in debug history.
10344 */
10345 kHlpFree(g_apszHistory[g_iHistoryNext]);
10346 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
10347 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
10348#endif
10349
10350 return 0;
10351}
10352
10353
10354/**
10355 * Does sandbox cleanup between jobs.
10356 *
10357 * We postpone whatever isn't externally visible (i.e. files) and doesn't
10358 * influence the result, so that kmk can get on with things ASAP.
10359 *
10360 * @param pSandbox The sandbox.
10361 */
10362static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
10363{
10364 PROCESS_MEMORY_COUNTERS MemInfo;
10365 PKWVIRTALLOC pTracker;
10366 PKWHEAP pHeap;
10367 PKWLOCALSTORAGE pLocalStorage;
10368#ifdef WITH_HASH_MD5_CACHE
10369 PKWHASHMD5 pHash;
10370#endif
10371#ifdef WITH_TEMP_MEMORY_FILES
10372 PKWFSTEMPFILE pTempFile;
10373#endif
10374 PKWEXITCALLACK pExitCallback;
10375
10376 /*
10377 * First stuff that may cause code to run.
10378 */
10379
10380 /* Do exit callback first. */
10381 pExitCallback = g_Sandbox.pExitCallbackHead;
10382 g_Sandbox.pExitCallbackHead = NULL;
10383 while (pExitCallback)
10384 {
10385 PKWEXITCALLACK pNext = pExitCallback->pNext;
10386 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
10387 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
10388 __try
10389 {
10390 pExitCallback->pfnCallback();
10391 }
10392 __except (EXCEPTION_EXECUTE_HANDLER)
10393 {
10394 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
10395 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
10396 kHlpAssertFailed();
10397 }
10398 kHlpFree(pExitCallback);
10399 pExitCallback = pNext;
10400 }
10401
10402 /* Free left behind FlsAlloc leaks. */
10403 pLocalStorage = g_Sandbox.pFlsAllocHead;
10404 g_Sandbox.pFlsAllocHead = NULL;
10405 while (pLocalStorage)
10406 {
10407 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10408 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
10409 FlsFree(pLocalStorage->idx);
10410 kHlpFree(pLocalStorage);
10411 pLocalStorage = pNext;
10412 }
10413
10414 /* Free left behind TlsAlloc leaks. */
10415 pLocalStorage = g_Sandbox.pTlsAllocHead;
10416 g_Sandbox.pTlsAllocHead = NULL;
10417 while (pLocalStorage)
10418 {
10419 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10420 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
10421 TlsFree(pLocalStorage->idx);
10422 kHlpFree(pLocalStorage);
10423 pLocalStorage = pNext;
10424 }
10425
10426
10427 /*
10428 * Then free resources associated with the sandbox run.
10429 */
10430
10431 /* Open handles, except fixed handles (stdout and stderr). */
10432 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
10433 {
10434 KU32 idxHandle = pSandbox->cHandles;
10435 while (idxHandle-- > 0)
10436 if (pSandbox->papHandles[idxHandle] == NULL)
10437 { /* likely */ }
10438 else
10439 {
10440 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
10441 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
10442 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
10443 {
10444 pSandbox->papHandles[idxHandle] = NULL;
10445 pSandbox->cLeakedHandles++;
10446
10447 switch (pHandle->enmType)
10448 {
10449 case KWHANDLETYPE_FSOBJ_READ_CACHE:
10450 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
10451 idxHandle, pHandle->hHandle, pHandle->cRefs));
10452 break;
10453 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
10454 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
10455 idxHandle, pHandle->hHandle, pHandle->cRefs));
10456 break;
10457 case KWHANDLETYPE_OUTPUT_BUF:
10458 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
10459 idxHandle, pHandle->hHandle, pHandle->cRefs));
10460 break;
10461 case KWHANDLETYPE_TEMP_FILE:
10462 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
10463 idxHandle, pHandle->hHandle, pHandle->cRefs));
10464 pHandle->u.pTempFile->cActiveHandles--;
10465 break;
10466 case KWHANDLETYPE_TEMP_FILE_MAPPING:
10467 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
10468 idxHandle, pHandle->hHandle, pHandle->cRefs));
10469 pHandle->u.pTempFile->cActiveHandles--;
10470 break;
10471 default:
10472 kHlpAssertFailed();
10473 }
10474 if (--pHandle->cRefs == 0)
10475 kHlpFree(pHandle);
10476 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
10477 break;
10478 }
10479 }
10480 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
10481 }
10482
10483 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
10484 g_Sandbox.cMemMappings = 0;
10485
10486#ifdef WITH_TEMP_MEMORY_FILES
10487 /* The temporary files aren't externally visible, they're all in memory. */
10488 pTempFile = pSandbox->pTempFileHead;
10489 pSandbox->pTempFileHead = NULL;
10490 while (pTempFile)
10491 {
10492 PKWFSTEMPFILE pNext = pTempFile->pNext;
10493 KU32 iSeg = pTempFile->cSegs;
10494 while (iSeg-- > 0)
10495 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
10496 kHlpFree(pTempFile->paSegs);
10497 pTempFile->pNext = NULL;
10498 kHlpFree(pTempFile);
10499
10500 pTempFile = pNext;
10501 }
10502#endif
10503
10504 /* Free left behind HeapCreate leaks. */
10505 pHeap = g_Sandbox.pHeapHead;
10506 g_Sandbox.pHeapHead = NULL;
10507 while (pHeap != NULL)
10508 {
10509 PKWHEAP pNext = pHeap->pNext;
10510 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10511 HeapDestroy(pHeap->hHeap);
10512 pHeap = pNext;
10513 }
10514
10515 /* Free left behind VirtualAlloc leaks. */
10516 pTracker = g_Sandbox.pVirtualAllocHead;
10517 g_Sandbox.pVirtualAllocHead = NULL;
10518 while (pTracker)
10519 {
10520 PKWVIRTALLOC pNext = pTracker->pNext;
10521 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10522
10523#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10524 if (pTracker->idxPreAllocated != KU32_MAX)
10525 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10526 else
10527#endif
10528 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10529 kHlpFree(pTracker);
10530 pTracker = pNext;
10531 }
10532
10533 /* Free the environment. */
10534 if (pSandbox->papszEnvVars)
10535 {
10536 KU32 i;
10537 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10538 kHlpFree(pSandbox->papszEnvVars[i]);
10539 pSandbox->environ[0] = NULL;
10540 pSandbox->papszEnvVars[0] = NULL;
10541
10542 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10543 kHlpFree(pSandbox->papwszEnvVars[i]);
10544 pSandbox->wenviron[0] = NULL;
10545 pSandbox->papwszEnvVars[0] = NULL;
10546 }
10547
10548#ifdef WITH_HASH_MD5_CACHE
10549 /*
10550 * Hash handles.
10551 */
10552 pHash = pSandbox->pHashHead;
10553 pSandbox->pHashHead = NULL;
10554 while (pHash)
10555 {
10556 PKWHASHMD5 pNext = pHash->pNext;
10557 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10558 kHlpFree(pHash);
10559 pHash = pNext;
10560 }
10561#endif
10562
10563 /*
10564 * Check the memory usage. If it's getting high, trigger a respawn
10565 * after the next job.
10566 */
10567 MemInfo.WorkingSetSize = 0;
10568 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10569 {
10570 /* The first time thru, we figure out approximately when to restart
10571 based on installed RAM and CPU threads. */
10572 static KU64 s_cbMaxWorkingSet = 0;
10573 if (s_cbMaxWorkingSet != 0)
10574 { /* likely */ }
10575 else
10576 {
10577 SYSTEM_INFO SysInfo;
10578 MEMORYSTATUSEX GlobalMemInfo;
10579 const char *pszValue;
10580
10581 /* Calculate a reasonable estimate. */
10582 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10583 GetNativeSystemInfo(&SysInfo);
10584
10585 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10586 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10587 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10588#if K_ARCH_BITS >= 64
10589 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10590#else
10591 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10592#endif
10593 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10594 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10595
10596 /* User limit. */
10597 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10598 if (pszValue != NULL)
10599 {
10600 char *pszNext;
10601 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10602 if (*pszNext == '\0' || *pszNext == 'M')
10603 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10604 else if (*pszNext == 'K')
10605 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10606 else if (*pszNext == 'G')
10607 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10608 else
10609 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10610 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10611 }
10612
10613 /* Clamp it a little. */
10614 if (s_cbMaxWorkingSet < 168*1024*1024)
10615 s_cbMaxWorkingSet = 168*1024*1024;
10616#if K_ARCH_BITS < 64
10617 else
10618 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10619 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10620 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10621 : 1536*1024*1024 /* got 4GB VA */);
10622#endif
10623 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10624 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10625 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10626 }
10627
10628 /* Finally the check. */
10629 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10630 {
10631 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10632 g_fRestart = K_TRUE;
10633 }
10634 }
10635
10636 /*
10637 * The CRT has a max of 8192 handles, so we better restart after a while if
10638 * someone is leaking handles or we risk running out of descriptors.
10639 *
10640 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10641 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10642 */
10643 if (pSandbox->cLeakedHandles > 6000)
10644 {
10645 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10646 g_fRestart = K_TRUE;
10647 }
10648}
10649
10650
10651/**
10652 * Does essential cleanups and restoring, anything externally visible.
10653 *
10654 * All cleanups that aren't externally visible are postponed till after we've
10655 * informed kmk of the result, so it can be done in the dead time between jobs.
10656 *
10657 * @param pSandbox The sandbox.
10658 */
10659static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10660{
10661 /*
10662 * Restore the parent command line string.
10663 */
10664 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10665 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10666 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10667 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10668 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10669}
10670
10671
10672static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10673 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10674{
10675 int rcExit = 42;
10676 int rc;
10677
10678 /*
10679 * Initialize the sandbox environment.
10680 */
10681 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10682 if (rc == 0)
10683 {
10684 /*
10685 * Do module initialization.
10686 */
10687 kwSandboxResetModuleVisited();
10688 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10689 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10690 if (rc == 0)
10691 {
10692 /*
10693 * Call the main function.
10694 */
10695#if K_ARCH == K_ARCH_AMD64
10696 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10697#elif K_ARCH == K_ARCH_X86_32
10698 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10699#else
10700# error "Port me!"
10701#endif
10702
10703 /* Save the NT TIB first (should do that here, not in some other function). */
10704 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10705 pSandbox->TibMainThread = *pTib;
10706
10707 /* Make the call in a guarded fashion. */
10708#if K_ARCH == K_ARCH_AMD64
10709 /* AMD64 */
10710 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10711 __try
10712 {
10713 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10714 if (setjmp(pSandbox->JmpBuf) == 0)
10715 {
10716 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10717 pSandbox->fRunning = K_TRUE;
10718 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10719 pSandbox->fRunning = K_FALSE;
10720 }
10721 else
10722 rcExit = pSandbox->rcExitCode;
10723 }
10724#elif K_ARCH == K_ARCH_X86_32
10725 /* x86 (see _tmainCRTStartup) */
10726 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10727 __try
10728 {
10729 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10730 if (setjmp(pSandbox->JmpBuf) == 0)
10731 {
10732 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10733 pSandbox->fRunning = K_TRUE;
10734 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10735 pSandbox->fRunning = K_FALSE;
10736 }
10737 else
10738 rcExit = pSandbox->rcExitCode;
10739 }
10740#endif
10741 __except (EXCEPTION_EXECUTE_HANDLER)
10742 {
10743 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10744#ifdef WITH_HISTORY
10745 {
10746 KU32 cPrinted = 0;
10747 while (cPrinted++ < 5)
10748 {
10749 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10750 if (g_apszHistory[idx])
10751 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10752 }
10753 }
10754#endif
10755 rcExit = 512;
10756 }
10757 pSandbox->fRunning = K_FALSE;
10758
10759 /* Now, restore the NT TIB. */
10760 *pTib = pSandbox->TibMainThread;
10761 }
10762 else
10763 rcExit = 42 + 4;
10764
10765 /*
10766 * Flush and clean up the essential bits only, postpone whatever we
10767 * can till after we've replied to kmk.
10768 */
10769#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10770 kwSandboxConsoleFlushAll(&g_Sandbox);
10771#endif
10772 kwSandboxCleanup(&g_Sandbox);
10773 /** @todo Flush sandboxed native CRTs too. */
10774 }
10775 else
10776 rcExit = 42 + 3;
10777
10778 return rcExit;
10779}
10780
10781
10782/**
10783 * Does the post command part of a job (optional).
10784 *
10785 * @returns The exit code of the job.
10786 * @param cPostCmdArgs Number of post command arguments (includes cmd).
10787 * @param papszPostCmdArgs The post command and its argument.
10788 */
10789static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10790{
10791 const char *pszCmd = papszPostCmdArgs[0];
10792
10793 /* Allow the kmk builtin prefix. */
10794 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
10795 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
10796 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
10797
10798 /* Command switch. */
10799 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
10800 {
10801 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
10802 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
10803 }
10804
10805 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
10806}
10807
10808
10809/**
10810 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
10811 */
10812static unsigned kwGetCurrentProcessorGroup(void)
10813{
10814 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
10815 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
10816 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
10817 if (pfnGetter)
10818 {
10819 GROUP_AFFINITY GroupAffinity;
10820 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
10821 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
10822 return GroupAffinity.Group;
10823 }
10824 return 0;
10825}
10826
10827
10828/**
10829 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
10830 */
10831static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
10832{
10833 KSIZE cchRet = 0;
10834 HANDLE hToken;
10835 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
10836 {
10837 DWORD cbRet;
10838 TOKEN_STATISTICS TokenStats;
10839 memset(&TokenStats, 0, sizeof(TokenStats));
10840 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
10841 cchRet = sprintf(pszValue, "%" KX64_PRI,
10842 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
10843 else
10844 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
10845 CloseHandle(hToken);
10846 }
10847 else
10848 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
10849 return cchRet;
10850}
10851
10852
10853/**
10854 * Look for and expand the special environment variable.
10855 *
10856 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
10857 * couldn't accuratly determine. Currently the following variables are
10858 * implemented:
10859 * - "@@PROCESSOR_GROUP@@" - The processor group number.
10860 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
10861 * - "@@PID@@" - The kWorker process ID.
10862 * - "@@@@" - Escaped "@@".
10863 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
10864 */
10865static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
10866{
10867 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
10868 KU32 i = cEnvVars;
10869 while (i-- > 0)
10870 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
10871 && papszEnvVars[i][cchSpecialEnv] == '=')
10872 {
10873 /* We will expand stuff like @@NAME@@ */
10874 const char *pszValue = papszEnvVars[i];
10875 KSIZE offDst = 0;
10876 char szTmp[1024];
10877 for (;;)
10878 {
10879 const char *pszAt = kHlpStrChr(pszValue, '@');
10880 while (pszAt && pszAt[1] != '@')
10881 pszAt = kHlpStrChr(pszAt + 1, '@');
10882 if (pszAt)
10883 {
10884 KSIZE cchSrc = pszAt - pszValue;
10885 if (offDst + cchSrc < sizeof(szTmp))
10886 {
10887 char szSrc[64];
10888
10889 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
10890 offDst += cchSrc;
10891 pszValue = pszAt + 2;
10892
10893 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
10894 {
10895 pszValue += 15;
10896 if (g_iProcessGroup == -1)
10897 g_iProcessGroup = kwGetCurrentProcessorGroup();
10898 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
10899 }
10900 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
10901 {
10902 pszValue += 19;
10903 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
10904 }
10905 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
10906 {
10907 pszValue += 5;
10908 cchSrc = sprintf(szSrc, "%d", getpid());
10909 }
10910 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
10911 {
10912 pszValue += 2;
10913 szSrc[0] = '@';
10914 szSrc[1] = '@';
10915 szSrc[2] = '\0';
10916 cchSrc = 2;
10917 }
10918 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
10919 {
10920 static unsigned int s_iCounter = 0;
10921 pszValue += 15;
10922 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
10923 }
10924 else
10925 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
10926 pszValue - 2);
10927 if (offDst + cchSrc < sizeof(szTmp))
10928 {
10929 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
10930 offDst += cchSrc;
10931 continue;
10932 }
10933 }
10934 }
10935 else
10936 {
10937 KSIZE cchSrc = kHlpStrLen(pszValue);
10938 if (offDst + cchSrc < sizeof(szTmp))
10939 {
10940 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
10941 offDst += cchSrc;
10942 break;
10943 }
10944 }
10945 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
10946 }
10947 szTmp[offDst] = '\0';
10948
10949 /* Return a copy of it: */
10950 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
10951 if (papszEnvVars[i])
10952 {
10953 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
10954 return 0;
10955 }
10956 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
10957 }
10958
10959 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
10960}
10961
10962
10963/**
10964 * Part 2 of the "JOB" command handler.
10965 *
10966 * @returns The exit code of the job.
10967 * @param pszExecutable The executable to execute.
10968 * @param pszCwd The current working directory of the job.
10969 * @param cArgs The number of arguments.
10970 * @param papszArgs The argument vector.
10971 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10972 * @param cEnvVars The number of environment variables.
10973 * @param papszEnvVars The environment vector.
10974 * @param pszSpecialEnv Name of special environment variable that
10975 * requires selective expansion here.
10976 * @param fNoPchCaching Whether to disable precompiled header file
10977 * caching. Avoid trouble when creating them.
10978 * @param cPostCmdArgs Number of post command arguments (includes cmd).
10979 * @param papszPostCmdArgs The post command and its argument.
10980 */
10981static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
10982 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10983 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
10984 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10985{
10986 int rcExit;
10987 PKWTOOL pTool;
10988 char *pszSpecialEnvFree = NULL;
10989
10990 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
10991 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
10992#ifdef KW_LOG_ENABLED
10993 {
10994 KU32 i;
10995 for (i = 0; i < cArgs; i++)
10996 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
10997 for (i = 0; i < cPostCmdArgs; i++)
10998 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
10999 }
11000#endif
11001 g_cJobs++;
11002
11003 /*
11004 * Expand pszSpecialEnv if present.
11005 */
11006 if (*pszSpecialEnv)
11007 {
11008 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11009 if (!rcExit)
11010 { /* likely */ }
11011 else
11012 return rcExit;
11013 }
11014
11015 /*
11016 * Lookup the tool.
11017 */
11018 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11019 if (pTool)
11020 {
11021 /*
11022 * Change the directory if we're going to execute the job inside
11023 * this process. Then invoke the tool type specific handler.
11024 */
11025 switch (pTool->enmType)
11026 {
11027 case KWTOOLTYPE_SANDBOXED:
11028 case KWTOOLTYPE_WATCOM:
11029 {
11030 /* Change dir. */
11031 KFSLOOKUPERROR enmError;
11032 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11033 if ( pNewCurDir == g_pCurDirObj
11034 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11035 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11036 else if (SetCurrentDirectoryA(pszCwd))
11037 {
11038 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11039 g_pCurDirObj = pNewCurDir;
11040 }
11041 else
11042 {
11043 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11044 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11045 rcExit = 42 + 1;
11046 break;
11047 }
11048
11049 /* Call specific handler. */
11050 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11051 {
11052 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11053 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11054 cEnvVars, papszEnvVars, fNoPchCaching);
11055 }
11056 else
11057 {
11058 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11059 rcExit = 42 + 2;
11060 }
11061 break;
11062 }
11063
11064 case KWTOOLTYPE_EXEC:
11065 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11066 rcExit = 42 + 2;
11067 break;
11068
11069 default:
11070 kHlpAssertFailed();
11071 kwErrPrintf("Internal tool type corruption!!\n");
11072 rcExit = 42 + 2;
11073 g_fRestart = K_TRUE;
11074 break;
11075 }
11076
11077 /*
11078 * Do the post command, if present.
11079 */
11080 if (cPostCmdArgs && rcExit == 0)
11081 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11082 }
11083 else
11084 rcExit = 42 + 1;
11085 if (pszSpecialEnvFree)
11086 {
11087 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11088 kHlpFree(pszSpecialEnvFree);
11089 }
11090 return rcExit;
11091}
11092
11093
11094/**
11095 * Handles a "JOB" command.
11096 *
11097 * @returns The exit code of the job.
11098 * @param pszMsg Points to the "JOB" command part of the message.
11099 * @param cbMsg Number of message bytes at @a pszMsg. There are
11100 * 4 more zero bytes after the message body to
11101 * simplify parsing.
11102 */
11103static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11104{
11105 int rcExit = 42;
11106
11107 /*
11108 * Unpack the message.
11109 */
11110 const char *pszExecutable;
11111 KSIZE cbTmp;
11112
11113 pszMsg += sizeof("JOB");
11114 cbMsg -= sizeof("JOB");
11115
11116 /* Executable name. */
11117 pszExecutable = pszMsg;
11118 cbTmp = kHlpStrLen(pszMsg) + 1;
11119 pszMsg += cbTmp;
11120 if ( cbTmp < cbMsg
11121 && cbTmp > 2)
11122 {
11123 const char *pszCwd;
11124 cbMsg -= cbTmp;
11125
11126 /* Current working directory. */
11127 pszCwd = pszMsg;
11128 cbTmp = kHlpStrLen(pszMsg) + 1;
11129 pszMsg += cbTmp;
11130 if ( cbTmp + sizeof(KU32) < cbMsg
11131 && cbTmp >= 2)
11132 {
11133 KU32 cArgs;
11134 cbMsg -= cbTmp;
11135
11136 /* Argument count. */
11137 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11138 pszMsg += sizeof(cArgs);
11139 cbMsg -= sizeof(cArgs);
11140
11141 if (cArgs > 0 && cArgs < 4096)
11142 {
11143 /* The argument vector. */
11144 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11145 if (papszArgs)
11146 {
11147 KU32 i;
11148 for (i = 0; i < cArgs; i++)
11149 {
11150 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11151 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11152 pszMsg += cbTmp;
11153 if (cbTmp < cbMsg)
11154 cbMsg -= cbTmp;
11155 else
11156 {
11157 cbMsg = 0;
11158 break;
11159 }
11160
11161 }
11162 papszArgs[cArgs] = 0;
11163
11164 /* Environment variable count. */
11165 if (cbMsg > sizeof(KU32))
11166 {
11167 KU32 cEnvVars;
11168 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11169 pszMsg += sizeof(cEnvVars);
11170 cbMsg -= sizeof(cEnvVars);
11171
11172 if (cEnvVars >= 0 && cEnvVars < 4096)
11173 {
11174 /* The argument vector. */
11175 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11176 if (papszEnvVars)
11177 {
11178 for (i = 0; i < cEnvVars; i++)
11179 {
11180 papszEnvVars[i] = pszMsg;
11181 cbTmp = kHlpStrLen(pszMsg) + 1;
11182 pszMsg += cbTmp;
11183 if (cbTmp < cbMsg)
11184 cbMsg -= cbTmp;
11185 else
11186 {
11187 cbMsg = 0;
11188 break;
11189 }
11190 }
11191 papszEnvVars[cEnvVars] = 0;
11192
11193 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11194 if (cbMsg >= sizeof(KU8) * 2)
11195 {
11196 KBOOL fWatcomBrainDamange = *pszMsg++;
11197 KBOOL fNoPchCaching = *pszMsg++;
11198 cbMsg -= 2;
11199
11200 /* Name of special enviornment variable requiring selective expansion. */
11201 if (cbMsg >= 1)
11202 {
11203 const char *pszSpecialEnv = pszMsg;
11204 cbTmp = kHlpStrLen(pszMsg);
11205 pszMsg += cbTmp + 1;
11206 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11207
11208 /* Post command argument count (can be zero). */
11209 if (cbMsg >= sizeof(KU32))
11210 {
11211 KU32 cPostCmdArgs;
11212 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11213 pszMsg += sizeof(cPostCmdArgs);
11214 cbMsg -= sizeof(cPostCmdArgs);
11215
11216 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11217 {
11218 char const *apszPostCmdArgs[32+1];
11219 for (i = 0; i < cPostCmdArgs; i++)
11220 {
11221 apszPostCmdArgs[i] = pszMsg;
11222 cbTmp = kHlpStrLen(pszMsg) + 1;
11223 pszMsg += cbTmp;
11224 if ( cbTmp < cbMsg
11225 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11226 cbMsg -= cbTmp;
11227 else
11228 {
11229 cbMsg = KSIZE_MAX;
11230 break;
11231 }
11232 }
11233 if (cbMsg == 0)
11234 {
11235 apszPostCmdArgs[cPostCmdArgs] = NULL;
11236
11237 /*
11238 * The next step.
11239 */
11240 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11241 cArgs, papszArgs, fWatcomBrainDamange,
11242 cEnvVars, papszEnvVars, pszSpecialEnv,
11243 fNoPchCaching,
11244 cPostCmdArgs, apszPostCmdArgs);
11245 }
11246 else if (cbMsg == KSIZE_MAX)
11247 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11248 else
11249 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11250 }
11251 else
11252 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11253 }
11254 else
11255 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11256 }
11257 else
11258 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
11259 }
11260 else
11261 kwErrPrintf("Detected bogus message unpacking flags!\n");
11262 kHlpFree((void *)papszEnvVars);
11263 }
11264 else
11265 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
11266 }
11267 else
11268 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
11269 }
11270 else
11271 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
11272 kHlpFree((void *)papszArgs);
11273 }
11274 else
11275 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
11276 }
11277 else
11278 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
11279 }
11280 else
11281 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
11282 }
11283 else
11284 kwErrPrintf("Detected bogus message unpacking executable path!\n");
11285 return rcExit;
11286}
11287
11288
11289/**
11290 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
11291 *
11292 * @retval 0 on success.
11293 * @retval -1 on error (fully bitched).
11294 *
11295 * @param hPipe The pipe handle.
11296 * @param pvBuf The buffer to write out out.
11297 * @param cbToWrite The number of bytes to write.
11298 */
11299static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
11300{
11301 KU8 const *pbBuf = (KU8 const *)pvBuf;
11302 KU32 cbLeft = cbToWrite;
11303 while (g_rcCtrlC == 0)
11304 {
11305 DWORD cbActuallyWritten = 0;
11306 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
11307 {
11308 cbLeft -= cbActuallyWritten;
11309 if (!cbLeft)
11310 return 0;
11311 pbBuf += cbActuallyWritten;
11312 }
11313 else
11314 {
11315 DWORD dwErr = GetLastError();
11316 if (cbLeft == cbToWrite)
11317 kwErrPrintf("WriteFile failed: %u\n", dwErr);
11318 else
11319 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
11320 return -1;
11321 }
11322 }
11323 return -1;
11324}
11325
11326
11327/**
11328 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
11329 *
11330 * @retval 0 on success.
11331 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
11332 * @retval -1 on error (fully bitched).
11333 * @param hPipe The pipe handle.
11334 * @param pvBuf The buffer to read into.
11335 * @param cbToRead The number of bytes to read.
11336 * @param fShutdownOkay Whether connection shutdown while reading the
11337 * first byte is okay or not.
11338 */
11339static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
11340{
11341 KU8 *pbBuf = (KU8 *)pvBuf;
11342 KU32 cbLeft = cbToRead;
11343 while (g_rcCtrlC == 0)
11344 {
11345 DWORD cbActuallyRead = 0;
11346 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
11347 {
11348 cbLeft -= cbActuallyRead;
11349 if (!cbLeft)
11350 return 0;
11351 pbBuf += cbActuallyRead;
11352 }
11353 else
11354 {
11355 DWORD dwErr = GetLastError();
11356 if (cbLeft == cbToRead)
11357 {
11358 if ( fMayShutdown
11359 && dwErr == ERROR_BROKEN_PIPE)
11360 return 1;
11361 kwErrPrintf("ReadFile failed: %u\n", dwErr);
11362 }
11363 else
11364 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
11365 return -1;
11366 }
11367 }
11368 return -1;
11369}
11370
11371
11372/**
11373 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
11374 *
11375 * @returns pszBuf
11376 * @param pszBuf The buffer (sufficiently large).
11377 * @param uValue The value.
11378 */
11379static const char *kwFmtU64(char *pszBuf, KU64 uValue)
11380{
11381 char szTmp[64];
11382 char *psz = &szTmp[63];
11383 int cch = 4;
11384
11385 *psz-- = '\0';
11386 do
11387 {
11388 if (--cch == 0)
11389 {
11390 *psz-- = ' ';
11391 cch = 3;
11392 }
11393 *psz-- = (uValue % 10) + '0';
11394 uValue /= 10;
11395 } while (uValue != 0);
11396
11397 return strcpy(pszBuf, psz + 1);
11398}
11399
11400
11401/**
11402 * Prints statistics.
11403 */
11404static void kwPrintStats(void)
11405{
11406 PROCESS_MEMORY_COUNTERS_EX MemInfo;
11407 MEMORYSTATUSEX MemStatus;
11408 IO_COUNTERS IoCounters;
11409 DWORD cHandles;
11410 KSIZE cMisses;
11411 char szBuf[16*1024];
11412 int off = 0;
11413 char szPrf[24];
11414 char sz1[64];
11415 char sz2[64];
11416 char sz3[64];
11417 char sz4[64];
11418
11419 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
11420
11421 szBuf[off++] = '\n';
11422
11423 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
11424 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
11425 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
11426 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
11427 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
11428
11429 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
11430 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
11431
11432 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
11433 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
11434 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
11435
11436 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
11437 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
11438 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
11439
11440 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
11441 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
11442 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
11443 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
11444 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
11445 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
11446 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
11447
11448 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
11449 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
11450 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
11451 kwFmtU64(sz1, g_pFsCache->cObjects),
11452 kwFmtU64(sz2, g_pFsCache->cbObjects),
11453 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
11454 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
11455 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
11456 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
11457 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
11458 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
11459#ifdef KFSCACHE_CFG_UTF16
11460 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
11461 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
11462 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
11463 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
11464 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
11465#endif
11466 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
11467 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
11468 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
11469 kwFmtU64(sz3, g_pFsCache->cChildHashed),
11470 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
11471
11472 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
11473 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
11474 kwFmtU64(sz1, g_pFsCache->cLookups),
11475 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
11476 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11477 kwFmtU64(sz3, g_pFsCache->cWalkHits),
11478 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11479 kwFmtU64(sz4, cMisses),
11480 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
11481
11482 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
11483 kwFmtU64(sz1, g_pFsCache->cChildSearches),
11484 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
11485 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
11486 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
11487 kwFmtU64(sz1, g_pFsCache->cNameChanges),
11488 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
11489 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
11490
11491
11492 /*
11493 * Process & Memory details.
11494 */
11495 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
11496 cHandles = 0;
11497 MemInfo.cb = sizeof(MemInfo);
11498 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
11499 memset(&MemInfo, 0, sizeof(MemInfo));
11500 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
11501 kwFmtU64(sz1, cHandles),
11502 kwFmtU64(sz2, MemInfo.PageFaultCount),
11503 kwFmtU64(sz3, MemInfo.PagefileUsage),
11504 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
11505 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
11506 kwFmtU64(sz1, MemInfo.WorkingSetSize),
11507 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
11508 kwFmtU64(sz3, MemInfo.PrivateUsage));
11509 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
11510 szPrf,
11511 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
11512 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
11513 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
11514 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
11515
11516 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
11517 memset(&IoCounters, 0, sizeof(IoCounters));
11518 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
11519 kwFmtU64(sz1, IoCounters.ReadTransferCount),
11520 kwFmtU64(sz2, IoCounters.ReadOperationCount));
11521 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
11522 kwFmtU64(sz1, IoCounters.WriteTransferCount),
11523 kwFmtU64(sz2, IoCounters.WriteOperationCount));
11524 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
11525 kwFmtU64(sz1, IoCounters.OtherTransferCount),
11526 kwFmtU64(sz2, IoCounters.OtherOperationCount));
11527
11528 MemStatus.dwLength = sizeof(MemStatus);
11529 if (!GlobalMemoryStatusEx(&MemStatus))
11530 memset(&MemStatus, 0, sizeof(MemStatus));
11531 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
11532 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
11533 MemStatus.ullAvailVirtual);
11534 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
11535
11536 maybe_con_fwrite(szBuf, off, 1, stdout);
11537 fflush(stdout);
11538}
11539
11540
11541/**
11542 * Handles what comes after --test.
11543 *
11544 * @returns Exit code.
11545 * @param argc Number of arguments after --test.
11546 * @param argv Arguments after --test.
11547 */
11548static int kwTestRun(int argc, char **argv)
11549{
11550 int i;
11551 int j;
11552 int rcExit;
11553 int cRepeats;
11554 char szCwd[MAX_PATH];
11555 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
11556 KU32 cEnvVars;
11557 char **papszEnvVars;
11558 const char *pszSpecialEnv = "";
11559 const char *pszSpecialEnvFull = NULL;
11560 KBOOL fWatcomBrainDamange = K_FALSE;
11561 KBOOL fNoPchCaching = K_FALSE;
11562
11563 /*
11564 * Parse arguments.
11565 */
11566 /* Repeat count. */
11567 i = 0;
11568 if (i >= argc)
11569 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
11570 if (strcmp(argv[i], "--") != 0)
11571 {
11572 cRepeats = atoi(argv[i]);
11573 if (cRepeats <= 0)
11574 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
11575 i++;
11576
11577 /* Optional directory change. */
11578 if ( i < argc
11579 && ( strcmp(argv[i], "--chdir") == 0
11580 || strcmp(argv[i], "-C") == 0 ) )
11581 {
11582 i++;
11583 if (i >= argc)
11584 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
11585 pszCwd = argv[i++];
11586 }
11587
11588 /* Optional watcom flag directory change. */
11589 if ( i < argc
11590 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
11591 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
11592 {
11593 fWatcomBrainDamange = K_TRUE;
11594 i++;
11595 }
11596
11597 /* Optional watcom flag directory change. */
11598 if ( i < argc
11599 && strcmp(argv[i], "--no-pch-caching") == 0)
11600 {
11601 fNoPchCaching = K_TRUE;
11602 i++;
11603 }
11604
11605 /* Optional directory change. */
11606 if ( i < argc
11607 && ( strcmp(argv[i], "--set-special") == 0
11608 || strcmp(argv[i], "-s") == 0 ) )
11609 {
11610 i++;
11611 if (i >= argc)
11612 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
11613 pszSpecialEnvFull = argv[i++];
11614 putenv(pszSpecialEnvFull);
11615 pszSpecialEnv = strdup(pszSpecialEnvFull);
11616 *strchr(pszSpecialEnv, '=') = '\0';
11617 }
11618
11619 /* Trigger breakpoint */
11620 if ( i < argc
11621 && strcmp(argv[i], "--breakpoint") == 0)
11622 {
11623 __debugbreak();
11624 i++;
11625 }
11626
11627 /* Check for '--'. */
11628 if (i >= argc)
11629 return kwErrPrintfRc(2, "Missing '--'\n");
11630 if (strcmp(argv[i], "--") != 0)
11631 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
11632 i++;
11633 }
11634 else
11635 {
11636 cRepeats = 1;
11637 i++;
11638 }
11639 if (i >= argc)
11640 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
11641
11642 /*
11643 * Duplicate the environment.
11644 */
11645 cEnvVars = 0;
11646 while (environ[cEnvVars] != NULL)
11647 cEnvVars++;
11648 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
11649
11650 /*
11651 * Do the job.
11652 */
11653 for (j = 0; j < cRepeats; j++)
11654 {
11655 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
11656 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
11657 argc - i, &argv[i], fWatcomBrainDamange,
11658 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
11659 0, NULL);
11660 KW_LOG(("rcExit=%d\n", rcExit));
11661 kwSandboxCleanupLate(&g_Sandbox);
11662 }
11663
11664 if (getenv("KWORKER_STATS") != NULL)
11665 kwPrintStats();
11666
11667# ifdef WITH_LOG_FILE
11668 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11669 CloseHandle(g_hLogFile);
11670# endif
11671 return rcExit;
11672}
11673
11674
11675/**
11676 * Reads @a pszFile into memory and chops it up into an argument vector.
11677 *
11678 * @returns Pointer to the argument vector on success, NULL on failure.
11679 * @param pszFile The file to load.
11680 * @param pcArgs Where to return the number of arguments.
11681 * @param ppszFileContent Where to return the allocation.
11682 */
11683static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
11684{
11685 char **papszArgs = NULL;
11686 FILE *pFile = fopen(pszFile, "r");
11687 if (pFile)
11688 {
11689 long cbFile;
11690 if ( fseek(pFile, 0, SEEK_END) == 0
11691 && (cbFile = ftell(pFile)) >= 0
11692 && fseek(pFile, 0, SEEK_SET) == 0)
11693 {
11694 char *pszFile = kHlpAllocZ(cbFile + 3);
11695 if (pszFile)
11696 {
11697 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
11698 if ( feof(pFile)
11699 && !ferror(pFile))
11700 {
11701 size_t off = 0;
11702 int cArgs = 0;
11703 int cAllocated = 0;
11704 char ch;
11705
11706 pszFile[cbRead] = '\0';
11707 pszFile[cbRead + 1] = '\0';
11708 pszFile[cbRead + 2] = '\0';
11709
11710 while ((ch = pszFile[off]) != '\0')
11711 {
11712 char *pszArg;
11713 switch (ch)
11714 {
11715 case ' ':
11716 case '\t':
11717 case '\n':
11718 case '\r':
11719 off++;
11720 continue;
11721
11722 case '\\':
11723 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
11724 {
11725 off += 2;
11726 continue;
11727 }
11728 /* fall thru */
11729 default:
11730 pszArg = &pszFile[off];
11731 do
11732 ch = pszFile[++off];
11733 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
11734 pszFile[off++] = '\0';
11735 break;
11736
11737 case '\'':
11738 pszArg = &pszFile[++off];
11739 while ((ch = pszFile[off]) != '\0' && ch != '\'')
11740 off++;
11741 pszFile[off++] = '\0';
11742 break;
11743
11744 case '\"': /** @todo escape sequences */
11745 pszArg = &pszFile[++off];
11746 while ((ch = pszFile[off]) != '\0' && ch != '"')
11747 off++;
11748 pszFile[off++] = '\0';
11749 break;
11750 }
11751 if (cArgs + 1 >= cAllocated)
11752 {
11753 void *pvNew;
11754 cAllocated = cAllocated ? cAllocated * 2 : 16;
11755 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
11756 if (pvNew)
11757 papszArgs = (char **)pvNew;
11758 else
11759 {
11760 kHlpFree(papszArgs);
11761 papszArgs = NULL;
11762 break;
11763 }
11764 }
11765 papszArgs[cArgs] = pszArg;
11766 papszArgs[++cArgs] = NULL;
11767 }
11768 *pcArgs = cArgs;
11769 }
11770 else
11771 kwErrPrintf("Error reading '%s'!\n", pszFile);
11772 }
11773 else
11774 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
11775 }
11776 else
11777 kwErrPrintf("Error seeking '%s'!\n", pszFile);
11778 fclose(pFile);
11779 }
11780 else
11781 kwErrPrintf("Error opening '%s'!\n", pszFile);
11782 return papszArgs;
11783}
11784
11785/**
11786 * Appends a string to an string vector (arguments or enviornment).
11787 *
11788 * @returns 0 on success, non-zero on failure (exit code).
11789 * @param ppapszVector Pointer to the string pointer array.
11790 * @param pcEntries Pointer to the array size.
11791 * @param pszAppend The string to append.
11792 */
11793static int kwFullTestVectorAppend(const char ***ppapszVector, int *pcEntries, char const *pszAppend)
11794{
11795 unsigned cEntries = *pcEntries;
11796 if (!(cEntries & 15))
11797 {
11798 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
11799 if (pvNew)
11800 *ppapszVector = (const char **)pvNew;
11801 else
11802 return kwErrPrintfRc(2, "Out of memory!\n");
11803 }
11804 (*ppapszVector)[cEntries] = pszAppend;
11805 (*ppapszVector)[++cEntries] = NULL;
11806 *pcEntries = cEntries;
11807 return 0;
11808}
11809
11810
11811/**
11812 * Parses arguments for --full-test.
11813 *
11814 * @returns 0 on success, non-zero on failure (exit code).
11815 */
11816static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
11817 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
11818{
11819 PKWONETEST pCur = *ppHead;
11820 int i;
11821 for (i = 0; i < argc; i++)
11822 {
11823 int rc = 0;
11824 const char *pszArg = argv[i];
11825 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
11826 {
11827 if (*piState != 0)
11828 {
11829 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
11830 if (!pCur)
11831 return kwErrPrintfRc(2, "Out of memory!\n");
11832 pCur->fVirgin = K_TRUE;
11833 pCur->pszCwd = pszDefaultCwd;
11834 pCur->cRuns = 1;
11835 pCur->pNext = *ppHead;
11836 *ppHead = pCur;
11837 *piState = 0;
11838 }
11839 else if (!pCur->fVirgin)
11840 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
11841 pCur->pszJobSrc = pszJobSrc;
11842 pCur->iJobSrc = i;
11843 continue; /* (to stay virgin) */
11844 }
11845 else if (*pszArg == '-' && *piState == 0)
11846 {
11847 const char *pszValue = NULL;
11848 char ch = *++pszArg;
11849 pszArg++;
11850 if (ch == '-')
11851 {
11852 ch = '\0';
11853 if (*pszArg == '\0') /* -- */
11854 *piState = 2;
11855 /* Translate or handle long options: */
11856 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
11857 ch = 'E';
11858 else if (kHlpStrComp(pszArg, "special-env") == 0)
11859 ch = 's';
11860 else if (kHlpStrComp(pszArg, "default-env") == 0)
11861 {
11862 unsigned i;
11863 pCur->cEnvVars = 0;
11864 for (i = 0; environ[i] && rc == 0; i++)
11865 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
11866 }
11867 else if (kHlpStrComp(pszArg, "chdir") == 0)
11868 ch = 'C';
11869 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
11870 ch = 'P';
11871 else if (kHlpStrComp(pszArg, "response-file") == 0)
11872 ch = '@';
11873 else if (kHlpStrComp(pszArg, "runs") == 0)
11874 ch = 'R';
11875 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
11876 pCur->fWatcomBrainDamange = K_TRUE;
11877 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
11878 pCur->fNoPchCaching = K_TRUE;
11879 else if (kHlpStrComp(pszArg, "executable") == 0)
11880 ch = 'e';
11881 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
11882 {
11883 __debugbreak();
11884 continue; /* (to stay virgin) */
11885 }
11886 else
11887 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
11888 pszArg = "";
11889 }
11890
11891 while (ch != '\0' && rc == 0)
11892 {
11893 /* Fetch value if needed: */
11894 switch (ch)
11895 {
11896 case '@':
11897 case 'e':
11898 case 'E':
11899 case 's':
11900 case 'C':
11901 case 'R':
11902 if (*pszArg == ':' || *pszArg == '=')
11903 pszValue = &pszArg[1];
11904 else if (*pszArg)
11905 pszValue = pszArg;
11906 else if (i + 1 < argc)
11907 pszValue = argv[++i];
11908 else
11909 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
11910 pszArg = "";
11911 break;
11912 }
11913
11914 /* Handle the option: */
11915 switch (ch)
11916 {
11917 case 'E':
11918 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
11919 break;
11920 case 'C':
11921 pCur->pszCwd = pszValue;
11922 break;
11923 case 's':
11924 pCur->pszSpecialEnv = pszValue;
11925 break;
11926 case 'e':
11927 pCur->pszExecutable = pszValue;
11928 break;
11929 case 'P':
11930 *piState = 1;
11931 if (*pszArg)
11932 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
11933 break;
11934 case 'R':
11935 pCur->cRuns = atoi(pszValue);
11936 if ((int)pCur->cRuns < 0)
11937 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
11938 break;
11939 case '@':
11940 if (cRecursions < 5)
11941 {
11942 char *pszLeaked = NULL;
11943 int cArgs = 0;
11944 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
11945 if (papszArgsLeaked)
11946 {
11947 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
11948 cRecursions + 1, pszValue);
11949 pCur = *ppHead;
11950 }
11951 else
11952 return 2;
11953 }
11954 else
11955 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
11956 break;
11957 }
11958
11959 /* next */
11960 ch = *pszArg++;
11961 }
11962 }
11963 else if (*piState == 2)
11964 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
11965 else if (*piState == 1)
11966 {
11967 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
11968 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
11969 else
11970 *piState = 2;
11971 }
11972 else
11973 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
11974 if (rc)
11975 return rc;
11976 pCur->fVirgin = K_FALSE;
11977 }
11978 return 0;
11979}
11980
11981
11982/**
11983 * Handles what comes after --full-test.
11984 *
11985 * @returns Exit code.
11986 * @param argc Number of arguments after --full-test.
11987 * @param argv Arguments after --full-test.
11988 */
11989static int kwFullTestRun(int argc, char **argv)
11990{
11991 char szDefaultCwd[MAX_PATH];
11992 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
11993 KWONETEST FirstTest;
11994 PKWONETEST pHead = &FirstTest;
11995 PKWONETEST pCur;
11996 int iState = 0;
11997 int rcExit;
11998
11999 /*
12000 * Parse arguments.
12001 */
12002 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12003 FirstTest.pszJobSrc = "command-line";
12004 FirstTest.iJobSrc = 1;
12005 FirstTest.fVirgin = K_TRUE;
12006 FirstTest.pszCwd = pszDefaultCwd;
12007 FirstTest.cRuns = 1;
12008
12009 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12010 if (rcExit)
12011 return rcExit;
12012
12013 /*
12014 * Do the job. LIFO ordering (see kSubmit).
12015 */
12016 for (pCur = pHead; pCur; pCur = pCur->pNext)
12017 {
12018 if (!pCur->pszExecutable && pCur->papszArgs)
12019 pCur->pszExecutable = pCur->papszArgs[0];
12020 if ( pCur->pszExecutable
12021 && pCur->cArgs > 0
12022 && pCur->cEnvVars > 0)
12023 {
12024 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12025 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12026 unsigned iRun;
12027
12028 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12029 {
12030 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12031 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12032 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12033 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12034
12035 KW_LOG(("rcExit=%d\n", rcExit));
12036 kwSandboxCleanupLate(&g_Sandbox);
12037
12038 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12039 }
12040 kHlpFree(papszEnvVarsCopy);
12041 }
12042 else
12043 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12044 pCur->pszExecutable ? "" : " No executable!",
12045 pCur->cArgs < 1 ? " No arguments!" : "",
12046 pCur->cEnvVars < 1 ? " No environment!" : "",
12047 pCur->iJobSrc, pCur->pszJobSrc);
12048 }
12049
12050 if (getenv("KWORKER_STATS") != NULL)
12051 kwPrintStats();
12052
12053# ifdef WITH_LOG_FILE
12054 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12055 CloseHandle(g_hLogFile);
12056# endif
12057 return rcExit;
12058}
12059
12060
12061int main(int argc, char **argv)
12062{
12063 KSIZE cbMsgBuf = 0;
12064 KU8 *pbMsgBuf = NULL;
12065 int i;
12066 HANDLE hPipe = INVALID_HANDLE_VALUE;
12067 const char *pszTmp;
12068 KFSLOOKUPERROR enmIgnored;
12069#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12070 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12071 kwSandboxVecXcptEmulateChained);
12072#endif
12073#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12074 HANDLE hCurProc = GetCurrentProcess();
12075 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12076 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12077 DWORD dwType;
12078#endif
12079
12080
12081#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12082 /*
12083 * Reserve memory for cl.exe
12084 */
12085 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12086 {
12087 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12088 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12089 g_aFixedVirtualAllocs[i].cbFixed,
12090 MEM_RESERVE, PAGE_READWRITE);
12091 if ( !g_aFixedVirtualAllocs[i].pvReserved
12092 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12093 {
12094 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12095 GetLastError());
12096 if (g_aFixedVirtualAllocs[i].pvReserved)
12097 {
12098 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12099 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12100 }
12101 }
12102 }
12103#endif
12104
12105 /*
12106 * Register our Control-C and Control-Break handlers.
12107 */
12108 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12109 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12110
12111 /*
12112 * Create the cache and mark the temporary directory as using the custom revision.
12113 */
12114 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12115 if (!g_pFsCache)
12116 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12117
12118 pszTmp = getenv("TEMP");
12119 if (pszTmp && *pszTmp != '\0')
12120 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12121 pszTmp = getenv("TMP");
12122 if (pszTmp && *pszTmp != '\0')
12123 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12124 pszTmp = getenv("TMPDIR");
12125 if (pszTmp && *pszTmp != '\0')
12126 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12127
12128 /*
12129 * Make g_abDefLdBuf executable.
12130 */
12131 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12132 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12133 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12134
12135#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12136 /*
12137 * Get and duplicate the console handles.
12138 */
12139 /* Standard output. */
12140 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12141 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12142 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12143 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12144 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12145 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12146 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12147 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12148 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12149 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12150 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12151 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12152 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12153 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12154 {
12155 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12156 g_Sandbox.cFixedHandles++;
12157 else
12158 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12159 }
12160 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12161 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12162
12163 /* Standard error. */
12164 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12165 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12166 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12167 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12168 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12169 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12170 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12171 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12172 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12173 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12174 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12175 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12176 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12177 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12178 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12179 {
12180 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12181 g_Sandbox.cFixedHandles++;
12182 else
12183 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12184 }
12185 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12186 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12187
12188 /* Combined console buffer. */
12189 if (g_Sandbox.StdErr.fIsConsole)
12190 {
12191 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12192 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12193 }
12194 else if (g_Sandbox.StdOut.fIsConsole)
12195 {
12196 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12197 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12198 }
12199 else
12200 {
12201 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12202 g_Sandbox.Combined.uCodepage = CP_ACP;
12203 }
12204 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12205#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12206
12207
12208 /*
12209 * Parse arguments.
12210 */
12211 for (i = 1; i < argc; i++)
12212 {
12213 if (strcmp(argv[i], "--pipe") == 0)
12214 {
12215 i++;
12216 if (i < argc)
12217 {
12218 char *pszEnd = NULL;
12219 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
12220 if ( *argv[i]
12221 && pszEnd != NULL
12222 && *pszEnd == '\0'
12223 && u64Value != 0
12224 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
12225 && (uintptr_t)u64Value == u64Value)
12226 hPipe = (HANDLE)(uintptr_t)u64Value;
12227 else
12228 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
12229 }
12230 else
12231 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
12232 }
12233 else if (strcmp(argv[i], "--volatile") == 0)
12234 {
12235 i++;
12236 if (i < argc)
12237 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
12238 else
12239 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
12240 }
12241 else if (strcmp(argv[i], "--test") == 0)
12242 return kwTestRun(argc - i - 1, &argv[i + 1]);
12243 else if (strcmp(argv[i], "--full-test") == 0)
12244 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
12245 else if (strcmp(argv[i], "--priority") == 0)
12246 {
12247 i++;
12248 if (i < argc)
12249 {
12250 char *pszEnd = NULL;
12251 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12252 if ( *argv[i]
12253 && pszEnd != NULL
12254 && *pszEnd == '\0'
12255 && uValue >= 1
12256 && uValue <= 5)
12257 {
12258 DWORD dwClass, dwPriority;
12259 switch (uValue)
12260 {
12261 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
12262 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
12263 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
12264 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12265 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12266 }
12267 SetPriorityClass(GetCurrentProcess(), dwClass);
12268 if (dwPriority != 0xffffffff)
12269 SetThreadPriority(GetCurrentThread(), dwPriority);
12270 }
12271 else
12272 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12273 }
12274 else
12275 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12276 }
12277 else if (strcmp(argv[i], "--group") == 0)
12278 {
12279 i++;
12280 if (i < argc)
12281 {
12282 char *pszEnd = NULL;
12283 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12284 if ( *argv[i]
12285 && pszEnd != NULL
12286 && *pszEnd == '\0'
12287 && uValue == (WORD)uValue)
12288 {
12289 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12290 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12291 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(GetModuleHandleW(L"KERNEL32.DLL"),
12292 "SetThreadGroupAffinity");
12293 if (pfnSetThreadGroupAffinity)
12294 {
12295 GROUP_AFFINITY NewAff = { ~(uintptr_t)0, (WORD)uValue, 0, 0, 0 };
12296 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12297 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12298 kwErrPrintf("Failed to set processor group to %lu: %u\n", uValue, GetLastError());
12299 }
12300 else
12301 kwErrPrintf("Cannot set processor group to %lu because SetThreadGroupAffinity was not found\n", uValue);
12302 }
12303 else
12304 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12305 }
12306 else
12307 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12308 }
12309 else if ( strcmp(argv[i], "--help") == 0
12310 || strcmp(argv[i], "-h") == 0
12311 || strcmp(argv[i], "-?") == 0)
12312 {
12313 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
12314 "usage: kWorker <--help|-h>\n"
12315 "usage: kWorker <--version|-V>\n"
12316 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
12317 "\n"
12318 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
12319 return 0;
12320 }
12321 else if ( strcmp(argv[i], "--version") == 0
12322 || strcmp(argv[i], "-V") == 0)
12323 return kbuild_version(argv[0]);
12324 else
12325 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
12326 }
12327
12328 /*
12329 * If no --pipe argument, then assume its standard input.
12330 * We need to carefully replace the CRT stdin with a handle to "nul".
12331 */
12332 if (hPipe == INVALID_HANDLE_VALUE)
12333 {
12334 hPipe = GetStdHandle(STD_INPUT_HANDLE);
12335 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
12336 {
12337 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
12338 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
12339 {
12340 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
12341 if (fdNul >= 0)
12342 {
12343 if (_dup2(fdNul, 0) >= 0)
12344 {
12345 close(fdNul);
12346 hPipe = hDuplicate;
12347 }
12348 else
12349 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12350 }
12351 else
12352 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12353 }
12354 else
12355 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12356 }
12357 else
12358 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
12359 GetFileType(hPipe), GetLastError());
12360 }
12361 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
12362 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
12363 GetFileType(hPipe), GetLastError());
12364 g_hPipe = hPipe;
12365
12366 /*
12367 * Serve the pipe.
12368 */
12369 for (;;)
12370 {
12371 KU32 cbMsg = 0;
12372 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
12373 if (rc == 0)
12374 {
12375 /* Make sure the message length is within sane bounds. */
12376 if ( cbMsg > 4
12377 && cbMsg <= 256*1024*1024)
12378 {
12379 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
12380 if (cbMsg + 4 <= cbMsgBuf)
12381 { /* likely */ }
12382 else
12383 {
12384 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
12385 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
12386 if (!pbMsgBuf)
12387 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
12388 }
12389
12390 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
12391 *(KU32 *)pbMsgBuf = cbMsg;
12392 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
12393 if (rc == 0)
12394 {
12395 const char *psz;
12396
12397 pbMsgBuf[cbMsg] = '\0';
12398 pbMsgBuf[cbMsg + 1] = '\0';
12399 pbMsgBuf[cbMsg + 2] = '\0';
12400 pbMsgBuf[cbMsg + 3] = '\0';
12401
12402 /* The first string after the header is the command. */
12403 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
12404 if ( strcmp(psz, "JOB") == 0
12405 && g_rcCtrlC == 0)
12406 {
12407 struct
12408 {
12409 KI32 rcExitCode;
12410 KU8 bExiting;
12411 KU8 abZero[3];
12412 } Reply;
12413 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
12414 Reply.bExiting = g_fRestart;
12415 Reply.abZero[0] = 0;
12416 Reply.abZero[1] = 0;
12417 Reply.abZero[2] = 0;
12418 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
12419 if ( rc == 0
12420 && !g_fRestart)
12421 {
12422 kwSandboxCleanupLate(&g_Sandbox);
12423 if (g_rcCtrlC == 0)
12424 continue;
12425 }
12426 }
12427 else
12428 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
12429 }
12430 }
12431 else
12432 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
12433 }
12434
12435 /*
12436 * If we're exitting because we're restarting, we need to delay till
12437 * kmk/kSubmit has read the result. Windows documentation says it
12438 * immediately discards pipe buffers once the pipe is broken by the
12439 * server (us). So, We flush the buffer and queues a 1 byte read
12440 * waiting for kSubmit to close the pipe when it receives the
12441 * bExiting = K_TRUE result.
12442 */
12443 if (g_fRestart)
12444 {
12445 KU8 b;
12446 FlushFileBuffers(hPipe);
12447 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
12448 }
12449
12450 CloseHandle(hPipe);
12451#ifdef WITH_LOG_FILE
12452 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12453 CloseHandle(g_hLogFile);
12454#endif
12455 if (getenv("KWORKER_STATS") != NULL)
12456 kwPrintStats();
12457 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
12458 }
12459}
12460
12461
12462/** @page pg_kWorker kSubmit / kWorker
12463 *
12464 * @section sec_kWorker_Motivation Motivation / Inspiration
12465 *
12466 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
12467 * builds on machines "infested" by Anti Virus protection and disk encryption
12468 * software. Build times jumping from 35-40 min to 77-82 min after the machine
12469 * got "infected".
12470 *
12471 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
12472 * mainly a bunch of tiny assembly and C files being compiler a million times.
12473 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
12474 * own toolchain from within the same process, saving a lot of process creation
12475 * and teardown overhead.
12476 *
12477 *
12478 * @section sec_kWorker_kSubmit About kSubmit
12479 *
12480 * When wanting to execute a job in a kWorker instance, it must be submitted
12481 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
12482 * built into kmk and does not exist as an external program. The reason for
12483 * this is that it keep track of the kWorker instances.
12484 *
12485 * The kSubmit command has the --32-bit and --64-bit options for selecting
12486 * between 32-bit and 64-bit worker instance. We generally assume the user of
12487 * the command knows which bit count the executable has, so kSubmit is spared
12488 * the extra work of finding out.
12489 *
12490 * The kSubmit command shares a environment and current directory manipulation
12491 * with the kRedirect command, but not the file redirection. So long no file
12492 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
12493 * hand for tools like OpenWatcom, NASM and YASM which all require environment
12494 * and/or current directory changes to work.
12495 *
12496 * Unlike the kRedirect command, the kSubmit command can also specify an
12497 * internall post command to be executed after the main command succeeds.
12498 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
12499 * information from Microsoft COFF object files and Watcom OMF object files and
12500 * is scheduled to replace kDepIDB.
12501 *
12502 *
12503 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
12504 *
12505 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
12506 * A job request is written by kSubmit and kWorker read, unpacks it and executes
12507 * it. When the job is completed, kWorker writes a short reply with the exit
12508 * code and an internal status indicating whether it is going to restart.
12509 *
12510 * The kWorker intance will reply to kSubmit before completing all the internal
12511 * cleanup work, so as not to delay the next job execution unnecessarily. This
12512 * includes checking its own memory consumption and checking whether it needs
12513 * restarting. So, a decision to restart unfortunately have to wait till after
12514 * the next job has completed. This is a little bit unfortunate if the next job
12515 * requires a lot of memory and kWorker has already leaked/used a lot.
12516 *
12517 *
12518 * @section sec_kWorker_How_Works How kWorker Works
12519 *
12520 * kWorker will load the executable specified by kSubmit into memory and call
12521 * it's entrypoint in a lightly sandbox'ed environment.
12522 *
12523 *
12524 * @subsection ssec_kWorker_Loaing Image loading
12525 *
12526 * kWorker will manually load all the executable images into memory, fix them
12527 * up, and make a copy of the virgin image so it can be restored using memcpy
12528 * the next time it is used.
12529 *
12530 * Imported functions are monitored and replacements used for a few of them.
12531 * These replacements are serve the following purposes:
12532 * - Provide a different command line.
12533 * - Provide a different environment.
12534 * - Intercept process termination.
12535 * - Intercept thread creation (only linker is allowed to create threads).
12536 * - Intercept file reading for caching (header files, ++) as file system
12537 * access is made even slower by anti-virus software.
12538 * - Intercept crypto hash APIs to cache MD5 digests of header files
12539 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
12540 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
12541 * in memory as writing files grows expensive with encryption and
12542 * anti-virus software active.
12543 * - Intercept some file system queries to use the kFsCache instead of
12544 * going to the kernel and slowly worm thru the AV filter driver.
12545 * - Intercept standard output/error and console writes to aggressivly
12546 * buffer the output. The MS CRT does not buffer either when it goes to
12547 * the console, resulting in terrible performance and mixing up output
12548 * with other compile jobs.
12549 * This also allows us to filter out the annoying source file announcements
12550 * by cl.exe.
12551 * - Intercept VirtualAlloc and VirtualFree to prevent
12552 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
12553 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
12554 * the callbacks run after each job.
12555 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
12556 * executables and tools using custom heaps (like the microsoft linker).
12557 * [exectuable images only]
12558 * - Intercept atexit and _onexit registration to be able run them after
12559 * each job instead of crashing as kWorker exits. This also helps avoid
12560 * some leaks. [executable image only]
12561 *
12562 * DLLs falls into two categories, system DLLs which we always load using the
12563 * native loader, and tool DLLs which can be handled like the executable or
12564 * optionally using the native loader. We maintain a hardcoded white listing of
12565 * tool DLLs we trust to load using the native loader.
12566 *
12567 * Imports of natively loaded DLLs are processed too, but we only replace a
12568 * subset of the functions compared to natively loaded excutable and DLL images.
12569 *
12570 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
12571 * This is to speed up job execution.
12572 *
12573 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
12574 * for each job run, but so far this hasn't been necessary.
12575 *
12576 *
12577 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
12578 *
12579 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
12580 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
12581 * intermediate representation between the first (c1/c1xx.dll) and second pass
12582 * (c2.dll).
12583 *
12584 * kWorker helps the compiler as best as it can. Given a little knowledge about
12585 * stable and volatile file system areas, it can do a lot of caching that a
12586 * normal compiler driver cannot easily do when given a single file.
12587 *
12588 *
12589 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
12590 *
12591 * The preprocessor part will open and process header files exactly as they are
12592 * encountered in the source files. If string.h is included by the main source
12593 * and five other header files, it will be searched for (include path), opened,
12594 * read, MD5-summed, and pre-processed six times. The last five times is just a
12595 * waste of time because of the guards or \#pragma once. A smart compiler would
12596 * make a little extra effort and realize this.
12597 *
12598 * kWorker will cache help the preprocessor by remembering places where the
12599 * header was not found with help of kFsCache, and cache the file in memory when
12600 * found. The first part is taken care of by intercepting GetFileAttributesW,
12601 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
12602 * cached, the file is kept open and the CreateFileW call returns a duplicate of
12603 * that handle. An internal handle table is used by ReadFile and CloseFile to
12604 * keep track of intercepted handles (also used for temporary file, temporary
12605 * file mappings, console buffering, and standard out/err buffering).
12606 *
12607 * PS. The header search optimization also comes in handy when cl.exe goes on
12608 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
12609 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
12610 * optionally compile the three pass DLLs as executables during development
12611 * and problem analysis.
12612 *
12613 *
12614 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
12615 *
12616 * The issues of the temporary files is pretty severe on the Dell machine used
12617 * for benchmarking with full AV and encryption. The synthetic benchmark
12618 * improved by 30% when kWorker implemented measures to keep them entirely in
12619 * memory.
12620 *
12621 * kWorker implement these by recognizing the filename pattern in CreateFileW
12622 * and creating/opening the given file as needed. The handle returned is a
12623 * duplicate of the current process, thus giving us a good chance of catching
12624 * API calls we're not intercepting.
12625 *
12626 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
12627 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
12628 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
12629 * UnmapViewOfFile.
12630 *
12631 *
12632 * @section sec_kWorker_Numbers Some measurements.
12633 *
12634 * - r2881 building src/VBox/Runtime:
12635 * - without: 2m01.016388s = 120.016388 s
12636 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
12637 * - r2884 building vbox/debug (r110512):
12638 * - without: 11m14.446609s = 674.446609 s
12639 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
12640 * - r2896 building vbox/debug (r110577):
12641 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
12642 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
12643 * MS Defender as AV):
12644 * - without: 10m24.990389s = 624.990389s
12645 * - with: 08m04.738184s = 484.738184s
12646 * - delta: 624.99s - 484.74s = 140.25s
12647 * - saved: 140.25/624.99 = 22% faster
12648 *
12649 *
12650 * @subsection subsec_kWorker_Early_Numbers Early Experiments
12651 *
12652 * These are some early experiments doing 1024 compilations of
12653 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
12654 * main function:
12655 *
12656 * Skylake (W10/amd64, only stdandard MS defender):
12657 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
12658 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
12659 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
12660 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
12661 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
12662 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
12663 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
12664 *
12665 * Dell (W7/amd64, infected by mcafee):
12666 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
12667 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
12668 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
12669 *
12670 * The command line:
12671 * @code{.cpp}
12672 "\"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"
12673 * @endcode
12674 */
12675
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