kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

wasm_run.c (32697B)


      1 #include "wasm_run.h"
      2 
      3 #include <kit/core.h>
      4 #include <kit/wasm.h>
      5 #include <stddef.h>
      6 #include <stdint.h>
      7 #include <string.h>
      8 
      9 #include "driver.h"
     10 
     11 #define DRIVER_WASM_DEFAULT_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
     12 #define DRIVER_WASM_DEFAULT_MAX_TOTAL_MEMORY_BYTES (1024ull * 1024ull * 1024ull)
     13 
     14 typedef void (*DriverWasmInitFn)(KitWasmInstance*);
     15 typedef int (*DriverWasmMainFn)(KitWasmInstance*);
     16 
     17 typedef struct DriverWasmOwnedString {
     18   char* ptr;
     19   size_t size;
     20 } DriverWasmOwnedString;
     21 
     22 typedef struct DriverWasmHostBuild {
     23   DriverEnv* env;
     24   const DriverWasmRunOptions* opts;
     25   const char* tool;
     26 
     27   KitWasmHost* host;
     28   const char** env_entries;
     29   uint32_t nenv_entries;
     30   uint32_t env_cap;
     31   KitWasmFsMount* mounts;
     32   uint32_t nmounts;
     33   uint32_t mounts_cap;
     34   DriverWasmOwnedString* owned;
     35   uint32_t nowned;
     36   uint32_t owned_cap;
     37   uint64_t random_state;
     38 } DriverWasmHostBuild;
     39 
     40 static int wasm_list_push(const char* tool, const char** items, size_t cap,
     41                           uint32_t* count, const char* value) {
     42   if (*count >= cap || *count == UINT32_MAX) {
     43     driver_errf(tool, "too many wasm sandbox options");
     44     return 1;
     45   }
     46   items[*count] = value;
     47   *count += 1u;
     48   return 0;
     49 }
     50 
     51 int driver_wasm_run_options_init(DriverWasmRunOptions* o, DriverEnv* env,
     52                                  size_t argv_bound) {
     53   size_t bytes = argv_bound * sizeof(const char*);
     54   memset(o, 0, sizeof *o);
     55   o->env = env;
     56   o->argv_bound = argv_bound;
     57   o->max_instance_bytes = DRIVER_WASM_DEFAULT_MAX_INSTANCE_BYTES;
     58   o->max_total_memory_bytes = DRIVER_WASM_DEFAULT_MAX_TOTAL_MEMORY_BYTES;
     59   o->max_memories = UINT32_MAX;
     60   o->imports = DRIVER_WASM_IMPORT_DENY;
     61   o->env_mode = DRIVER_WASM_ENV_NONE;
     62   o->stdio_mode = DRIVER_WASM_STDIO_NULL;
     63   o->clock_mode = DRIVER_WASM_CLOCK_NONE;
     64   o->random_mode = DRIVER_WASM_RANDOM_NONE;
     65   if (argv_bound == 0) return 0;
     66   o->env_pass = (const char**)driver_alloc_zeroed(env, bytes);
     67   o->env_set = (const char**)driver_alloc_zeroed(env, bytes);
     68   o->map_dirs = (const char**)driver_alloc_zeroed(env, bytes);
     69   o->map_files = (const char**)driver_alloc_zeroed(env, bytes);
     70   if (!o->env_pass || !o->env_set || !o->map_dirs || !o->map_files) {
     71     driver_errf("run", "out of memory");
     72     return 1;
     73   }
     74   return 0;
     75 }
     76 
     77 void driver_wasm_run_options_fini(DriverWasmRunOptions* o) {
     78   size_t bytes;
     79   if (!o) return;
     80   bytes = o->argv_bound * sizeof(const char*);
     81   if (o->env_pass) driver_free(o->env, o->env_pass, bytes);
     82   if (o->env_set) driver_free(o->env, o->env_set, bytes);
     83   if (o->map_dirs) driver_free(o->env, o->map_dirs, bytes);
     84   if (o->map_files) driver_free(o->env, o->map_files, bytes);
     85   memset(o, 0, sizeof *o);
     86 }
     87 
     88 static int wasm_parse_u64_dec(const char* s, uint64_t* out) {
     89   uint64_t v = 0;
     90   int any = 0;
     91   if (!s) return 1;
     92   while (*s >= '0' && *s <= '9') {
     93     unsigned d = (unsigned)(*s - '0');
     94     if (v > (UINT64_MAX - d) / 10u) return 1;
     95     v = v * 10u + d;
     96     any = 1;
     97     s++;
     98   }
     99   if (!any || *s) return 1;
    100   *out = v;
    101   return 0;
    102 }
    103 
    104 static int wasm_parse_size(const char* s, uint64_t* out) {
    105   uint64_t v = 0;
    106   uint64_t mul = 1;
    107   int any = 0;
    108   if (!s) return 1;
    109   while (*s >= '0' && *s <= '9') {
    110     unsigned d = (unsigned)(*s - '0');
    111     if (v > (UINT64_MAX - d) / 10u) return 1;
    112     v = v * 10u + d;
    113     any = 1;
    114     s++;
    115   }
    116   if (!any) return 1;
    117   if (*s) {
    118     char c = *s++;
    119     if (c >= 'a' && c <= 'z') c = (char)(c - ('a' - 'A'));
    120     if (c == 'K')
    121       mul = 1024ull;
    122     else if (c == 'M')
    123       mul = 1024ull * 1024ull;
    124     else if (c == 'G')
    125       mul = 1024ull * 1024ull * 1024ull;
    126     else
    127       return 1;
    128     if (*s == 'i' || *s == 'I') s++;
    129     if (*s == 'b' || *s == 'B') s++;
    130     if (*s) return 1;
    131   }
    132   if (v > UINT64_MAX / mul) return 1;
    133   *out = v * mul;
    134   return 0;
    135 }
    136 
    137 static const char* wasm_take_value(const char* tool, int argc, char** argv,
    138                                    int* index, const char* flag,
    139                                    const char* inline_value) {
    140   if (inline_value) return inline_value;
    141   if (*index + 1 >= argc) {
    142     driver_errf(tool, "%.*s requires an argument",
    143                 KIT_SLICE_ARG(kit_slice_cstr(flag)));
    144     return NULL;
    145   }
    146   *index += 1;
    147   return argv[*index];
    148 }
    149 
    150 static int wasm_set_import_mode(const char* tool, DriverWasmRunOptions* o,
    151                                 const char* value) {
    152   if (driver_streq(value, "deny") || driver_streq(value, "none")) {
    153     o->imports = DRIVER_WASM_IMPORT_DENY;
    154     return 0;
    155   }
    156   if (driver_streq(value, "wasi")) {
    157     o->imports = DRIVER_WASM_IMPORT_WASI;
    158     return 0;
    159   }
    160   if (driver_streq(value, "test")) {
    161     o->imports = DRIVER_WASM_IMPORT_TEST;
    162     return 0;
    163   }
    164   driver_errf(tool, "unknown --wasm-imports value: %.*s",
    165               KIT_SLICE_ARG(kit_slice_cstr(value)));
    166   return 1;
    167 }
    168 
    169 static int wasm_set_env_mode(const char* tool, DriverWasmRunOptions* o,
    170                              const char* value) {
    171   if (driver_streq(value, "none")) {
    172     o->env_mode = DRIVER_WASM_ENV_NONE;
    173     return 0;
    174   }
    175   if (driver_streq(value, "allowlist")) {
    176     o->env_mode = DRIVER_WASM_ENV_ALLOWLIST;
    177     return 0;
    178   }
    179   if (driver_streq(value, "inherit")) {
    180     o->env_mode = DRIVER_WASM_ENV_INHERIT;
    181     return 0;
    182   }
    183   driver_errf(tool, "unknown --wasm-env value: %.*s",
    184               KIT_SLICE_ARG(kit_slice_cstr(value)));
    185   return 1;
    186 }
    187 
    188 static int wasm_set_stdio_mode(const char* tool, DriverWasmRunOptions* o,
    189                                const char* value) {
    190   if (driver_streq(value, "null")) {
    191     o->stdio_mode = DRIVER_WASM_STDIO_NULL;
    192     return 0;
    193   }
    194   if (driver_streq(value, "inherit")) {
    195     o->stdio_mode = DRIVER_WASM_STDIO_INHERIT;
    196     return 0;
    197   }
    198   driver_errf(tool, "unknown --wasm-stdio value: %.*s",
    199               KIT_SLICE_ARG(kit_slice_cstr(value)));
    200   return 1;
    201 }
    202 
    203 static int wasm_set_clock_mode(const char* tool, DriverWasmRunOptions* o,
    204                                const char* value) {
    205   if (driver_streq(value, "none")) {
    206     o->clock_mode = DRIVER_WASM_CLOCK_NONE;
    207     return 0;
    208   }
    209   if (driver_streq(value, "monotonic")) {
    210     o->clock_mode = DRIVER_WASM_CLOCK_MONOTONIC;
    211     return 0;
    212   }
    213   if (driver_streq(value, "realtime")) {
    214     o->clock_mode = DRIVER_WASM_CLOCK_REALTIME;
    215     return 0;
    216   }
    217   driver_errf(tool, "unknown --wasm-clock value: %.*s",
    218               KIT_SLICE_ARG(kit_slice_cstr(value)));
    219   return 1;
    220 }
    221 
    222 static int wasm_set_random_mode(const char* tool, DriverWasmRunOptions* o,
    223                                 const char* value) {
    224   if (driver_streq(value, "none")) {
    225     o->random_mode = DRIVER_WASM_RANDOM_NONE;
    226     o->random_seed = NULL;
    227     return 0;
    228   }
    229   if (driver_streq(value, "host")) {
    230     o->random_mode = DRIVER_WASM_RANDOM_HOST;
    231     o->random_seed = NULL;
    232     return 0;
    233   }
    234   if (driver_strneq(value, "seed:", 5)) {
    235     o->random_mode = DRIVER_WASM_RANDOM_SEED;
    236     o->random_seed = value + 5;
    237     return 0;
    238   }
    239   driver_errf(tool, "unknown --wasm-random value: %.*s",
    240               KIT_SLICE_ARG(kit_slice_cstr(value)));
    241   return 1;
    242 }
    243 
    244 int driver_wasm_run_try_consume(DriverWasmRunOptions* o, const char* tool,
    245                                 int argc, char** argv, int* index) {
    246   const char* a = argv[*index];
    247   const char* v;
    248   uint64_t u64;
    249   if (!driver_strneq(a, "--wasm-", 7)) return 0;
    250   o->used = 1;
    251 
    252   if (driver_streq(a, "--wasm-wasi")) {
    253     o->imports = DRIVER_WASM_IMPORT_WASI;
    254     return 1;
    255   }
    256   if (driver_streq(a, "--wasm-memory-max") ||
    257       driver_strneq(a, "--wasm-memory-max=", 18)) {
    258     v = wasm_take_value(
    259         tool, argc, argv, index, "--wasm-memory-max",
    260         driver_strneq(a, "--wasm-memory-max=", 18) ? a + 18 : NULL);
    261     if (!v) return -1;
    262     if (wasm_parse_size(v, &u64) != 0) {
    263       driver_errf(tool, "--wasm-memory-max requires a byte size");
    264       return -1;
    265     }
    266     o->max_total_memory_bytes = u64;
    267     return 1;
    268   }
    269   if (driver_streq(a, "--wasm-instance-max") ||
    270       driver_strneq(a, "--wasm-instance-max=", 20)) {
    271     v = wasm_take_value(
    272         tool, argc, argv, index, "--wasm-instance-max",
    273         driver_strneq(a, "--wasm-instance-max=", 20) ? a + 20 : NULL);
    274     if (!v) return -1;
    275     if (wasm_parse_size(v, &u64) != 0) {
    276       driver_errf(tool, "--wasm-instance-max requires a byte size");
    277       return -1;
    278     }
    279     o->max_instance_bytes = u64;
    280     return 1;
    281   }
    282   if (driver_streq(a, "--wasm-memories-max") ||
    283       driver_strneq(a, "--wasm-memories-max=", 20)) {
    284     v = wasm_take_value(
    285         tool, argc, argv, index, "--wasm-memories-max",
    286         driver_strneq(a, "--wasm-memories-max=", 20) ? a + 20 : NULL);
    287     if (!v) return -1;
    288     if (wasm_parse_u64_dec(v, &u64) != 0 || u64 > UINT32_MAX) {
    289       driver_errf(tool, "--wasm-memories-max requires a non-negative integer");
    290       return -1;
    291     }
    292     o->max_memories = (uint32_t)u64;
    293     return 1;
    294   }
    295   if (driver_streq(a, "--wasm-imports") ||
    296       driver_strneq(a, "--wasm-imports=", 15)) {
    297     v = wasm_take_value(
    298         tool, argc, argv, index, "--wasm-imports",
    299         driver_strneq(a, "--wasm-imports=", 15) ? a + 15 : NULL);
    300     if (!v) return -1;
    301     return wasm_set_import_mode(tool, o, v) == 0 ? 1 : -1;
    302   }
    303   if (driver_streq(a, "--wasm-env") || driver_strneq(a, "--wasm-env=", 11)) {
    304     v = wasm_take_value(tool, argc, argv, index, "--wasm-env",
    305                         driver_strneq(a, "--wasm-env=", 11) ? a + 11 : NULL);
    306     if (!v) return -1;
    307     return wasm_set_env_mode(tool, o, v) == 0 ? 1 : -1;
    308   }
    309   if (driver_streq(a, "--wasm-env-pass") ||
    310       driver_strneq(a, "--wasm-env-pass=", 16)) {
    311     v = wasm_take_value(
    312         tool, argc, argv, index, "--wasm-env-pass",
    313         driver_strneq(a, "--wasm-env-pass=", 16) ? a + 16 : NULL);
    314     if (!v) return -1;
    315     if (wasm_list_push(tool, o->env_pass, o->argv_bound, &o->nenv_pass, v) != 0)
    316       return -1;
    317     return 1;
    318   }
    319   if (driver_streq(a, "--wasm-env-set") ||
    320       driver_strneq(a, "--wasm-env-set=", 15)) {
    321     v = wasm_take_value(
    322         tool, argc, argv, index, "--wasm-env-set",
    323         driver_strneq(a, "--wasm-env-set=", 15) ? a + 15 : NULL);
    324     if (!v) return -1;
    325     if (wasm_list_push(tool, o->env_set, o->argv_bound, &o->nenv_set, v) != 0)
    326       return -1;
    327     return 1;
    328   }
    329   if (driver_streq(a, "--wasm-fs") || driver_strneq(a, "--wasm-fs=", 10)) {
    330     v = wasm_take_value(tool, argc, argv, index, "--wasm-fs",
    331                         driver_strneq(a, "--wasm-fs=", 10) ? a + 10 : NULL);
    332     if (!v) return -1;
    333     if (!driver_streq(v, "none")) {
    334       driver_errf(tool, "unknown --wasm-fs value: %.*s",
    335                   KIT_SLICE_ARG(kit_slice_cstr(v)));
    336       return -1;
    337     }
    338     return 1;
    339   }
    340   if (driver_streq(a, "--wasm-map-dir") ||
    341       driver_strneq(a, "--wasm-map-dir=", 15)) {
    342     v = wasm_take_value(
    343         tool, argc, argv, index, "--wasm-map-dir",
    344         driver_strneq(a, "--wasm-map-dir=", 15) ? a + 15 : NULL);
    345     if (!v) return -1;
    346     if (wasm_list_push(tool, o->map_dirs, o->argv_bound, &o->nmap_dirs, v) != 0)
    347       return -1;
    348     return 1;
    349   }
    350   if (driver_streq(a, "--wasm-map-file") ||
    351       driver_strneq(a, "--wasm-map-file=", 16)) {
    352     v = wasm_take_value(
    353         tool, argc, argv, index, "--wasm-map-file",
    354         driver_strneq(a, "--wasm-map-file=", 16) ? a + 16 : NULL);
    355     if (!v) return -1;
    356     if (wasm_list_push(tool, o->map_files, o->argv_bound, &o->nmap_files, v) !=
    357         0)
    358       return -1;
    359     return 1;
    360   }
    361   if (driver_streq(a, "--wasm-cwd") || driver_strneq(a, "--wasm-cwd=", 11)) {
    362     v = wasm_take_value(tool, argc, argv, index, "--wasm-cwd",
    363                         driver_strneq(a, "--wasm-cwd=", 11) ? a + 11 : NULL);
    364     if (!v) return -1;
    365     o->cwd = v;
    366     return 1;
    367   }
    368   if (driver_streq(a, "--wasm-stdio") ||
    369       driver_strneq(a, "--wasm-stdio=", 13)) {
    370     v = wasm_take_value(tool, argc, argv, index, "--wasm-stdio",
    371                         driver_strneq(a, "--wasm-stdio=", 13) ? a + 13 : NULL);
    372     if (!v) return -1;
    373     return wasm_set_stdio_mode(tool, o, v) == 0 ? 1 : -1;
    374   }
    375   if (driver_streq(a, "--wasm-clock") ||
    376       driver_strneq(a, "--wasm-clock=", 13)) {
    377     v = wasm_take_value(tool, argc, argv, index, "--wasm-clock",
    378                         driver_strneq(a, "--wasm-clock=", 13) ? a + 13 : NULL);
    379     if (!v) return -1;
    380     return wasm_set_clock_mode(tool, o, v) == 0 ? 1 : -1;
    381   }
    382   if (driver_streq(a, "--wasm-random") ||
    383       driver_strneq(a, "--wasm-random=", 14)) {
    384     v = wasm_take_value(tool, argc, argv, index, "--wasm-random",
    385                         driver_strneq(a, "--wasm-random=", 14) ? a + 14 : NULL);
    386     if (!v) return -1;
    387     return wasm_set_random_mode(tool, o, v) == 0 ? 1 : -1;
    388   }
    389 
    390   driver_errf(tool, "unknown wasm sandbox flag: %.*s",
    391               KIT_SLICE_ARG(kit_slice_cstr(a)));
    392   return -1;
    393 }
    394 
    395 int driver_wasm_run_options_used(const DriverWasmRunOptions* o) {
    396   return o && o->used;
    397 }
    398 
    399 static int wasm_page_bytes(const char* tool, uint64_t pages, uint64_t* out) {
    400   if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
    401     driver_errf(tool, "wasm memory size overflows");
    402     return 1;
    403   }
    404   *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
    405   return 0;
    406 }
    407 
    408 static int wasm_preflight_instance(const DriverWasmRunOptions* opts,
    409                                    const char* tool, KitJit* jit) {
    410   KitWasmRuntimeLayout layout;
    411   KitStatus st = kit_wasm_get_runtime_layout(jit, &layout);
    412   uint64_t instance_bytes;
    413   uint64_t total_memory_bytes = 0;
    414   uint32_t i;
    415   if (st != KIT_OK) {
    416     driver_errf(tool, st == KIT_NOT_FOUND
    417                           ? "wasm runtime layout metadata missing"
    418                           : "wasm runtime layout metadata malformed");
    419     return 1;
    420   }
    421   if (layout.nmemories > opts->max_memories) {
    422     driver_errf(tool, "wasm memory count exceeds %u", opts->max_memories);
    423     return 1;
    424   }
    425   instance_bytes = layout.instance_size ? layout.instance_size : 1u;
    426   if (instance_bytes > opts->max_instance_bytes) {
    427     driver_errf(tool, "wasm instance too large: %llu bytes",
    428                 (unsigned long long)instance_bytes);
    429     return 1;
    430   }
    431   for (i = 0; i < layout.nmemories; ++i) {
    432     const KitWasmMemoryLayout* ml = &layout.memories[i];
    433     uint64_t mem_bytes;
    434     if (ml->max_pages < ml->min_pages) {
    435       driver_errf(tool, "wasm memory maximum below minimum");
    436       return 1;
    437     }
    438     if (wasm_page_bytes(tool, ml->max_pages, &mem_bytes) != 0) return 1;
    439     if (mem_bytes > opts->max_total_memory_bytes ||
    440         total_memory_bytes > opts->max_total_memory_bytes - mem_bytes) {
    441       driver_errf(tool, "wasm linear memory reservation exceeds %llu bytes",
    442                   (unsigned long long)opts->max_total_memory_bytes);
    443       return 1;
    444     }
    445     total_memory_bytes += mem_bytes;
    446   }
    447   return 0;
    448 }
    449 
    450 /* Canned host import for the wasm-front test suite. Active only when the
    451  * caller explicitly selects --wasm-imports=test. */
    452 int32_t driver_wasm_test_host_add(KitWasmInstance* inst, int32_t a, int32_t b) {
    453   (void)inst;
    454   return a + b;
    455 }
    456 
    457 static int wasm_host_add_type(const KitWasmImportType* type) {
    458   return type && type->nparams == 2u && type->nresults == 1u &&
    459          type->params[0] == KIT_WASM_VAL_I32 &&
    460          type->params[1] == KIT_WASM_VAL_I32 &&
    461          type->results[0] == KIT_WASM_VAL_I32;
    462 }
    463 
    464 static void* wasm_test_resolve(void* user, const char* module,
    465                                const char* field,
    466                                const KitWasmImportType* type) {
    467   (void)user;
    468   if (!module || !field) return NULL;
    469   if (driver_streq(module, "env") && driver_streq(field, "host_add") &&
    470       wasm_host_add_type(type))
    471     return (void*)(uintptr_t)driver_wasm_test_host_add;
    472   return NULL;
    473 }
    474 
    475 static const char* wasm_status_name(KitStatus st) {
    476   switch (st) {
    477     case KIT_OK:
    478       return "ok";
    479     case KIT_NOMEM:
    480       return "out of memory";
    481     case KIT_INVALID:
    482       return "invalid wasm host configuration";
    483     case KIT_UNSUPPORTED:
    484       return "wasm host import kind unsupported";
    485     case KIT_MALFORMED:
    486       return "wasm host import metadata malformed";
    487     case KIT_NOT_FOUND:
    488       return "wasm host import unresolved";
    489     case KIT_IO:
    490       return "wasm host I/O failed";
    491     case KIT_ERR:
    492     case KIT_AMBIGUOUS:
    493     default:
    494       return "wasm host setup failed";
    495   }
    496 }
    497 
    498 static int wasm_build_add_owned(DriverWasmHostBuild* b, const char* data,
    499                                 size_t len, const char** out) {
    500   char* p;
    501   if (b->nowned >= b->owned_cap) return 1;
    502   p = (char*)driver_alloc(b->env, len + 1u);
    503   if (!p) return 1;
    504   if (len) driver_memcpy(p, data, len);
    505   p[len] = '\0';
    506   b->owned[b->nowned].ptr = p;
    507   b->owned[b->nowned].size = len + 1u;
    508   b->nowned++;
    509   *out = p;
    510   return 0;
    511 }
    512 
    513 static int wasm_env_name_valid(const char* name) {
    514   return name && name[0] && !driver_strchr(name, '=');
    515 }
    516 
    517 static int wasm_env_set_valid(const char* entry) {
    518   const char* eq = driver_strchr(entry, '=');
    519   return entry && entry[0] && eq && eq != entry;
    520 }
    521 
    522 static int wasm_build_env_pass(DriverWasmHostBuild* b, const char* name,
    523                                const char** out) {
    524   const char* value;
    525   size_t name_len;
    526   size_t value_len;
    527   char* p;
    528   if (!wasm_env_name_valid(name)) {
    529     driver_errf(b->tool, "--wasm-env-pass requires NAME");
    530     return 1;
    531   }
    532   value = driver_getenv(name);
    533   if (!value) {
    534     *out = NULL;
    535     return 0;
    536   }
    537   if (b->nowned >= b->owned_cap) return 1;
    538   name_len = driver_strlen(name);
    539   value_len = driver_strlen(value);
    540   if (name_len > SIZE_MAX - value_len - 2u) return 1;
    541   p = (char*)driver_alloc(b->env, name_len + value_len + 2u);
    542   if (!p) return 1;
    543   driver_memcpy(p, name, name_len);
    544   p[name_len] = '=';
    545   driver_memcpy(p + name_len + 1u, value, value_len);
    546   p[name_len + 1u + value_len] = '\0';
    547   b->owned[b->nowned].ptr = p;
    548   b->owned[b->nowned].size = name_len + value_len + 2u;
    549   b->nowned++;
    550   *out = p;
    551   return 0;
    552 }
    553 
    554 static uint32_t wasm_process_env_count(void) {
    555   const char* const* envp = driver_environ();
    556   uint32_t n = 0;
    557   if (!envp) return 0;
    558   while (envp[n]) {
    559     if (n == UINT32_MAX) break;
    560     ++n;
    561   }
    562   return n;
    563 }
    564 
    565 static int wasm_build_env(DriverWasmHostBuild* b, KitWasmHostConfig* cfg) {
    566   const DriverWasmRunOptions* o = b->opts;
    567   uint32_t inherited =
    568       o->env_mode == DRIVER_WASM_ENV_INHERIT ? wasm_process_env_count() : 0u;
    569   uint64_t cap64 = (uint64_t)inherited + o->nenv_pass + o->nenv_set;
    570   uint32_t n = 0;
    571   uint32_t i;
    572   if (cap64 > UINT32_MAX) {
    573     driver_errf(b->tool, "too many wasm environment entries");
    574     return 1;
    575   }
    576   b->env_cap = (uint32_t)cap64;
    577   if (b->env_cap) {
    578     b->env_entries = (const char**)driver_alloc_zeroed(
    579         b->env, (size_t)b->env_cap * sizeof(*b->env_entries));
    580     if (!b->env_entries) {
    581       driver_errf(b->tool, "out of memory");
    582       return 1;
    583     }
    584   }
    585   if (inherited) {
    586     const char* const* envp = driver_environ();
    587     for (i = 0; i < inherited; ++i) b->env_entries[n++] = envp[i];
    588   }
    589   for (i = 0; i < o->nenv_pass; ++i) {
    590     const char* entry = NULL;
    591     if (wasm_build_env_pass(b, o->env_pass[i], &entry) != 0) {
    592       driver_errf(b->tool, "out of memory");
    593       return 1;
    594     }
    595     if (entry) b->env_entries[n++] = entry;
    596   }
    597   for (i = 0; i < o->nenv_set; ++i) {
    598     if (!wasm_env_set_valid(o->env_set[i])) {
    599       driver_errf(b->tool, "--wasm-env-set requires NAME=VALUE");
    600       return 1;
    601     }
    602     b->env_entries[n++] = o->env_set[i];
    603   }
    604   b->nenv_entries = n;
    605   cfg->env = b->env_entries;
    606   cfg->nenv = n;
    607   return 0;
    608 }
    609 
    610 static const char* wasm_find_eq(const char* s) {
    611   while (s && *s) {
    612     if (*s == '=') return s;
    613     ++s;
    614   }
    615   return NULL;
    616 }
    617 
    618 static int wasm_suffix_mode_len(const char* s, size_t len, uint32_t* flags,
    619                                 size_t* new_len) {
    620   if (len >= 3u && s[len - 3u] == ':' && s[len - 2u] == 'r' &&
    621       s[len - 1u] == 'o') {
    622     *flags = KIT_WASM_FS_READ;
    623     *new_len = len - 3u;
    624     return 0;
    625   }
    626   if (len >= 3u && s[len - 3u] == ':' && s[len - 2u] == 'r' &&
    627       s[len - 1u] == 'w') {
    628     *flags = KIT_WASM_FS_READ | KIT_WASM_FS_WRITE;
    629     *new_len = len - 3u;
    630     return 0;
    631   }
    632   *flags = KIT_WASM_FS_READ;
    633   *new_len = len;
    634   return 0;
    635 }
    636 
    637 static int wasm_build_one_mount(DriverWasmHostBuild* b, const char* spec,
    638                                 uint32_t extra_flags, KitWasmFsMount* out) {
    639   const char* eq = wasm_find_eq(spec);
    640   const char* host;
    641   const char* guest;
    642   size_t host_len;
    643   size_t guest_len;
    644   uint32_t flags;
    645   if (!eq || eq == spec || !eq[1]) {
    646     driver_errf(b->tool, "wasm filesystem maps require HOST=GUEST[:ro|rw]");
    647     return 1;
    648   }
    649   host = spec;
    650   host_len = (size_t)(eq - spec);
    651   guest = eq + 1;
    652   guest_len = driver_strlen(guest);
    653   wasm_suffix_mode_len(guest, guest_len, &flags, &guest_len);
    654   if (guest_len == 0 || guest[0] != '/') {
    655     driver_errf(b->tool, "wasm guest paths must be absolute");
    656     return 1;
    657   }
    658   if (wasm_build_add_owned(b, host, host_len, &out->host_path) != 0 ||
    659       wasm_build_add_owned(b, guest, guest_len, &out->guest_path) != 0) {
    660     driver_errf(b->tool, "out of memory");
    661     return 1;
    662   }
    663   out->flags = flags | extra_flags;
    664   return 0;
    665 }
    666 
    667 static int wasm_build_mounts(DriverWasmHostBuild* b, KitWasmHostConfig* cfg) {
    668   const DriverWasmRunOptions* o = b->opts;
    669   uint64_t total = (uint64_t)o->nmap_dirs + o->nmap_files;
    670   uint32_t n = 0;
    671   uint32_t i;
    672   if (total > UINT32_MAX) {
    673     driver_errf(b->tool, "too many wasm filesystem maps");
    674     return 1;
    675   }
    676   b->mounts_cap = (uint32_t)total;
    677   if (b->mounts_cap) {
    678     b->mounts = (KitWasmFsMount*)driver_alloc_zeroed(
    679         b->env, (size_t)b->mounts_cap * sizeof(*b->mounts));
    680     if (!b->mounts) {
    681       driver_errf(b->tool, "out of memory");
    682       return 1;
    683     }
    684   }
    685   for (i = 0; i < o->nmap_dirs; ++i) {
    686     if (wasm_build_one_mount(b, o->map_dirs[i], 0u, &b->mounts[n]) != 0)
    687       return 1;
    688     ++n;
    689   }
    690   for (i = 0; i < o->nmap_files; ++i) {
    691     if (wasm_build_one_mount(b, o->map_files[i], KIT_WASM_FS_FILE,
    692                              &b->mounts[n]) != 0)
    693       return 1;
    694     ++n;
    695   }
    696   b->nmounts = n;
    697   cfg->mounts = b->mounts;
    698   cfg->nmounts = n;
    699   return 0;
    700 }
    701 
    702 static KitStatus wasm_driver_write(void* user, uint32_t fd, const uint8_t* data,
    703                                    size_t n, size_t* nwritten_out) {
    704   DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
    705   KitWriter* w =
    706       fd == 2u ? driver_stderr_writer(b->env) : driver_stdout_writer(b->env);
    707   KitStatus st;
    708   if (!w) return KIT_NOMEM;
    709   st = kit_writer_write(w, data, n);
    710   if (st == KIT_OK) st = kit_writer_status(w);
    711   kit_writer_close(w);
    712   if (nwritten_out) *nwritten_out = st == KIT_OK ? n : 0u;
    713   return st;
    714 }
    715 
    716 static KitStatus wasm_driver_clock(void* user, uint32_t clock_id,
    717                                    uint64_t* ns_out) {
    718   DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
    719   if (b->opts->clock_mode == DRIVER_WASM_CLOCK_MONOTONIC && clock_id == 1u) {
    720     *ns_out = driver_now_ns();
    721     return KIT_OK;
    722   }
    723   if (b->opts->clock_mode == DRIVER_WASM_CLOCK_REALTIME && clock_id == 0u &&
    724       b->env->now >= 0) {
    725     *ns_out = (uint64_t)b->env->now * 1000000000ull;
    726     return KIT_OK;
    727   }
    728   return KIT_UNSUPPORTED;
    729 }
    730 
    731 static uint64_t wasm_seed_hash(const char* s) {
    732   uint64_t h = 1469598103934665603ull;
    733   if (!s || !*s) return 0x9e3779b97f4a7c15ull;
    734   while (*s) {
    735     h ^= (uint8_t)*s++;
    736     h *= 1099511628211ull;
    737   }
    738   return h ? h : 0x9e3779b97f4a7c15ull;
    739 }
    740 
    741 static uint64_t wasm_seed_next(DriverWasmHostBuild* b) {
    742   uint64_t x = b->random_state;
    743   x ^= x >> 12;
    744   x ^= x << 25;
    745   x ^= x >> 27;
    746   b->random_state = x;
    747   return x * 2685821657736338717ull;
    748 }
    749 
    750 static KitStatus wasm_driver_random(void* user, uint8_t* dst, size_t n) {
    751   DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
    752   size_t i;
    753   if (b->opts->random_mode == DRIVER_WASM_RANDOM_HOST)
    754     return driver_random_bytes(dst, n) == 0 ? KIT_OK : KIT_ERR;
    755   if (b->opts->random_mode != DRIVER_WASM_RANDOM_SEED) return KIT_UNSUPPORTED;
    756   for (i = 0; i < n; ++i) {
    757     if ((i & 7u) == 0) {
    758       uint64_t x = wasm_seed_next(b);
    759       size_t j;
    760       for (j = 0; j < 8u && i + j < n; ++j)
    761         dst[i + j] = (uint8_t)(x >> (j * 8u));
    762     }
    763   }
    764   return KIT_OK;
    765 }
    766 
    767 static KitStatus wasm_driver_stat_path(void* user, const char* path,
    768                                        uint64_t* out_size,
    769                                        uint64_t* out_mtime_ns,
    770                                        uint8_t* out_filetype) {
    771   int r;
    772   (void)user;
    773   r = driver_path_stat(path, out_size, out_mtime_ns, out_filetype);
    774   if (r == 0) return KIT_OK;
    775   if (r == 1) return KIT_NOT_FOUND;
    776   return KIT_IO;
    777 }
    778 
    779 static KitStatus wasm_driver_open_dir(void* user, const char* path,
    780                                       void** out_handle) {
    781   DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
    782   DriverDirHandle* h = driver_open_dir(b->env, path);
    783   if (!h) return KIT_IO;
    784   *out_handle = h;
    785   return KIT_OK;
    786 }
    787 
    788 static KitStatus wasm_driver_read_dir_entry(void* user, void* handle,
    789                                             uint64_t index,
    790                                             KitWasmDirEntry* out) {
    791   const char* name = NULL;
    792   uint32_t name_len = 0;
    793   uint64_t ino = 0, size = 0, mtime_ns = 0;
    794   uint8_t filetype = 0;
    795   int r;
    796   (void)user;
    797   r = driver_read_dir_entry((DriverDirHandle*)handle, index, &name, &name_len,
    798                             &ino, &size, &mtime_ns, &filetype);
    799   if (r == 1) return KIT_NOT_FOUND;
    800   if (r != 0) return KIT_IO;
    801   out->ino = ino;
    802   out->size = size;
    803   out->mtime_ns = mtime_ns;
    804   out->name = name;
    805   out->name_len = name_len;
    806   out->filetype = filetype;
    807   return KIT_OK;
    808 }
    809 
    810 static void wasm_driver_close_dir(void* user, void* handle) {
    811   DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
    812   driver_close_dir(b->env, (DriverDirHandle*)handle);
    813 }
    814 
    815 static void wasm_build_release(DriverWasmHostBuild* b) {
    816   uint32_t i;
    817   if (!b) return;
    818   if (b->host) kit_wasm_host_free(b->host);
    819   for (i = 0; i < b->nowned; ++i) {
    820     if (b->owned[i].ptr) driver_free(b->env, b->owned[i].ptr, b->owned[i].size);
    821   }
    822   if (b->owned)
    823     driver_free(b->env, b->owned, (size_t)b->owned_cap * sizeof(*b->owned));
    824   if (b->mounts)
    825     driver_free(b->env, b->mounts, (size_t)b->mounts_cap * sizeof(*b->mounts));
    826   if (b->env_entries) {
    827     driver_free(b->env, b->env_entries,
    828                 (size_t)b->env_cap * sizeof(*b->env_entries));
    829   }
    830   memset(b, 0, sizeof *b);
    831 }
    832 
    833 static int wasm_build_host(const DriverWasmRunOptions* opts, const char* tool,
    834                            DriverWasmHostBuild* b) {
    835   KitWasmHostConfig cfg;
    836   uint64_t owned_cap64;
    837   KitStatus st;
    838   memset(b, 0, sizeof *b);
    839   memset(&cfg, 0, sizeof cfg);
    840   b->env = opts->env;
    841   b->opts = opts;
    842   b->tool = tool;
    843   owned_cap64 = (uint64_t)opts->nenv_pass +
    844                 ((uint64_t)opts->nmap_dirs + opts->nmap_files) * 2u;
    845   if (owned_cap64 > UINT32_MAX) {
    846     driver_errf(tool, "too many wasm sandbox strings");
    847     return 1;
    848   }
    849   b->owned_cap = (uint32_t)owned_cap64;
    850   if (b->owned_cap) {
    851     b->owned = (DriverWasmOwnedString*)driver_alloc_zeroed(
    852         opts->env, (size_t)b->owned_cap * sizeof(*b->owned));
    853     if (!b->owned) {
    854       driver_errf(tool, "out of memory");
    855       return 1;
    856     }
    857   }
    858 
    859   cfg.heap = opts->env->heap;
    860   cfg.flags = opts->imports == DRIVER_WASM_IMPORT_WASI
    861                   ? KIT_WASM_HOST_WASI_PREVIEW1
    862                   : 0u;
    863   cfg.max_instance_bytes = opts->max_instance_bytes;
    864   cfg.max_total_memory_bytes = opts->max_total_memory_bytes;
    865   cfg.max_memories = opts->max_memories;
    866   cfg.args = opts->args;
    867   cfg.nargs = opts->nargs;
    868   cfg.cwd = opts->cwd ? opts->cwd : "/";
    869   cfg.file_io = &opts->env->file_io;
    870   cfg.user = b;
    871   if (opts->stdio_mode == DRIVER_WASM_STDIO_INHERIT)
    872     cfg.write = wasm_driver_write;
    873   if (opts->clock_mode != DRIVER_WASM_CLOCK_NONE) cfg.clock = wasm_driver_clock;
    874   if (opts->random_mode != DRIVER_WASM_RANDOM_NONE) {
    875     cfg.random = wasm_driver_random;
    876     b->random_state = wasm_seed_hash(opts->random_seed);
    877   }
    878   cfg.stat_path = wasm_driver_stat_path;
    879   cfg.open_dir = wasm_driver_open_dir;
    880   cfg.read_dir_entry = wasm_driver_read_dir_entry;
    881   cfg.close_dir = wasm_driver_close_dir;
    882   if (wasm_build_env(b, &cfg) != 0 || wasm_build_mounts(b, &cfg) != 0) return 1;
    883   st = kit_wasm_host_new(&cfg, &b->host);
    884   if (st != KIT_OK) {
    885     driver_errf(tool, "%s", wasm_status_name(st));
    886     return 1;
    887   }
    888   return 0;
    889 }
    890 
    891 static int wasm_bind_imports(const DriverWasmRunOptions* opts, const char* tool,
    892                              KitCompiler* compiler, KitJit* jit,
    893                              DriverWasmHostBuild* build,
    894                              KitWasmInstance* inst) {
    895   KitStatus st;
    896   if (opts->imports == DRIVER_WASM_IMPORT_TEST) {
    897     st = kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0,
    898                                     wasm_test_resolve, NULL);
    899   } else if (opts->imports == DRIVER_WASM_IMPORT_WASI) {
    900     st = kit_wasm_host_bind_imports(build->host, compiler, jit, inst);
    901   } else {
    902     st = kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0, NULL, NULL);
    903   }
    904   if (st != KIT_OK) {
    905     driver_errf(tool, "%s", wasm_status_name(st));
    906     return 1;
    907   }
    908   return 0;
    909 }
    910 
    911 static int wasm_prepare_instance(const DriverWasmRunOptions* opts,
    912                                  const char* tool, KitCompiler* compiler,
    913                                  KitJit* jit, DriverWasmHostBuild* build,
    914                                  KitWasmInstance** out) {
    915   KitStatus st;
    916   *out = NULL;
    917   if (wasm_preflight_instance(opts, tool, jit) != 0) return 1;
    918   if (wasm_build_host(opts, tool, build) != 0) return 1;
    919   st = kit_wasm_instance_new(build->host, jit, out);
    920   if (st != KIT_OK) {
    921     driver_errf(tool, "%s", wasm_status_name(st));
    922     return 1;
    923   }
    924   if (wasm_bind_imports(opts, tool, compiler, jit, build, *out) != 0) {
    925     kit_wasm_instance_free(*out);
    926     *out = NULL;
    927     return 1;
    928   }
    929   return 0;
    930 }
    931 
    932 int driver_wasm_run_call_entry(const DriverWasmRunOptions* opts,
    933                                const char* tool, KitCompiler* compiler,
    934                                KitJit* jit, void* entry, int* rc_out) {
    935   void* init_sym = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init"));
    936   DriverWasmHostBuild build;
    937   KitWasmInstance* inst = NULL;
    938   union {
    939     void* p;
    940     DriverWasmInitFn fn;
    941   } init_u;
    942   union {
    943     void* p;
    944     DriverWasmMainFn fn;
    945   } entry_u;
    946   if (!init_sym) return 0;
    947   memset(&build, 0, sizeof build);
    948   if (wasm_prepare_instance(opts, tool, compiler, jit, &build, &inst) != 0) {
    949     *rc_out = 1;
    950     wasm_build_release(&build);
    951     return 1;
    952   }
    953   init_u.p = init_sym;
    954   entry_u.p = entry;
    955   /* Run init + entry under the proc_exit unwind guard so a WASI guest that
    956    * calls proc_exit unwinds cleanly with its exit code instead of returning
    957    * into the trailing `unreachable` and trapping the process. */
    958   (void)kit_wasm_instance_run_entry(inst, init_u.fn, entry_u.fn, rc_out);
    959   kit_wasm_instance_free(inst);
    960   wasm_build_release(&build);
    961   return 1;
    962 }
    963 
    964 int driver_wasm_run_call_entry_interp(const DriverWasmRunOptions* opts,
    965                                       const char* tool, KitCompiler* compiler,
    966                                       KitJit* jit, KitInterpProgram* interp,
    967                                       const char* entry_name, int* rc_out) {
    968   KitInterpFunc* init_fn =
    969       kit_interp_lookup(interp, KIT_SLICE_LIT("__kit_wasm_init"));
    970   KitInterpFunc* entry_fn =
    971       kit_interp_lookup(interp, kit_slice_cstr(entry_name));
    972   DriverWasmHostBuild build;
    973   KitWasmInstance* inst = NULL;
    974   uint64_t args[1];
    975   int64_t ret = 0;
    976   KitInterpStatus s;
    977   if (!init_fn) return 0;
    978   if (!entry_fn) {
    979     driver_errf(tool, "interp: wasm entry %.*s has no interpretable IR",
    980                 KIT_SLICE_ARG(kit_slice_cstr(entry_name)));
    981     *rc_out = 1;
    982     return 1;
    983   }
    984   memset(&build, 0, sizeof build);
    985   if (wasm_prepare_instance(opts, tool, compiler, jit, &build, &inst) != 0) {
    986     *rc_out = 1;
    987     wasm_build_release(&build);
    988     return 1;
    989   }
    990   args[0] = (uint64_t)(uintptr_t)inst;
    991   s = kit_interp_call_args(interp, init_fn, args, 1u, &ret);
    992   if (s == KIT_INTERP_DONE)
    993     s = kit_interp_call_args(interp, entry_fn, args, 1u, &ret);
    994   if (s == KIT_INTERP_DONE) {
    995     *rc_out = (int)ret;
    996     (void)kit_wasm_instance_exit_code(inst, rc_out);
    997   } else if (kit_wasm_instance_exit_code(inst, rc_out)) {
    998     /* proc_exit fired, then the trailing `unreachable` trapped the interpreter;
    999      * the recorded exit code is the real result, not an execution failure.
   1000      * (The native JIT path unwinds via kit_wasm_instance_run_entry instead.) */
   1001   } else {
   1002     driver_errf(tool, "interp: could not execute wasm entry %.*s",
   1003                 KIT_SLICE_ARG(kit_slice_cstr(entry_name)));
   1004     *rc_out = 1;
   1005   }
   1006   kit_wasm_instance_free(inst);
   1007   wasm_build_release(&build);
   1008   return 1;
   1009 }