kit

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

wasm_host.c (39783B)


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