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 }