VirtualBox

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

Last change on this file since 2831 was 2831, checked in by bird, 8 years ago

kWorker: early prototype.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.8 KB
Line 
1/* $Id: kWorker.c 2831 2016-08-18 22:10:08Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 */
5
6/*
7 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#include <k/kHlp.h>
31#include <k/kLdr.h>
32
33#include <stdio.h>
34#include <intrin.h>
35#include <setjmp.h>
36
37#include <nt/ntstat.h>
38/* lib/nt_fullpath.c */
39extern void nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull);
40#include <Windows.h>
41
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46typedef enum KWLOCATION
47{
48 KWLOCATION_INVALID = 0,
49 KWLOCATION_EXE_DIR,
50 KWLOCATION_IMPORTER_DIR,
51 KWLOCATION_SYSTEM32
52} KWLOCATION;
53
54typedef enum KWMODSTATE
55{
56 KWMODSTATE_INVALID = 0,
57 KWMODSTATE_NEEDS_BITS,
58 KWMODSTATE_NEEDS_INIT,
59 KWMODSTATE_BEING_INITED,
60 KWMODSTATE_INIT_FAILED,
61 KWMODSTATE_READY,
62} KWMODSTATE;
63
64typedef struct KWMODULE *PKWMODULE;
65typedef struct KWMODULE
66{
67 /** Pointer to the next image. */
68 PKWMODULE pNext;
69 /** The normalized path to the image. */
70 const char *pszPath;
71 /** The hash of the program path. */
72 KU32 uHashPath;
73 /** Number of references. */
74 KU32 cRefs;
75 /** The offset of the filename in pszPath. */
76 KU16 offFilename;
77 /** Set if executable. */
78 KBOOL fExe;
79 /** Set if native module entry. */
80 KBOOL fNative;
81 /** Loader module handle. */
82 PKLDRMOD pLdrMod;
83
84 union
85 {
86 /** Data for a manually loaded image. */
87 struct
88 {
89 /** The of the loaded image bits. */
90 size_t cbImage;
91 /** Where we load the image. */
92 void *pvLoad;
93 /** Virgin copy of the image. */
94 void *pvCopy;
95 /** Ldr pvBits argument. This is NULL till we've successfully resolved
96 * the imports. */
97 void *pvBits;
98 /** The state. */
99 KWMODSTATE enmState;
100 /** Number of imported modules. */
101 size_t cImpMods;
102 /** Import array (variable size). */
103 PKWMODULE apImpMods[1];
104 } Manual;
105 } u;
106} KWMODULE;
107
108typedef enum KWTOOLTYPE
109{
110 KWTOOLTYPE_INVALID = 0,
111 KWTOOLTYPE_SANDBOXED,
112 KWTOOLTYPE_WATCOM,
113 KWTOOLTYPE_EXEC,
114 KWTOOLTYPE_END
115} KWTOOLTYPE;
116
117typedef struct KWTOOL *PKWTOOL;
118typedef struct KWTOOL
119{
120 /** Pointer to the next in the hash collision chain. */
121 PKWTOOL pNext;
122 /** The normalized path to the program. */
123 const char *pszPath;
124 /** The hash of the program path. */
125 KU32 uHashPath;
126 /** The kind of tool. */
127 KWTOOLTYPE enmType;
128
129 union
130 {
131 struct
132 {
133 /** The executable. */
134 PKWMODULE pExe;
135 } Sandboxed;
136 } u;
137} KWTOOL;
138
139
140typedef struct KWSANDBOX *PKWSANDBOX;
141typedef struct KWSANDBOX
142{
143 /** The tool currently running in the sandbox. */
144 PKWTOOL pTool;
145 /** Jump buffer. */
146 jmp_buf JmpBuf;
147 /** The thread ID of the main thread (owner of JmpBuf). */
148 DWORD idMainThread;
149 /** The exit code in case of longjmp. */
150 int rcExitCode;
151
152
153} KWSANDBOX;
154
155/** Replacement function entry. */
156typedef struct KWREPLACEMENTFUNCTION
157{
158 /** The function name. */
159 const char *pszFunction;
160 /** The length of the function name. */
161 KSIZE cchFunction;
162 /** The module name (optional). */
163 const char *pszModule;
164 /** The replacement function. */
165 KUPTR pfnReplacement;
166} KWREPLACEMENTFUNCTION;
167typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
168
169
170/*********************************************************************************************************************************
171* Global Variables *
172*********************************************************************************************************************************/
173/** The currently active sandbox. */
174static PKWSANDBOX g_pSandbox;
175
176/** Module hash table. */
177static PKWMODULE g_apModules[127];
178
179/** Tool hash table. */
180static PKWTOOL g_apTools[63];
181
182/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
183 * cover the default executable link address of 0x400000. */
184#pragma section("DefLdBuf", write, execute, read)
185__declspec(allocate("DefLdBuf"))
186static KU8 g_abDefLdBuf[16*1024*1024];
187
188/* Further down. */
189extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
190extern KU32 const g_cSandboxReplacements;
191
192
193/*********************************************************************************************************************************
194* Internal Functions *
195*********************************************************************************************************************************/
196static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
197static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter, PKWMODULE *ppMod);
198
199
200/**
201 * Normalizes the path so we get a consistent hash.
202 *
203 * @returns status code.
204 * @param pszPath The path.
205 * @param pszNormPath The output buffer.
206 * @param cbNormPath The size of the output buffer.
207 */
208static int kwPathNormalize(const char *pszPath, char *pszNormPath, size_t cbNormPath)
209{
210 char *pchSlash;
211 nt_fullpath(pszPath, pszNormPath, cbNormPath);
212
213 pchSlash = kHlpStrChr(pszNormPath, '/');
214 while (pchSlash)
215 {
216 *pchSlash = '\\';
217 pchSlash = kHlpStrChr(pchSlash + 1, '/');
218 }
219
220 return 0;
221}
222
223
224/**
225 * Hashes a string.
226 *
227 * @returns 32-bit string hash.
228 * @param pszString String to hash.
229 */
230static KU32 kwStrHash(const char *pszString)
231{
232 /* This algorithm was created for sdbm (a public-domain reimplementation of
233 ndbm) database library. it was found to do well in scrambling bits,
234 causing better distribution of the keys and fewer splits. it also happens
235 to be a good general hashing function with good distribution. the actual
236 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
237 is the faster version used in gawk. [there is even a faster, duff-device
238 version] the magic constant 65599 was picked out of thin air while
239 experimenting with different constants, and turns out to be a prime.
240 this is one of the algorithms used in berkeley db (see sleepycat) and
241 elsewhere. */
242 KU32 uHash = 0;
243 KU32 uChar;
244 while ((uChar = (unsigned char)*pszString++) != 0)
245 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
246 return uHash;
247}
248
249
250
251
252/**
253 * Retains a new reference to the given module
254 * @returns pMod
255 * @param pMod The module to retain.
256 */
257static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
258{
259 kHlpAssert(pMod->cRefs > 0);
260 kHlpAssert(pMod->cRefs < 64);
261 pMod->cRefs++;
262 return pMod;
263}
264
265
266/**
267 * Releases a module reference.
268 *
269 * @param pMod The module to release.
270 */
271static void kwLdrModuleRelease(PKWMODULE pMod)
272{
273 if (--pMod->cRefs == 0)
274 {
275 /* Unlink it. */
276 if (!pMod->fExe)
277 {
278 PKWMODULE pPrev = NULL;
279 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
280 if (g_apModules[idx] == pMod)
281 g_apModules[idx] = pMod->pNext;
282 else
283 {
284 PKWMODULE pPrev = g_apModules[idx];
285 kHlpAssert(pPrev != NULL);
286 while (pPrev->pNext != pMod)
287 {
288 pPrev = pPrev->pNext;
289 kHlpAssert(pPrev != NULL);
290 }
291 pPrev->pNext = pMod->pNext;
292 }
293 }
294
295 /* Release import modules. */
296 if (!pMod->fNative)
297 {
298 KSIZE idx = pMod->u.Manual.cImpMods;
299 while (idx-- > 0)
300 {
301 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
302 pMod->u.Manual.apImpMods[idx] = NULL;
303 }
304 }
305
306 /* Free our resources. */
307 kLdrModClose(pMod->pLdrMod);
308 pMod->pLdrMod = NULL;
309
310 if (!pMod->fNative)
311 {
312 kHlpPageFree(pMod->u.Manual.pvCopy, pMod->u.Manual.cbImage);
313 kHlpPageFree(pMod->u.Manual.pvLoad, pMod->u.Manual.cbImage);
314 }
315
316 kHlpFree(pMod);
317 }
318 else
319 kHlpAssert(pMod->cRefs < 64);
320}
321
322
323/**
324 * Links the module into the module hash table.
325 *
326 * @returns pMod
327 * @param pMod The module to link.
328 */
329static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
330{
331 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
332 pMod->pNext = g_apModules[idx];
333 g_apModules[idx] = pMod;
334 return pMod;
335}
336
337
338/**
339 * Creates a module using the native loader.
340 *
341 * @returns Module w/ 1 reference on success, NULL on failure.
342 * @param pszPath The normalized path to the module.
343 * @param uHashPath The module path hash.
344 */
345static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath)
346{
347 /*
348 * Open the module and check the type.
349 */
350 PKLDRMOD pLdrMod;
351 int rc = kLdrModOpenNative(pszPath, &pLdrMod);
352 if (rc == 0)
353 {
354 /*
355 * Create the entry.
356 */
357 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
358 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath);
359 if (pMod)
360 {
361 pMod->pszPath = (char *)kHlpMemCopy(pMod + 1, pszPath, cbPath);
362 pMod->uHashPath = uHashPath;
363 pMod->cRefs = 1;
364 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
365 pMod->fExe = K_FALSE;
366 pMod->fNative = K_TRUE;
367 pMod->pLdrMod = pLdrMod;
368 return kwLdrModuleLink(pMod);
369 }
370 //kLdrModClose(pLdrMod);
371 }
372 return NULL;
373}
374
375
376/**
377 * Creates a module using the our own loader.
378 *
379 * @returns Module w/ 1 reference on success, NULL on failure.
380 * @param pszPath The normalized path to the module.
381 * @param uHashPath The module path hash.
382 * @param fExe K_TRUE if this is an executable image, K_FALSE
383 * if not. Executable images does not get entered
384 * into the global module table.
385 * @param pExeMod The executable module of the process (for
386 * resolving imports). NULL if fExe is set.
387 */
388static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe, PKWMODULE pExeMod)
389{
390 /*
391 * Open the module and check the type.
392 */
393 PKLDRMOD pLdrMod;
394 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
395 if (rc == 0)
396 {
397 switch (pLdrMod->enmType)
398 {
399 case KLDRTYPE_EXECUTABLE_FIXED:
400 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
401 case KLDRTYPE_EXECUTABLE_PIC:
402 if (!fExe)
403 rc = KERR_GENERAL_FAILURE;
404 break;
405
406 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
407 case KLDRTYPE_SHARED_LIBRARY_PIC:
408 case KLDRTYPE_SHARED_LIBRARY_FIXED:
409 if (fExe)
410 rc = KERR_GENERAL_FAILURE;
411 break;
412
413 default:
414 rc = KERR_GENERAL_FAILURE;
415 break;
416 }
417 if (rc == 0)
418 {
419 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
420 if (cImports >= 0)
421 {
422 /*
423 * Create the entry.
424 */
425 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
426 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + sizeof(pMod) * cImports);
427 if (pMod)
428 {
429 KBOOL fFixed;
430
431 pMod->cRefs = 1;
432 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
433 pMod->fExe = fExe;
434 pMod->fNative = K_FALSE;
435 pMod->pLdrMod = pLdrMod;
436 pMod->u.Manual.cImpMods = (KU32)cImports;
437 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
438
439 /*
440 * Figure out where to load it and get memory there.
441 */
442 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
443 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
444 pMod->u.Manual.pvLoad = fFixed ? (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
445 pMod->u.Manual.cbImage = kLdrModSize(pLdrMod);
446 if ( !fFixed
447 || (KUPTR)pMod->u.Manual.pvLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
448 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pvLoad - (KUPTR)g_abDefLdBuf < pMod->u.Manual.cbImage)
449 rc = kHlpPageAlloc(&pMod->u.Manual.pvLoad, pMod->u.Manual.cbImage, KPROT_EXECUTE_READWRITE, fFixed);
450 if (rc == 0)
451 {
452 rc = kHlpPageAlloc(&pMod->u.Manual.pvCopy, pMod->u.Manual.cbImage, KPROT_READWRITE, K_FALSE);
453 if (rc == 0)
454 {
455
456 KI32 iImp;
457
458 /*
459 * Link the module (unless it's an executable image) and process the imports.
460 */
461 if (!fExe)
462 kwLdrModuleLink(pMod);
463
464 for (iImp = 0; iImp < cImports; iImp++)
465 {
466 char szName[1024];
467 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
468 if (rc == 0)
469 {
470 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, &pMod->u.Manual.apImpMods[iImp]);
471 if (rc == 0)
472 continue;
473 }
474 break;
475 }
476
477 if (rc == 0)
478 {
479 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pvCopy, (KUPTR)pMod->u.Manual.pvLoad,
480 kwLdrModuleGetImportCallback, pMod);
481 if (rc == 0)
482 {
483 pMod->u.Manual.pvBits = pMod->u.Manual.pvCopy;
484 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
485 return pMod;
486 }
487 }
488
489 kwLdrModuleRelease(pMod);
490 return NULL;
491
492 //kHlpPageFree(pMod->u.Manual.pvCopy, pMod->u.Manual.cbImage);
493 }
494 kHlpPageFree(pMod->u.Manual.pvLoad, pMod->u.Manual.cbImage);
495 }
496 }
497 }
498 }
499 kLdrModClose(pLdrMod);
500 }
501 return NULL;
502}
503
504
505/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
506static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
507 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
508{
509 PKWMODULE pCurMod = (PKWMODULE)pvUser;
510 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
511 int rc;
512 K_NOREF(pMod);
513
514 if (pImpMod->fNative)
515 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
516 iSymbol, pchSymbol, cchSymbol, pszVersion,
517 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
518 puValue, pfKind);
519 else
520 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pvLoad,
521 iSymbol, pchSymbol, cchSymbol, pszVersion,
522 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
523 puValue, pfKind);
524 if (rc == 0)
525 {
526 KU32 i = g_cSandboxReplacements;
527 while (i-- > 0)
528 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
529 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
530 {
531 if ( !g_aSandboxReplacements[i].pszModule
532 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
533 {
534 printf("replacing %s!%s\n", &pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction);
535 *puValue = g_aSandboxReplacements[i].pfnReplacement;
536 }
537 }
538 }
539
540 printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
541 return rc;
542
543}
544
545
546/**
547 * Gets the main entrypoint for a module.
548 *
549 * @returns 0 on success, KERR on failure
550 * @param pMod The module.
551 * @param puAddrMain Where to return the address.
552 */
553static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
554{
555 KLDRADDR uLdrAddrMain;
556 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pvLoad, &uLdrAddrMain);
557 if (rc == 0)
558 {
559 *puAddrMain = (KUPTR)uLdrAddrMain;
560 return 0;
561 }
562 return rc;
563}
564
565
566static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation)
567{
568 if (enmLocation == KWLOCATION_SYSTEM32)
569 return K_TRUE;
570 return kHlpStrICompAscii(pszFilename, "msvcrt.dll") == 0
571 || kHlpStrNICompAscii(pszFilename, "msvc", 4) == 0;
572}
573
574
575/**
576 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
577 *
578 * If the file exists, we consult the module hash table before trying to load it
579 * off the disk.
580 *
581 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
582 * failure.
583 * @param pszPath The name of the import module.
584 * @param enmLocation The location we're searching. This is used in
585 * the heuristics for determining if we can use the
586 * native loader or need to sandbox the DLL.
587 * @param pExe The executable (optional).
588 */
589static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod)
590{
591 /*
592 * Does the file exists and is it a regular file?
593 */
594 BirdStat_T Stat;
595 int rc = birdStatFollowLink(pszPath, &Stat);
596 if (rc == 0)
597 {
598 if (S_ISREG(Stat.st_mode))
599 {
600 /*
601 * Yes! Normalize it and look it up in the hash table.
602 */
603 char szNormPath[1024];
604 rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
605 if (rc == 0)
606 {
607 KU32 const uHashPath = kwStrHash(szNormPath);
608 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
609 PKWMODULE pMod = g_apModules[idxHash];
610 if (pMod)
611 {
612 do
613 {
614 if ( pMod->uHashPath == uHashPath
615 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
616 return kwLdrModuleRetain(pMod);
617 pMod = pMod->pNext;
618 } while (pMod);
619 }
620
621 /*
622 * Not in the hash table, so we have to load it from scratch.
623 */
624 if (kwLdrModuleCanLoadNatively(kHlpGetFilename(szNormPath), enmLocation))
625 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath);
626 else
627 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod);
628 if (pMod)
629 return pMod;
630 return (PKWMODULE)~(KUPTR)0;
631 }
632 }
633 }
634 return NULL;
635}
636
637
638/**
639 * Gets a reference to the module by the given name.
640 *
641 * We must do the search path thing, as our hash table may multiple DLLs with
642 * the same base name due to different tools version and similar. We'll use a
643 * modified search sequence, though. No point in searching the current
644 * directory for instance.
645 *
646 * @returns 0 on success, KERR on failure.
647 * @param pszName The name of the import module.
648 * @param pExe The executable (optional).
649 * @param pImporter The module doing the importing (optional).
650 * @param ppMod Where to return the module pointer w/ reference.
651 */
652static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter, PKWMODULE *ppMod)
653{
654 KSIZE const cchName = kHlpStrLen(pszName);
655 char szPath[1024];
656 PKWMODULE pMod = NULL;
657
658
659 /* The import path. */
660 if (pMod == NULL && pImporter != NULL)
661 {
662 if (pImporter->offFilename + cchName >= sizeof(szPath))
663 return KERR_BUFFER_OVERFLOW;
664 kHlpMemCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
665 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe);
666 }
667
668 /* Application directory first. */
669 if (pMod == NULL && pExe != NULL && pExe != pImporter)
670 {
671 if (pExe->offFilename + cchName >= sizeof(szPath))
672 return KERR_BUFFER_OVERFLOW;
673 kHlpMemCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
674 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe);
675 }
676
677 /* The windows directory. */
678 if (pMod == NULL)
679 {
680 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
681 if ( cchDir <= 2
682 || cchDir + 1 + cchName >= sizeof(szPath))
683 return KERR_BUFFER_OVERFLOW;
684 szPath[cchDir++] = '\\';
685 kHlpMemCopy(&szPath[cchDir], pszName, cchName + 1);
686 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe);
687 }
688
689 /* Return. */
690 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
691 {
692 *ppMod = pMod;
693 return 0;
694 }
695 *ppMod = NULL;
696 return KERR_GENERAL_FAILURE;
697}
698
699
700
701/**
702 * Creates a tool entry and inserts it.
703 *
704 * @returns Pointer to the tool entry. NULL on failure.
705 * @param pszTool The normalized path to the tool.
706 * @param uHashPath The hash of the tool path.
707 * @param idxHashTab The hash table index of the tool.
708 */
709static PKWTOOL kwToolEntryCreate(const char *pszTool, KU32 uHashPath, unsigned idxHashTab)
710{
711 KSIZE cbTool = kHlpStrLen(pszTool) + 1;
712 PKWTOOL pTool = (PKWTOOL)kHlpAllocZ(sizeof(*pTool) + cbTool);
713 if (pTool)
714 {
715 pTool->pszPath = (char *)kHlpMemCopy(pTool + 1, pszTool, cbTool);
716 pTool->uHashPath = uHashPath;
717 pTool->enmType = KWTOOLTYPE_SANDBOXED;
718
719 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pszTool, uHashPath, K_TRUE /*fExe*/, NULL);
720 if (!pTool->u.Sandboxed.pExe)
721 pTool->enmType = KWTOOLTYPE_EXEC;
722
723 /* Link the tool. */
724 pTool->pNext = g_apTools[idxHashTab];
725 g_apTools[idxHashTab] = pTool;
726 return pTool;
727 }
728 return NULL;
729}
730
731
732/**
733 * Looks up the given tool, creating a new tool table entry if necessary.
734 *
735 * @returns Pointer to the tool entry. NULL on failure.
736 * @param pszExe The executable for the tool (not normalized).
737 */
738static PKWTOOL kwToolLookup(const char *pszExe)
739{
740 /*
741 * Normalize the path and look up the tool in the g_apTools hash table.
742 */
743 char szNormPath[4096];
744 int rc = kwPathNormalize(pszExe, szNormPath, sizeof(szNormPath));
745 if (rc == 0)
746 {
747 KU32 uHashPath = kwStrHash(szNormPath);
748 unsigned idxHash = uHashPath % K_ELEMENTS(g_apTools);
749 PKWTOOL pTool = g_apTools[idxHash];
750 if (pTool)
751 {
752 do
753 {
754 if ( pTool->uHashPath == uHashPath
755 && kHlpStrComp(pTool->pszPath, szNormPath) == 0)
756 return pTool;
757 pTool = pTool->pNext;
758 } while (pTool);
759 }
760
761 /*
762 * Not found, create new entry.
763 */
764 return kwToolEntryCreate(szNormPath, uHashPath, idxHash);
765 }
766 return NULL;
767}
768
769
770
771/*
772 *
773 * Kernel32 API replacements.
774 * Kernel32 API replacements.
775 * Kernel32 API replacements.
776 *
777 */
778
779/** ExitProcess replacement. */
780static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
781{
782 if (g_pSandbox->idMainThread == GetCurrentThreadId())
783 {
784 g_pSandbox->rcExitCode = (int)uExitCode;
785 longjmp(g_pSandbox->JmpBuf, 1);
786 }
787 __debugbreak();
788}
789
790/** ExitProcess replacement. */
791static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
792{
793 if (hProcess == GetCurrentProcess())
794 kwSandbox_Kernel32_ExitProcess(uExitCode);
795__debugbreak();
796 return TerminateProcess(hProcess, uExitCode);
797}
798
799
800
801/*
802 *
803 * MS Visual C++ CRT replacements.
804 * MS Visual C++ CRT replacements.
805 * MS Visual C++ CRT replacements.
806 *
807 */
808
809/** Normal CRT exit(). */
810static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
811{
812 fprintf(stderr, "kwSandbox_msvcrt_exit\n");
813 kwSandbox_Kernel32_ExitProcess(rcExitCode);
814}
815
816
817/** Quick CRT _exit(). */
818static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
819{
820 /* Quick. */
821 fprintf(stderr, "kwSandbox_msvcrt__exit\n");
822 kwSandbox_Kernel32_ExitProcess(rcExitCode);
823}
824
825
826/** Return to caller CRT _cexit(). */
827static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
828{
829 fprintf(stderr, "kwSandbox_msvcrt__cexit\n");
830 kwSandbox_Kernel32_ExitProcess(rcExitCode);
831}
832
833
834/** Quick return to caller CRT _c_exit(). */
835static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
836{
837 fprintf(stderr, "kwSandbox_msvcrt__c_exit\n");
838 kwSandbox_Kernel32_ExitProcess(rcExitCode);
839}
840
841
842/** Runtime error and exit. */
843static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
844{
845 fprintf(stderr, "\nRuntime error #%u!\n", iMsgNo);
846 kwSandbox_Kernel32_ExitProcess(255);
847}
848
849
850/** The CRT internal __getmainargs() API. */
851static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
852 int dowildcard, /*_startupinfo*/ void *startinfo)
853{
854 /** @todo startinfo points at a newmode (setmode) value. */
855
856 *pargc = 2;
857 *pargv = (char **)kHlpAllocZ(sizeof(char *) * 3);
858 (*pargv)[0] = "nasm.exe";
859 (*pargv)[1] = "-h";
860 *penvp = (char **)kHlpAllocZ(sizeof(char *) * 1);
861 return 0;
862}
863
864
865/**
866 * Functions that needs replacing for sandboxed execution.
867 */
868KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
869{
870#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
871 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
872 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
873 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
874 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
875 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
876 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
877 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
878 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
879};
880/** Number of entries in g_aReplacements. */
881KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
882
883
884/**
885 * Used by kwSandboxExec to reset the state of the module tree.
886 *
887 * This is done recursively.
888 *
889 * @param pMod The root of the tree to consider.
890 */
891static void kwSandboxResetModuleState(PKWMODULE pMod)
892{
893 if ( !pMod->fNative
894 && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
895 {
896 KSIZE iImp;
897 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
898 iImp = pMod->u.Manual.cImpMods;
899 while (iImp-- > 0)
900 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
901 }
902}
903
904
905/**
906 * Does module initialization starting at @a pMod.
907 *
908 * This is initially used on the executable. Later it is used by the
909 * LoadLibrary interceptor.
910 *
911 * @returns 0 on success, error on failure.
912 * @param pMod The module to initialize.
913 */
914static int kwSandboxInitModuleTree(PKWMODULE pMod)
915{
916 int rc = 0;
917 if (!pMod->fNative)
918 {
919 /* Need to copy bits? */
920 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
921 {
922 kHlpMemCopy(pMod->u.Manual.pvLoad, pMod->u.Manual.pvCopy, pMod->u.Manual.cbImage);
923 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
924 }
925
926 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
927 {
928 /* Must do imports first, but mark our module as being initialized to avoid
929 endless recursion should there be a dependency loop. */
930 KSIZE iImp;
931 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
932
933 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
934 {
935 rc = kwSandboxInitModuleTree(pMod->u.Manual.apImpMods[iImp]);
936 if (rc != 0)
937 return rc;
938 }
939
940 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pvLoad, (KUPTR)pMod->u.Manual.pvLoad);
941 if (rc == 0)
942 pMod->u.Manual.enmState = KWMODSTATE_READY;
943 else
944 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
945 }
946 }
947 return rc;
948}
949
950
951static void *kwSandboxGetProcessEnvironmentBlock(void)
952{
953#if K_ARCH == K_ARCH_X86_32
954 return (void *)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
955#elif K_ARCH == K_ARCH_AMD64
956 return (void *)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
957#else
958# error "Port me!"
959#endif
960}
961
962
963static int kwSandboxExec(PKWTOOL pTool, const char *pszCmdLine, int *prcExitCode)
964{
965 int rc;
966 KWSANDBOX Sandbox;
967
968 /*
969 * Initialize the sandbox environment.
970 */
971 Sandbox.pTool = pTool;
972 Sandbox.rcExitCode = *prcExitCode = 256;
973 Sandbox.idMainThread = GetCurrentThreadId();
974 g_pSandbox = &Sandbox;
975
976 /*
977 * Do module initialization.
978 */
979 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
980 rc = kwSandboxInitModuleTree(pTool->u.Sandboxed.pExe);
981 if (rc == 0)
982 {
983 /*
984 * Call the main function.
985 */
986 KUPTR uAddrMain;
987 rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &uAddrMain);
988 if (rc == 0)
989 {
990 int rcExitCode;
991 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
992 *(KUPTR *)&pfnWin64Entrypoint = uAddrMain;
993
994 __try
995 {
996 if (setjmp(Sandbox.JmpBuf) == 0)
997 {
998 *(KU64*)(Sandbox.JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
999 rcExitCode = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock, NULL, NULL, NULL);
1000 }
1001 else
1002 rcExitCode = Sandbox.rcExitCode;
1003 }
1004 __except (EXCEPTION_EXECUTE_HANDLER)
1005 {
1006 rcExitCode = 512;
1007 }
1008 *prcExitCode = rcExitCode;
1009 }
1010 }
1011 return rc;
1012}
1013
1014
1015static int kwExecCmdLine(const char *pszExe, const char *pszCmdLine)
1016{
1017 int rc;
1018 PKWTOOL pTool = kwToolLookup(pszExe);
1019 if (pTool)
1020 {
1021 int rc;
1022 int rcExitExit;
1023 switch (pTool->enmType)
1024 {
1025 case KWTOOLTYPE_SANDBOXED:
1026 rc = kwSandboxExec(pTool, pszCmdLine, &rcExitExit);
1027 break;
1028
1029 default:
1030 kHlpAssertFailed();
1031 case KWTOOLTYPE_WATCOM:
1032 case KWTOOLTYPE_EXEC:
1033 rc = 2;
1034 break;
1035 }
1036 }
1037 else
1038 rc = 1;
1039 return rc;
1040}
1041
1042
1043int main(int argc, char **argv)
1044{
1045 int rc = kwExecCmdLine(argv[1], argv[2]);
1046 rc = kwExecCmdLine(argv[1], argv[2]);
1047 return rc;
1048}
1049
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