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 }