wasm_host.c (41236B)
1 #include <kit/wasm.h> 2 #include <setjmp.h> 3 #include <stddef.h> 4 #include <stdint.h> 5 #include <string.h> 6 7 typedef struct KitWasmMemoryRecord { 8 uint8_t* data; 9 uint64_t pages; 10 uint64_t max_pages; 11 uint32_t flags; 12 } KitWasmMemoryRecord; 13 14 #define KIT_WASM_HOST_MAX_FDS 64u 15 16 typedef struct KitWasmOpenFd { 17 uint32_t used; 18 uint32_t is_dir; 19 KitFileData data; 20 const KitFileIO* io; 21 uint64_t pos; 22 void* dir_handle; 23 char* host_path; 24 size_t host_path_size; /* 0=borrowed (KIT_WASM_FS_FILE), >0=heap-owned */ 25 } KitWasmOpenFd; 26 27 struct KitWasmHost { 28 KitWasmHostConfig config; 29 }; 30 31 typedef struct KitWasmRuntimeInstance { 32 KitWasmHost* host; 33 const KitWasmMemoryLayout* memory_layouts; 34 uint64_t allocation_bytes; 35 uint64_t instance_bytes; 36 uint64_t total_memory_bytes; 37 uint8_t** memories; 38 uint64_t* memory_bytes; 39 uint32_t nmemories; 40 int exit_called; 41 int exit_code; 42 /* proc_exit unwind landing pad. Valid only while running under 43 * kit_wasm_instance_run_entry (the native JIT path); zero otherwise so the 44 * interpreter path falls through to its own trap-then-exit-code handling. */ 45 jmp_buf exit_jmp; 46 int exit_jmp_valid; 47 KitWasmOpenFd fds[KIT_WASM_HOST_MAX_FDS]; 48 uint8_t instance[]; 49 } KitWasmRuntimeInstance; 50 51 enum { 52 WASI_ESUCCESS = 0, 53 WASI_EACCES = 2, 54 WASI_EBADF = 8, 55 WASI_EISDIR = 31, 56 WASI_EINVAL = 28, 57 WASI_EIO = 29, 58 WASI_ENOMEM = 34, 59 WASI_ENOENT = 44, 60 WASI_ENOSYS = 52, 61 WASI_ENOTDIR = 54, 62 WASI_ENOTCAPABLE = 76, 63 }; 64 65 static KitWasmRuntimeInstance* wasm_wrap_from_instance(KitWasmInstance* inst) { 66 return (KitWasmRuntimeInstance*)((uint8_t*)inst - 67 offsetof(KitWasmRuntimeInstance, instance)); 68 } 69 70 static KitWasmInstance* wasm_instance_from_wrap(KitWasmRuntimeInstance* w) { 71 return (KitWasmInstance*)w->instance; 72 } 73 74 static size_t wasm_cstrlen(const char* s) { 75 size_t n = 0; 76 if (!s) return 0; 77 while (s[n]) ++n; 78 return n; 79 } 80 81 static int wasm_streq(const char* a, const char* b) { 82 size_t i = 0; 83 if (!a || !b) return 0; 84 while (a[i] && b[i]) { 85 if (a[i] != b[i]) return 0; 86 ++i; 87 } 88 return a[i] == b[i]; 89 } 90 91 static KitStatus wasm_u64_to_size(uint64_t n, size_t* out) { 92 if (n > (uint64_t)SIZE_MAX) return KIT_INVALID; 93 *out = (size_t)n; 94 return KIT_OK; 95 } 96 97 static KitStatus wasm_page_bytes(uint64_t pages, uint64_t* out) { 98 if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) return KIT_INVALID; 99 *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE; 100 return KIT_OK; 101 } 102 103 static uint32_t wasm_preopen_count(const KitWasmHost* host) { 104 return host ? host->config.nmounts : 0u; 105 } 106 107 static const KitWasmFsMount* wasm_preopen_for_fd(KitWasmRuntimeInstance* w, 108 uint32_t fd) { 109 uint32_t index; 110 if (!w || !w->host || fd < 3u) return NULL; 111 index = fd - 3u; 112 if (index >= w->host->config.nmounts) return NULL; 113 return &w->host->config.mounts[index]; 114 } 115 116 static size_t wasm_guest_parent_len(const char* path) { 117 size_t i; 118 size_t last = 0; 119 if (!path || path[0] != '/') return 0; 120 for (i = 1; path[i]; ++i) { 121 if (path[i] == '/') last = i; 122 } 123 return last ? last : 1u; 124 } 125 126 static const char* wasm_guest_basename(const char* path) { 127 size_t i; 128 size_t last = 0; 129 if (!path) return NULL; 130 for (i = 0; path[i]; ++i) { 131 if (path[i] == '/') last = i + 1u; 132 } 133 return path + last; 134 } 135 136 static int wasm_bytes_eq_cstr(const uint8_t* a, uint32_t alen, const char* b) { 137 uint32_t i; 138 if (!a || !b) return 0; 139 for (i = 0; i < alen; ++i) { 140 if (b[i] == '\0' || a[i] != (uint8_t)b[i]) return 0; 141 } 142 return b[alen] == '\0'; 143 } 144 145 static KitWasmMemoryRecord* wasm_memory0(KitWasmRuntimeInstance* w) { 146 if (!w || w->nmemories == 0 || !w->memory_layouts) return NULL; 147 if (w->memory_layouts[0].offset > w->instance_bytes) return NULL; 148 if (w->instance_bytes - w->memory_layouts[0].offset < 149 sizeof(KitWasmMemoryRecord)) 150 return NULL; 151 return (KitWasmMemoryRecord*)(w->instance + w->memory_layouts[0].offset); 152 } 153 154 static int wasm_mem_ptr(KitWasmInstance* inst, uint32_t addr, uint32_t n, 155 uint8_t** out) { 156 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 157 KitWasmMemoryRecord* mem = wasm_memory0(w); 158 uint64_t end; 159 uint64_t size; 160 if (!out || !mem || !mem->data) return 0; 161 if (addr > UINT32_MAX - n) return 0; 162 end = (uint64_t)addr + (uint64_t)n; 163 if (wasm_page_bytes(mem->pages, &size) != KIT_OK) return 0; 164 if (end > size) return 0; 165 *out = mem->data + addr; 166 return 1; 167 } 168 169 static int wasm_read_u32(KitWasmInstance* inst, uint32_t addr, uint32_t* out) { 170 uint8_t* p; 171 if (!wasm_mem_ptr(inst, addr, 4u, &p)) return 0; 172 *out = ((uint32_t)p[0]) | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | 173 ((uint32_t)p[3] << 24); 174 return 1; 175 } 176 177 static int wasm_write_u32(KitWasmInstance* inst, uint32_t addr, uint32_t v) { 178 uint8_t* p; 179 if (!wasm_mem_ptr(inst, addr, 4u, &p)) return 0; 180 p[0] = (uint8_t)(v & 0xffu); 181 p[1] = (uint8_t)((v >> 8) & 0xffu); 182 p[2] = (uint8_t)((v >> 16) & 0xffu); 183 p[3] = (uint8_t)((v >> 24) & 0xffu); 184 return 1; 185 } 186 187 static int wasm_write_u64(KitWasmInstance* inst, uint32_t addr, uint64_t v) { 188 uint8_t* p; 189 if (!wasm_mem_ptr(inst, addr, 8u, &p)) return 0; 190 p[0] = (uint8_t)(v & 0xffu); 191 p[1] = (uint8_t)((v >> 8) & 0xffu); 192 p[2] = (uint8_t)((v >> 16) & 0xffu); 193 p[3] = (uint8_t)((v >> 24) & 0xffu); 194 p[4] = (uint8_t)((v >> 32) & 0xffu); 195 p[5] = (uint8_t)((v >> 40) & 0xffu); 196 p[6] = (uint8_t)((v >> 48) & 0xffu); 197 p[7] = (uint8_t)((v >> 56) & 0xffu); 198 return 1; 199 } 200 201 static void wasm_put_u64_le(uint8_t* buf, uint32_t off, uint64_t v) { 202 buf[off + 0] = (uint8_t)(v); 203 buf[off + 1] = (uint8_t)(v >> 8); 204 buf[off + 2] = (uint8_t)(v >> 16); 205 buf[off + 3] = (uint8_t)(v >> 24); 206 buf[off + 4] = (uint8_t)(v >> 32); 207 buf[off + 5] = (uint8_t)(v >> 40); 208 buf[off + 6] = (uint8_t)(v >> 48); 209 buf[off + 7] = (uint8_t)(v >> 56); 210 } 211 212 static void wasm_put_u32_le(uint8_t* buf, uint32_t off, uint32_t v) { 213 buf[off + 0] = (uint8_t)(v); 214 buf[off + 1] = (uint8_t)(v >> 8); 215 buf[off + 2] = (uint8_t)(v >> 16); 216 buf[off + 3] = (uint8_t)(v >> 24); 217 } 218 219 /* wasi_filestat_t: dev(u64@0) ino(u64@8) filetype(u8@16)+7pad nlink(u64@24) 220 * size(u64@32) atim(u64@40) mtim(u64@48) ctim(u64@56) — 64 bytes total */ 221 static int wasm_write_filestat(KitWasmInstance* inst, uint32_t ptr, 222 uint8_t filetype, uint64_t size, 223 uint64_t mtime_ns) { 224 uint8_t* p; 225 if (!wasm_mem_ptr(inst, ptr, 64u, &p)) return 0; 226 memset(p, 0, 64u); 227 p[16] = filetype; 228 wasm_put_u64_le(p, 32u, size); 229 wasm_put_u64_le(p, 40u, mtime_ns); 230 wasm_put_u64_le(p, 48u, mtime_ns); 231 wasm_put_u64_le(p, 56u, mtime_ns); 232 return 1; 233 } 234 235 static int wasm_status_errno(KitStatus st) { 236 switch (st) { 237 case KIT_OK: 238 return WASI_ESUCCESS; 239 case KIT_NOMEM: 240 return WASI_ENOMEM; 241 case KIT_NOT_FOUND: 242 return WASI_ENOENT; 243 case KIT_IO: 244 return WASI_EIO; 245 case KIT_UNSUPPORTED: 246 return WASI_ENOSYS; 247 case KIT_INVALID: 248 case KIT_MALFORMED: 249 case KIT_AMBIGUOUS: 250 case KIT_ERR: 251 default: 252 return WASI_EINVAL; 253 } 254 } 255 256 static int wasm_sig(const KitWasmImportType* t, const KitWasmValType* params, 257 uint32_t nparams, const KitWasmValType* results, 258 uint32_t nresults) { 259 uint32_t i; 260 if (!t || t->nparams != nparams || t->nresults != nresults) return 0; 261 for (i = 0; i < nparams; ++i) 262 if (!t->params || t->params[i] != params[i]) return 0; 263 for (i = 0; i < nresults; ++i) 264 if (!t->results || t->results[i] != results[i]) return 0; 265 return 1; 266 } 267 268 static void wasi_proc_exit(KitWasmInstance* inst, int32_t code) { 269 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 270 w->exit_called = 1; 271 w->exit_code = code; 272 /* proc_exit is _Noreturn: toolchains emit `unreachable` right after the call. 273 * Under the native JIT path a setjmp landing pad is armed, so unwind there 274 * instead of returning into the guest (which would hit that `unreachable` and 275 * trap). The interpreter path leaves exit_jmp_valid clear and recovers the 276 * exit code from the resulting trap instead. */ 277 if (w->exit_jmp_valid) { 278 w->exit_jmp_valid = 0; 279 longjmp(w->exit_jmp, 1); 280 } 281 } 282 283 static int32_t wasi_fd_write(KitWasmInstance* inst, int32_t fd_i, 284 int32_t iovs_i, int32_t iovs_len_i, 285 int32_t nwritten_i) { 286 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 287 uint32_t fd = (uint32_t)fd_i; 288 uint32_t iovs = (uint32_t)iovs_i; 289 uint32_t iovs_len = (uint32_t)iovs_len_i; 290 uint32_t nwritten = (uint32_t)nwritten_i; 291 uint32_t total = 0; 292 uint32_t i; 293 if (fd != 1u && fd != 2u) return WASI_EBADF; 294 for (i = 0; i < iovs_len; ++i) { 295 uint32_t ptr; 296 uint32_t len; 297 uint8_t* data; 298 uint32_t off; 299 uint32_t rec; 300 size_t wrote = 0; 301 KitStatus st; 302 if (i > UINT32_MAX / 8u) return WASI_EINVAL; 303 off = i * 8u; 304 if (iovs > UINT32_MAX - off) return WASI_EINVAL; 305 rec = iovs + off; 306 if (!wasm_read_u32(inst, rec, &ptr) || 307 !wasm_read_u32(inst, rec + 4u, &len) || 308 !wasm_mem_ptr(inst, ptr, len, &data)) 309 return WASI_EINVAL; 310 if (w->host->config.write) { 311 st = w->host->config.write(w->host->config.user, fd, data, len, &wrote); 312 if (st != KIT_OK) return wasm_status_errno(st); 313 if (wrote > len) wrote = len; 314 } else { 315 wrote = len; 316 } 317 if (total > UINT32_MAX - (uint32_t)wrote) return WASI_EINVAL; 318 total += (uint32_t)wrote; 319 if (wrote < len) break; 320 } 321 if (!wasm_write_u32(inst, nwritten, total)) return WASI_EINVAL; 322 return WASI_ESUCCESS; 323 } 324 325 static int wasm_strings_size(const char* const* items, uint32_t n, 326 uint32_t* bytes_out) { 327 uint64_t total = 0; 328 uint32_t i; 329 for (i = 0; i < n; ++i) { 330 total += (uint64_t)wasm_cstrlen(items ? items[i] : NULL) + 1u; 331 if (total > UINT32_MAX) return 0; 332 } 333 *bytes_out = (uint32_t)total; 334 return 1; 335 } 336 337 static int32_t wasi_environ_sizes_get(KitWasmInstance* inst, int32_t count_i, 338 int32_t size_i) { 339 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 340 uint32_t bytes; 341 if (!wasm_strings_size(w->host->config.env, w->host->config.nenv, &bytes)) 342 return WASI_EINVAL; 343 if (!wasm_write_u32(inst, (uint32_t)count_i, w->host->config.nenv) || 344 !wasm_write_u32(inst, (uint32_t)size_i, bytes)) 345 return WASI_EINVAL; 346 return WASI_ESUCCESS; 347 } 348 349 static int wasm_write_strings(KitWasmInstance* inst, const char* const* items, 350 uint32_t nitems, uint32_t ptrs_addr, 351 uint32_t buf_addr) { 352 uint32_t total; 353 uint8_t* ptrs; 354 uint8_t* buf; 355 uint32_t off = 0; 356 uint32_t i; 357 if (!wasm_strings_size(items, nitems, &total)) return 0; 358 if (nitems > UINT32_MAX / 4u) return 0; 359 if (!wasm_mem_ptr(inst, ptrs_addr, nitems * 4u, &ptrs) || 360 !wasm_mem_ptr(inst, buf_addr, total, &buf)) 361 return 0; 362 (void)ptrs; 363 for (i = 0; i < nitems; ++i) { 364 const char* s = items ? items[i] : NULL; 365 size_t len = wasm_cstrlen(s); 366 if (len > (size_t)(UINT32_MAX - off - 1u)) return 0; 367 if (!wasm_write_u32(inst, ptrs_addr + i * 4u, buf_addr + off)) return 0; 368 if (len) memcpy(buf + off, s, len); 369 buf[off + (uint32_t)len] = 0; 370 off += (uint32_t)len + 1u; 371 } 372 return 1; 373 } 374 375 static int32_t wasi_environ_get(KitWasmInstance* inst, int32_t env_i, 376 int32_t buf_i) { 377 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 378 return wasm_write_strings(inst, w->host->config.env, w->host->config.nenv, 379 (uint32_t)env_i, (uint32_t)buf_i) 380 ? WASI_ESUCCESS 381 : WASI_EINVAL; 382 } 383 384 static int32_t wasi_args_sizes_get(KitWasmInstance* inst, int32_t count_i, 385 int32_t size_i) { 386 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 387 uint32_t bytes; 388 if (!wasm_strings_size(w->host->config.args, w->host->config.nargs, &bytes)) 389 return WASI_EINVAL; 390 if (!wasm_write_u32(inst, (uint32_t)count_i, w->host->config.nargs) || 391 !wasm_write_u32(inst, (uint32_t)size_i, bytes)) 392 return WASI_EINVAL; 393 return WASI_ESUCCESS; 394 } 395 396 static int32_t wasi_args_get(KitWasmInstance* inst, int32_t args_i, 397 int32_t buf_i) { 398 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 399 return wasm_write_strings(inst, w->host->config.args, w->host->config.nargs, 400 (uint32_t)args_i, (uint32_t)buf_i) 401 ? WASI_ESUCCESS 402 : WASI_EINVAL; 403 } 404 405 static int32_t wasi_random_get(KitWasmInstance* inst, int32_t ptr_i, 406 int32_t len_i) { 407 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 408 uint8_t* dst; 409 KitStatus st; 410 if (!w->host->config.random) return WASI_ENOSYS; 411 if (!wasm_mem_ptr(inst, (uint32_t)ptr_i, (uint32_t)len_i, &dst)) 412 return WASI_EINVAL; 413 st = w->host->config.random(w->host->config.user, dst, (size_t)len_i); 414 return wasm_status_errno(st); 415 } 416 417 static int32_t wasi_clock_time_get(KitWasmInstance* inst, int32_t clock_id_i, 418 int64_t precision_i, int32_t time_i) { 419 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 420 uint64_t ns = 0; 421 KitStatus st; 422 (void)precision_i; 423 if (!w->host->config.clock) return WASI_ENOSYS; 424 st = w->host->config.clock(w->host->config.user, (uint32_t)clock_id_i, &ns); 425 if (st != KIT_OK) return wasm_status_errno(st); 426 return wasm_write_u64(inst, (uint32_t)time_i, ns) ? WASI_ESUCCESS 427 : WASI_EINVAL; 428 } 429 430 static int32_t wasi_fd_prestat_get(KitWasmInstance* inst, int32_t fd_i, 431 int32_t prestat_i) { 432 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 433 const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)fd_i); 434 uint8_t* p; 435 uint32_t len; 436 if (!m) return WASI_EBADF; 437 len = (m->flags & KIT_WASM_FS_FILE) 438 ? (uint32_t)wasm_guest_parent_len(m->guest_path) 439 : (uint32_t)wasm_cstrlen(m->guest_path); 440 if (!wasm_mem_ptr(inst, (uint32_t)prestat_i, 8u, &p)) return WASI_EINVAL; 441 p[0] = 0; 442 p[1] = 0; 443 p[2] = 0; 444 p[3] = 0; 445 p[4] = (uint8_t)(len & 0xffu); 446 p[5] = (uint8_t)((len >> 8) & 0xffu); 447 p[6] = (uint8_t)((len >> 16) & 0xffu); 448 p[7] = (uint8_t)((len >> 24) & 0xffu); 449 return WASI_ESUCCESS; 450 } 451 452 static int32_t wasi_fd_prestat_dir_name(KitWasmInstance* inst, int32_t fd_i, 453 int32_t path_i, int32_t path_len_i) { 454 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 455 const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)fd_i); 456 uint8_t* dst; 457 size_t len; 458 if (!m) return WASI_EBADF; 459 len = (m->flags & KIT_WASM_FS_FILE) ? wasm_guest_parent_len(m->guest_path) 460 : wasm_cstrlen(m->guest_path); 461 if ((uint32_t)path_len_i < len) return WASI_EINVAL; 462 if (!wasm_mem_ptr(inst, (uint32_t)path_i, (uint32_t)len, &dst)) 463 return WASI_EINVAL; 464 if (len) memcpy(dst, m->guest_path, len); 465 return WASI_ESUCCESS; 466 } 467 468 static int wasm_guest_path_safe(const uint8_t* path, uint32_t len) { 469 uint32_t i = 0; 470 if (!path || len == 0 || path[0] == '/') return 0; 471 while (i < len) { 472 uint32_t start = i; 473 while (i < len && path[i] != '/') { 474 if (path[i] == '\\') return 0; 475 ++i; 476 } 477 if (i == start) return 0; 478 if (i - start == 2u && path[start] == '.' && path[start + 1u] == '.') 479 return 0; 480 if (i < len) ++i; 481 } 482 return 1; 483 } 484 485 static KitStatus wasm_join_path(KitHeap* heap, const char* base, 486 const uint8_t* rel, uint32_t rel_len, 487 char** out, size_t* out_size) { 488 size_t base_len = wasm_cstrlen(base); 489 size_t need; 490 char* p; 491 int slash; 492 if (!base || !rel || !out || !out_size) return KIT_INVALID; 493 slash = base_len && base[base_len - 1u] != '/' && base[base_len - 1u] != '\\'; 494 if (base_len > SIZE_MAX - (size_t)rel_len - (slash ? 2u : 1u)) 495 return KIT_INVALID; 496 need = base_len + (slash ? 1u : 0u) + (size_t)rel_len + 1u; 497 p = (char*)heap->alloc(heap, need, 1u); 498 if (!p) return KIT_NOMEM; 499 if (base_len) memcpy(p, base, base_len); 500 if (slash) p[base_len++] = '/'; 501 if (rel_len) memcpy(p + base_len, rel, rel_len); 502 p[base_len + (size_t)rel_len] = 0; 503 *out = p; 504 *out_size = need; 505 return KIT_OK; 506 } 507 508 static int wasm_find_free_fd(KitWasmRuntimeInstance* w, uint32_t* out) { 509 uint32_t start = 3u + wasm_preopen_count(w->host); 510 uint32_t fd; 511 if (start >= KIT_WASM_HOST_MAX_FDS) return 0; 512 for (fd = start; fd < KIT_WASM_HOST_MAX_FDS; ++fd) { 513 if (!w->fds[fd].used) { 514 *out = fd; 515 return 1; 516 } 517 } 518 return 0; 519 } 520 521 static void wasm_fd_close_slot(KitWasmRuntimeInstance* w, uint32_t fd) { 522 KitHeap* heap = w->host->config.heap; 523 if (!w->fds[fd].used) return; 524 if (w->fds[fd].is_dir) { 525 if (w->fds[fd].dir_handle && w->host->config.close_dir) 526 w->host->config.close_dir(w->host->config.user, w->fds[fd].dir_handle); 527 } else { 528 if (w->fds[fd].io && w->fds[fd].io->release) 529 w->fds[fd].io->release(w->fds[fd].io->user, &w->fds[fd].data); 530 } 531 if (w->fds[fd].host_path_size > 0) 532 heap->free(heap, w->fds[fd].host_path, w->fds[fd].host_path_size); 533 memset(&w->fds[fd], 0, sizeof w->fds[fd]); 534 } 535 536 static int32_t wasi_path_open(KitWasmInstance* inst, int32_t dirfd_i, 537 int32_t dirflags_i, int32_t path_i, 538 int32_t path_len_i, int32_t oflags_i, 539 int64_t rights_base_i, 540 int64_t rights_inheriting_i, int32_t fdflags_i, 541 int32_t opened_fd_i) { 542 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 543 const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)dirfd_i); 544 uint8_t* guest_path; 545 char* host_path = NULL; 546 size_t host_path_size = 0; 547 uint32_t fd; 548 KitStatus st; 549 int open_as_dir; 550 (void)dirflags_i; 551 (void)rights_base_i; 552 (void)rights_inheriting_i; 553 (void)fdflags_i; 554 if (!m) return WASI_EBADF; 555 if (!(m->flags & KIT_WASM_FS_READ)) return WASI_EACCES; 556 /* OFLAGS_DIRECTORY=4; CREAT/EXCL/TRUNC not supported */ 557 if (oflags_i & ~4) return WASI_ENOSYS; 558 open_as_dir = (oflags_i & 4) != 0; 559 if (!open_as_dir && 560 (!w->host->config.file_io || !w->host->config.file_io->read_all)) 561 return WASI_ENOSYS; 562 if (!wasm_mem_ptr(inst, (uint32_t)path_i, (uint32_t)path_len_i, &guest_path)) 563 return WASI_EINVAL; 564 if (!wasm_guest_path_safe(guest_path, (uint32_t)path_len_i)) 565 return WASI_ENOTCAPABLE; 566 if (m->flags & KIT_WASM_FS_FILE) { 567 const char* base = wasm_guest_basename(m->guest_path); 568 if (!base || !*base || 569 !wasm_bytes_eq_cstr(guest_path, (uint32_t)path_len_i, base)) 570 return WASI_ENOENT; 571 host_path = (char*)m->host_path; /* borrowed */ 572 } else { 573 st = wasm_join_path(w->host->config.heap, m->host_path, guest_path, 574 (uint32_t)path_len_i, &host_path, &host_path_size); 575 if (st != KIT_OK) return wasm_status_errno(st); 576 } 577 if (open_as_dir) { 578 void* dir_handle = NULL; 579 if (!w->host->config.open_dir) { 580 if (host_path_size > 0) 581 w->host->config.heap->free(w->host->config.heap, host_path, 582 host_path_size); 583 return WASI_ENOSYS; 584 } 585 st = w->host->config.open_dir(w->host->config.user, host_path, &dir_handle); 586 if (st != KIT_OK) { 587 if (host_path_size > 0) 588 w->host->config.heap->free(w->host->config.heap, host_path, 589 host_path_size); 590 return wasm_status_errno(st); 591 } 592 if (!wasm_find_free_fd(w, &fd)) { 593 if (w->host->config.close_dir) 594 w->host->config.close_dir(w->host->config.user, dir_handle); 595 if (host_path_size > 0) 596 w->host->config.heap->free(w->host->config.heap, host_path, 597 host_path_size); 598 return WASI_ENOMEM; 599 } 600 w->fds[fd].used = 1; 601 w->fds[fd].is_dir = 1; 602 w->fds[fd].dir_handle = dir_handle; 603 w->fds[fd].host_path = host_path; 604 w->fds[fd].host_path_size = host_path_size; 605 if (!wasm_write_u32(inst, (uint32_t)opened_fd_i, fd)) return WASI_EINVAL; 606 return WASI_ESUCCESS; 607 } else { 608 KitFileData data; 609 memset(&data, 0, sizeof data); 610 st = w->host->config.file_io->read_all(w->host->config.file_io->user, 611 host_path, &data); 612 if (st != KIT_OK) { 613 if (host_path_size > 0) 614 w->host->config.heap->free(w->host->config.heap, host_path, 615 host_path_size); 616 return wasm_status_errno(st); 617 } 618 if (!wasm_find_free_fd(w, &fd)) { 619 if (w->host->config.file_io->release) 620 w->host->config.file_io->release(w->host->config.file_io->user, &data); 621 if (host_path_size > 0) 622 w->host->config.heap->free(w->host->config.heap, host_path, 623 host_path_size); 624 return WASI_ENOMEM; 625 } 626 w->fds[fd].used = 1; 627 w->fds[fd].data = data; 628 w->fds[fd].io = w->host->config.file_io; 629 w->fds[fd].pos = 0; 630 w->fds[fd].host_path = host_path; 631 w->fds[fd].host_path_size = host_path_size; 632 if (!wasm_write_u32(inst, (uint32_t)opened_fd_i, fd)) return WASI_EINVAL; 633 return WASI_ESUCCESS; 634 } 635 } 636 637 static int32_t wasi_fd_read(KitWasmInstance* inst, int32_t fd_i, int32_t iovs_i, 638 int32_t iovs_len_i, int32_t nread_i) { 639 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 640 uint32_t fd = (uint32_t)fd_i; 641 uint32_t iovs = (uint32_t)iovs_i; 642 uint32_t iovs_len = (uint32_t)iovs_len_i; 643 uint32_t total = 0; 644 uint32_t i; 645 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 646 if (w->fds[fd].is_dir) return WASI_EISDIR; 647 for (i = 0; i < iovs_len; ++i) { 648 uint32_t ptr; 649 uint32_t len; 650 uint8_t* dst; 651 uint32_t off; 652 uint32_t rec; 653 uint64_t remain64; 654 uint32_t ncopy; 655 if (i > UINT32_MAX / 8u) return WASI_EINVAL; 656 off = i * 8u; 657 if (iovs > UINT32_MAX - off) return WASI_EINVAL; 658 rec = iovs + off; 659 if (!wasm_read_u32(inst, rec, &ptr) || 660 !wasm_read_u32(inst, rec + 4u, &len) || 661 !wasm_mem_ptr(inst, ptr, len, &dst)) 662 return WASI_EINVAL; 663 if (w->fds[fd].pos >= (uint64_t)w->fds[fd].data.size) break; 664 remain64 = (uint64_t)w->fds[fd].data.size - w->fds[fd].pos; 665 ncopy = len; 666 if ((uint64_t)ncopy > remain64) ncopy = (uint32_t)remain64; 667 if (ncopy) 668 memcpy(dst, w->fds[fd].data.data + (size_t)w->fds[fd].pos, ncopy); 669 w->fds[fd].pos += ncopy; 670 if (total > UINT32_MAX - ncopy) return WASI_EINVAL; 671 total += ncopy; 672 if (ncopy < len) break; 673 } 674 if (!wasm_write_u32(inst, (uint32_t)nread_i, total)) return WASI_EINVAL; 675 return WASI_ESUCCESS; 676 } 677 678 static int32_t wasi_fd_close(KitWasmInstance* inst, int32_t fd_i) { 679 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 680 uint32_t fd = (uint32_t)fd_i; 681 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 682 wasm_fd_close_slot(w, fd); 683 return WASI_ESUCCESS; 684 } 685 686 static int32_t wasi_fd_seek(KitWasmInstance* inst, int32_t fd_i, int64_t offset, 687 int32_t whence_i, int32_t newoffset_i) { 688 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 689 uint32_t fd = (uint32_t)fd_i; 690 int64_t base; 691 int64_t newpos; 692 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 693 if (w->fds[fd].is_dir) return WASI_EISDIR; 694 switch (whence_i) { 695 case 0: 696 base = 0; 697 break; 698 case 1: 699 base = (int64_t)w->fds[fd].pos; 700 break; 701 case 2: 702 base = (int64_t)(uint64_t)w->fds[fd].data.size; 703 break; 704 default: 705 return WASI_EINVAL; 706 } 707 if (offset < 0 && (uint64_t)(-offset) > (uint64_t)base) return WASI_EINVAL; 708 newpos = base + offset; 709 if (newpos < 0) return WASI_EINVAL; 710 if ((uint64_t)newpos > (uint64_t)w->fds[fd].data.size) 711 newpos = (int64_t)(uint64_t)w->fds[fd].data.size; 712 w->fds[fd].pos = (uint64_t)newpos; 713 return wasm_write_u64(inst, (uint32_t)newoffset_i, (uint64_t)newpos) 714 ? WASI_ESUCCESS 715 : WASI_EINVAL; 716 } 717 718 static int32_t wasi_fd_tell(KitWasmInstance* inst, int32_t fd_i, 719 int32_t offset_i) { 720 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 721 uint32_t fd = (uint32_t)fd_i; 722 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 723 if (w->fds[fd].is_dir) return WASI_EISDIR; 724 return wasm_write_u64(inst, (uint32_t)offset_i, w->fds[fd].pos) 725 ? WASI_ESUCCESS 726 : WASI_EINVAL; 727 } 728 729 static int32_t wasi_fd_filestat_get(KitWasmInstance* inst, int32_t fd_i, 730 int32_t filestat_i) { 731 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 732 uint32_t fd = (uint32_t)fd_i; 733 uint32_t npreopen = wasm_preopen_count(w->host); 734 uint64_t size = 0, mtime_ns = 0; 735 uint8_t ft; 736 if (fd <= 2u) 737 return wasm_write_filestat(inst, (uint32_t)filestat_i, 738 KIT_WASM_FILETYPE_CHARACTER_DEVICE, 0u, 0u) 739 ? WASI_ESUCCESS 740 : WASI_EINVAL; 741 if (fd >= 3u && fd < 3u + npreopen) { 742 const KitWasmFsMount* m = wasm_preopen_for_fd(w, fd); 743 ft = (m && (m->flags & KIT_WASM_FS_FILE)) ? KIT_WASM_FILETYPE_REGULAR_FILE 744 : KIT_WASM_FILETYPE_DIRECTORY; 745 return wasm_write_filestat(inst, (uint32_t)filestat_i, ft, 0u, 0u) 746 ? WASI_ESUCCESS 747 : WASI_EINVAL; 748 } 749 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 750 ft = w->fds[fd].is_dir ? KIT_WASM_FILETYPE_DIRECTORY 751 : KIT_WASM_FILETYPE_REGULAR_FILE; 752 if (w->host->config.stat_path && w->fds[fd].host_path) { 753 w->host->config.stat_path(w->host->config.user, w->fds[fd].host_path, &size, 754 &mtime_ns, &ft); 755 } else if (!w->fds[fd].is_dir) { 756 size = (uint64_t)w->fds[fd].data.size; 757 } 758 return wasm_write_filestat(inst, (uint32_t)filestat_i, ft, size, mtime_ns) 759 ? WASI_ESUCCESS 760 : WASI_EINVAL; 761 } 762 763 static int32_t wasi_path_filestat_get(KitWasmInstance* inst, int32_t dirfd_i, 764 int32_t flags_i, int32_t path_i, 765 int32_t path_len_i, int32_t filestat_i) { 766 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 767 const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)dirfd_i); 768 uint8_t* guest_path; 769 char* host_path = NULL; 770 size_t host_path_size = 0; 771 uint64_t size = 0, mtime_ns = 0; 772 uint8_t ft = KIT_WASM_FILETYPE_UNKNOWN; 773 KitStatus st; 774 (void)flags_i; 775 if (!m) return WASI_EBADF; 776 if (!w->host->config.stat_path) return WASI_ENOSYS; 777 if (!wasm_mem_ptr(inst, (uint32_t)path_i, (uint32_t)path_len_i, &guest_path)) 778 return WASI_EINVAL; 779 if (!wasm_guest_path_safe(guest_path, (uint32_t)path_len_i)) 780 return WASI_ENOTCAPABLE; 781 if (m->flags & KIT_WASM_FS_FILE) { 782 const char* base = wasm_guest_basename(m->guest_path); 783 if (!base || !*base || 784 !wasm_bytes_eq_cstr(guest_path, (uint32_t)path_len_i, base)) 785 return WASI_ENOENT; 786 host_path = (char*)m->host_path; 787 } else { 788 st = wasm_join_path(w->host->config.heap, m->host_path, guest_path, 789 (uint32_t)path_len_i, &host_path, &host_path_size); 790 if (st != KIT_OK) return wasm_status_errno(st); 791 } 792 st = w->host->config.stat_path(w->host->config.user, host_path, &size, 793 &mtime_ns, &ft); 794 if (host_path_size > 0) 795 w->host->config.heap->free(w->host->config.heap, host_path, host_path_size); 796 if (st != KIT_OK) return wasm_status_errno(st); 797 return wasm_write_filestat(inst, (uint32_t)filestat_i, ft, size, mtime_ns) 798 ? WASI_ESUCCESS 799 : WASI_EINVAL; 800 } 801 802 /* wasi_dirent_t: d_next(u64@0) ino(u64@8) namlen(u32@16) type(u8@20) 3pad 803 * then namlen bytes of name (no NUL). Cookie = zero-based entry index. */ 804 static int32_t wasi_fd_readdir(KitWasmInstance* inst, int32_t fd_i, 805 int32_t buf_i, int32_t buf_len_i, 806 int64_t cookie_i, int32_t bufused_i) { 807 KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst); 808 uint32_t fd = (uint32_t)fd_i; 809 uint32_t buf_len = (uint32_t)buf_len_i; 810 uint64_t cookie = (uint64_t)cookie_i; 811 uint8_t* buf; 812 uint32_t written = 0; 813 if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF; 814 if (!w->fds[fd].is_dir) return WASI_ENOTDIR; 815 if (!w->host->config.read_dir_entry) return WASI_ENOSYS; 816 if (!wasm_mem_ptr(inst, (uint32_t)buf_i, buf_len, &buf)) return WASI_EINVAL; 817 while (written < buf_len) { 818 KitWasmDirEntry entry; 819 uint8_t hdr[24]; 820 uint32_t remain = buf_len - written; 821 uint32_t to_write; 822 KitStatus st = w->host->config.read_dir_entry( 823 w->host->config.user, w->fds[fd].dir_handle, cookie, &entry); 824 if (st == KIT_NOT_FOUND) break; 825 if (st != KIT_OK) return wasm_status_errno(st); 826 memset(hdr, 0, 24u); 827 wasm_put_u64_le(hdr, 0u, cookie + 1u); 828 wasm_put_u64_le(hdr, 8u, entry.ino); 829 wasm_put_u32_le(hdr, 16u, entry.name_len); 830 hdr[20] = entry.filetype; 831 to_write = (remain < 24u) ? remain : 24u; 832 memcpy(buf + written, hdr, to_write); 833 written += to_write; 834 remain -= to_write; 835 if (remain > 0u && entry.name_len > 0u && entry.name) { 836 to_write = (remain < entry.name_len) ? remain : entry.name_len; 837 memcpy(buf + written, entry.name, to_write); 838 written += to_write; 839 } 840 ++cookie; 841 } 842 return wasm_write_u32(inst, (uint32_t)bufused_i, written) ? WASI_ESUCCESS 843 : WASI_EINVAL; 844 } 845 846 static void* wasm_wasi_resolve(void* user, const char* module, 847 const char* field, 848 const KitWasmImportType* type) { 849 KitWasmHost* host = (KitWasmHost*)user; 850 KitWasmValType r_i32[1] = {KIT_WASM_VAL_I32}; 851 KitWasmValType p_proc_exit[1] = {KIT_WASM_VAL_I32}; 852 KitWasmValType p2_i32[2] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; 853 KitWasmValType p3_i32[3] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 854 KIT_WASM_VAL_I32}; 855 KitWasmValType p4_i32[4] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 856 KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; 857 KitWasmValType p5_i32[5] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 858 KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 859 KIT_WASM_VAL_I32}; 860 KitWasmValType p_clock[3] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I64, 861 KIT_WASM_VAL_I32}; 862 KitWasmValType p_path_open[9] = { 863 KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 864 KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I64, 865 KIT_WASM_VAL_I64, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; 866 KitWasmValType p_fd_seek[4] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I64, 867 KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; 868 KitWasmValType p_fd_readdir[5] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, 869 KIT_WASM_VAL_I32, KIT_WASM_VAL_I64, 870 KIT_WASM_VAL_I32}; 871 if (!host || !(host->config.flags & KIT_WASM_HOST_WASI_PREVIEW1)) return NULL; 872 if (!wasm_streq(module, "wasi_snapshot_preview1")) return NULL; 873 if (wasm_streq(field, "proc_exit") && 874 wasm_sig(type, p_proc_exit, 1u, NULL, 0u)) 875 return (void*)(uintptr_t)wasi_proc_exit; 876 if (wasm_streq(field, "fd_write") && wasm_sig(type, p4_i32, 4u, r_i32, 1u)) 877 return (void*)(uintptr_t)wasi_fd_write; 878 if (wasm_streq(field, "environ_sizes_get") && 879 wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 880 return (void*)(uintptr_t)wasi_environ_sizes_get; 881 if (wasm_streq(field, "environ_get") && wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 882 return (void*)(uintptr_t)wasi_environ_get; 883 if (wasm_streq(field, "args_sizes_get") && 884 wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 885 return (void*)(uintptr_t)wasi_args_sizes_get; 886 if (wasm_streq(field, "args_get") && wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 887 return (void*)(uintptr_t)wasi_args_get; 888 if (wasm_streq(field, "random_get") && wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 889 return (void*)(uintptr_t)wasi_random_get; 890 if (wasm_streq(field, "clock_time_get") && 891 wasm_sig(type, p_clock, 3u, r_i32, 1u)) 892 return (void*)(uintptr_t)wasi_clock_time_get; 893 if (wasm_streq(field, "fd_prestat_get") && 894 wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 895 return (void*)(uintptr_t)wasi_fd_prestat_get; 896 if (wasm_streq(field, "fd_prestat_dir_name") && 897 wasm_sig(type, p3_i32, 3u, r_i32, 1u)) 898 return (void*)(uintptr_t)wasi_fd_prestat_dir_name; 899 if (wasm_streq(field, "path_open") && 900 wasm_sig(type, p_path_open, 9u, r_i32, 1u)) 901 return (void*)(uintptr_t)wasi_path_open; 902 if (wasm_streq(field, "fd_read") && wasm_sig(type, p4_i32, 4u, r_i32, 1u)) 903 return (void*)(uintptr_t)wasi_fd_read; 904 if (wasm_streq(field, "fd_close") && 905 wasm_sig(type, p_proc_exit, 1u, r_i32, 1u)) 906 return (void*)(uintptr_t)wasi_fd_close; 907 if (wasm_streq(field, "fd_seek") && wasm_sig(type, p_fd_seek, 4u, r_i32, 1u)) 908 return (void*)(uintptr_t)wasi_fd_seek; 909 if (wasm_streq(field, "fd_tell") && wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 910 return (void*)(uintptr_t)wasi_fd_tell; 911 if (wasm_streq(field, "fd_filestat_get") && 912 wasm_sig(type, p2_i32, 2u, r_i32, 1u)) 913 return (void*)(uintptr_t)wasi_fd_filestat_get; 914 if (wasm_streq(field, "path_filestat_get") && 915 wasm_sig(type, p5_i32, 5u, r_i32, 1u)) 916 return (void*)(uintptr_t)wasi_path_filestat_get; 917 if (wasm_streq(field, "fd_readdir") && 918 wasm_sig(type, p_fd_readdir, 5u, r_i32, 1u)) 919 return (void*)(uintptr_t)wasi_fd_readdir; 920 return NULL; 921 } 922 923 KIT_API KitStatus kit_wasm_host_new(const KitWasmHostConfig* config, 924 KitWasmHost** out) { 925 KitWasmHost* host; 926 uint32_t i; 927 if (!out || !config || !config->heap) return KIT_INVALID; 928 *out = NULL; 929 if (config->nmounts && !config->mounts) return KIT_INVALID; 930 if (config->nmounts > KIT_WASM_HOST_MAX_FDS - 3u) return KIT_INVALID; 931 for (i = 0; i < config->nmounts; ++i) { 932 const KitWasmFsMount* m = &config->mounts[i]; 933 if (!m->host_path || !m->guest_path || m->guest_path[0] != '/') 934 return KIT_INVALID; 935 if ((m->flags & KIT_WASM_FS_FILE) && (!*wasm_guest_basename(m->guest_path))) 936 return KIT_INVALID; 937 } 938 host = (KitWasmHost*)config->heap->alloc(config->heap, sizeof(*host), 939 _Alignof(KitWasmHost)); 940 if (!host) return KIT_NOMEM; 941 host->config = *config; 942 *out = host; 943 return KIT_OK; 944 } 945 946 KIT_API void kit_wasm_host_free(KitWasmHost* host) { 947 KitHeap* heap; 948 if (!host) return; 949 heap = host->config.heap; 950 heap->free(heap, host, sizeof(*host)); 951 } 952 953 KIT_API KitStatus kit_wasm_instance_new(KitWasmHost* host, KitJit* jit, 954 KitWasmInstance** out) { 955 KitWasmRuntimeLayout layout; 956 KitWasmRuntimeInstance* w; 957 KitHeap* heap; 958 uint64_t instance_bytes; 959 uint64_t allocation_bytes; 960 size_t allocation_size; 961 uint64_t total_memory = 0; 962 KitStatus st; 963 uint32_t i; 964 if (!out || !host || !host->config.heap || !jit) return KIT_INVALID; 965 *out = NULL; 966 heap = host->config.heap; 967 st = kit_wasm_get_runtime_layout(jit, &layout); 968 if (st != KIT_OK) return st; 969 if (layout.nmemories > host->config.max_memories) return KIT_INVALID; 970 instance_bytes = layout.instance_size ? layout.instance_size : 1u; 971 if (instance_bytes > host->config.max_instance_bytes) return KIT_INVALID; 972 if (instance_bytes > UINT64_MAX - offsetof(KitWasmRuntimeInstance, instance)) 973 return KIT_INVALID; 974 allocation_bytes = 975 (uint64_t)offsetof(KitWasmRuntimeInstance, instance) + instance_bytes; 976 st = wasm_u64_to_size(allocation_bytes, &allocation_size); 977 if (st != KIT_OK) return st; 978 w = (KitWasmRuntimeInstance*)heap->alloc(heap, allocation_size, 979 _Alignof(KitWasmRuntimeInstance)); 980 if (!w) return KIT_NOMEM; 981 memset(w, 0, allocation_size); 982 w->host = host; 983 w->memory_layouts = layout.memories; 984 w->allocation_bytes = allocation_bytes; 985 w->instance_bytes = instance_bytes; 986 w->nmemories = layout.nmemories; 987 if (layout.nmemories) { 988 size_t ptr_bytes; 989 size_t bytes_bytes; 990 if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*) || 991 (uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint64_t)) { 992 heap->free(heap, w, allocation_size); 993 return KIT_INVALID; 994 } 995 ptr_bytes = (size_t)layout.nmemories * sizeof(uint8_t*); 996 bytes_bytes = (size_t)layout.nmemories * sizeof(uint64_t); 997 w->memories = (uint8_t**)heap->alloc(heap, ptr_bytes, _Alignof(uint8_t*)); 998 w->memory_bytes = 999 (uint64_t*)heap->alloc(heap, bytes_bytes, _Alignof(uint64_t)); 1000 if (!w->memories || !w->memory_bytes) { 1001 if (w->memory_bytes) heap->free(heap, w->memory_bytes, bytes_bytes); 1002 if (w->memories) heap->free(heap, w->memories, ptr_bytes); 1003 heap->free(heap, w, allocation_size); 1004 return KIT_NOMEM; 1005 } 1006 memset(w->memories, 0, ptr_bytes); 1007 memset(w->memory_bytes, 0, bytes_bytes); 1008 } 1009 for (i = 0; i < layout.nmemories; ++i) { 1010 const KitWasmMemoryLayout* ml = &layout.memories[i]; 1011 KitWasmMemoryRecord* rec; 1012 uint64_t mem_bytes; 1013 size_t mem_size; 1014 if (ml->max_pages < ml->min_pages) { 1015 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1016 return KIT_MALFORMED; 1017 } 1018 if (ml->offset > instance_bytes || 1019 instance_bytes - ml->offset < sizeof(KitWasmMemoryRecord)) { 1020 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1021 return KIT_MALFORMED; 1022 } 1023 st = wasm_page_bytes(ml->max_pages, &mem_bytes); 1024 if (st != KIT_OK) { 1025 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1026 return st; 1027 } 1028 if (mem_bytes > host->config.max_total_memory_bytes || 1029 total_memory > host->config.max_total_memory_bytes - mem_bytes) { 1030 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1031 return KIT_INVALID; 1032 } 1033 total_memory += mem_bytes; 1034 st = wasm_u64_to_size(mem_bytes, &mem_size); 1035 if (st != KIT_OK) { 1036 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1037 return st; 1038 } 1039 if (mem_size) { 1040 w->memories[i] = (uint8_t*)heap->alloc(heap, mem_size, 1u); 1041 if (!w->memories[i]) { 1042 kit_wasm_instance_free(wasm_instance_from_wrap(w)); 1043 return KIT_NOMEM; 1044 } 1045 memset(w->memories[i], 0, mem_size); 1046 } 1047 w->memory_bytes[i] = mem_bytes; 1048 rec = (KitWasmMemoryRecord*)(w->instance + ml->offset); 1049 rec->data = w->memories[i]; 1050 } 1051 w->total_memory_bytes = total_memory; 1052 *out = wasm_instance_from_wrap(w); 1053 return KIT_OK; 1054 } 1055 1056 KIT_API void kit_wasm_instance_free(KitWasmInstance* inst) { 1057 KitWasmRuntimeInstance* w; 1058 KitHeap* heap; 1059 uint32_t i; 1060 size_t allocation_size; 1061 if (!inst) return; 1062 w = wasm_wrap_from_instance(inst); 1063 heap = w->host->config.heap; 1064 for (i = 0; i < KIT_WASM_HOST_MAX_FDS; ++i) wasm_fd_close_slot(w, i); 1065 for (i = 0; i < w->nmemories; ++i) { 1066 size_t mem_size; 1067 if (w->memories && w->memories[i] && 1068 wasm_u64_to_size(w->memory_bytes[i], &mem_size) == KIT_OK) 1069 heap->free(heap, w->memories[i], mem_size); 1070 } 1071 if (w->memories) 1072 heap->free(heap, w->memories, (size_t)w->nmemories * sizeof(uint8_t*)); 1073 if (w->memory_bytes) 1074 heap->free(heap, w->memory_bytes, (size_t)w->nmemories * sizeof(uint64_t)); 1075 allocation_size = (size_t)w->allocation_bytes; 1076 heap->free(heap, w, allocation_size); 1077 } 1078 1079 KIT_API KitStatus kit_wasm_host_bind_imports(KitWasmHost* host, 1080 KitCompiler* compiler, KitJit* jit, 1081 KitWasmInstance* inst) { 1082 if (!host || !compiler || !jit || !inst) return KIT_INVALID; 1083 return kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0, 1084 wasm_wasi_resolve, host); 1085 } 1086 1087 KIT_API int kit_wasm_instance_exit_code(KitWasmInstance* inst, int* code_out) { 1088 KitWasmRuntimeInstance* w; 1089 if (!inst) return 0; 1090 w = wasm_wrap_from_instance(inst); 1091 if (!w->exit_called) return 0; 1092 if (code_out) *code_out = w->exit_code; 1093 return 1; 1094 } 1095 1096 KIT_API KitStatus kit_wasm_instance_run_entry(KitWasmInstance* inst, 1097 KitWasmInitFn init, 1098 KitWasmEntryFn entry, 1099 int* rc_out) { 1100 KitWasmRuntimeInstance* w; 1101 int rc = 0; 1102 if (!inst || !entry) return KIT_INVALID; 1103 w = wasm_wrap_from_instance(inst); 1104 if (setjmp(w->exit_jmp) == 0) { 1105 /* First return from setjmp: arm the pad and run the guest. A proc_exit call 1106 * longjmps back with a nonzero value, landing in the else branch. */ 1107 w->exit_jmp_valid = 1; 1108 if (init) init(inst); 1109 rc = entry(inst); 1110 w->exit_jmp_valid = 0; 1111 if (w->exit_called) rc = w->exit_code; 1112 } else { 1113 /* Unwound out of the guest via proc_exit; exit_code is authoritative. */ 1114 rc = w->exit_code; 1115 } 1116 if (rc_out) *rc_out = rc; 1117 return KIT_OK; 1118 }