kit

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

wasm_run.c (33173B)


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