Replace WAMR with WasmKit Embedded Swift runtime#18
Conversation
… memory config Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014oHdvLR9P2c8GpozUx7cD9
Adds a new wendy_wasmkit component that reimplements the wendy_wasm.h C API using WasmKit (https://github.com/swiftwasm/WasmKit, branch jo/embedded-runtime-v2), compiled with -enable-experimental-feature Embedded targeting riscv32-none-none-eabi for ESP32-C6. Architecture: - wendy_wasmkit/include/wendy_wasm.h — same public C API as before - wendy_wasmkit/include/wasm_export.h — WAMR stub (allows existing component sources to compile without the managed component) - wendy_wasmkit/src/Runtime.swift — WasmKit Engine/Store/Module lifecycle, @_cdecl C entry points - wendy_wasmkit/src/WendyImports.swift — all "wendy" host functions as WasmKit Function objects; dispatches callbacks through the WASM instance's wendy_handle_callback export - wendy_wasmkit/src/WasmKitBridge.c — thin C shim for output callback - wendy_wasmkit/CMakeLists.txt — custom swiftc build step for WasmKit + bridge sources in a single WMO compilation unit Subsystem changes: - wendy_callback: add wendy_callback_dequeue() for Swift callback dispatch - wendy_uart/spi/storage/otel: add wendy_*_guest_*() public C wrappers callable from Swift without a wasm_exec_env_t - All components: replace wasm-micro-runtime CMake dep with wendy_wasmkit - wendy_wasm: neutered (no sources, no WAMR dep); kept for reference - sdkconfig.defaults: WAMR Kconfig options removed Build requirement: a Swift toolchain that supports -enable-experimental-feature Embedded -target riscv32-none-none-eabi Set SWIFT_TOOLCHAIN_BIN=/path/to/swift/usr/bin before running idf.py. Override WasmKit location with -DWASMKIT_ROOT=/path if needed (default: ../../../../swiftwasm/WasmKit relative to the wendy_wasmkit component). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WvUkRGKaQLGvW5BwCnF6qn
All 4 Swift compilation stages now succeed and the firmware links cleanly into a 1.5 MB binary (fits the 1.88 MB factory partition). Build-system changes: - CMakeLists.txt: post-configure linker script patch that inserts an explicit `.got.plt 0 (INFO)` section before /DISCARD/, fixing the "discarded output section: .got.plt" fatal GNU ld error triggered by Swift Embedded RISC-V code - wendy_wasmkit/CMakeLists.txt: - 4-stage Swift compilation pipeline (WasmTypes→WasmParserCore→WasmKit→bridge) - -package-name WasmKit so Swift `package` access level symbols are visible - -fno-pic -fno-plt to prevent GOT/PLT generation - C compilation of _CWasmKit.c, TrapGuard.c, WasmKitBridge.c with GCC - Swift Unicode data tables bundled into the archive (avoids cross-archive PLT) - INTERFACE library for component (no SRCS = ESP-IDF creates INTERFACE target) - linker.lf: maps .swift_modhash sections to flash_rodata Swift sources: - Runtime.swift → WendyRuntime.swift (avoids WasmKit/Execution/Runtime.swift clash) - WendyRuntime.swift: `import WasmKit; import WasmTypes`; fixed partition subtype enum; fixed esp_log_write (variadic) → wendy_log wrapper; relaxed gTerminateRequested to `internal` visibility; fixed unsafeBitCast for handle unwrapping - WendyImports.swift: `import WasmKit; import WasmTypes`; pdMS_TO_TICKS → wendy_ms_to_ticks C wrapper; Value.i32/i64 use UInt32/UInt64 (not Int32/Int64); added Value.int32/int64 and UInt32.cInt helpers for HAL calls Component changes: - wendy_callback.h: added <stdbool.h> for bool; added wendy_callback_dequeue() - wendy_otel/safety/wasi_shim CMakeLists: added esp_timer dependency - wasm_export.h stub: added wasm_runtime_set_exception() Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WvUkRGKaQLGvW5BwCnF6qn
Applies WasmKit optimizations from the paired jo/embedded-runtime-v2 branch commit 12dd701. Net result: 1,625,008 → 1,607,392 bytes (-17 KB). Summary of changes in WasmKit (reflected here via updated build): - Imports: array storage to avoid String.hashValue → NFC normalization - ExportsStorage: [(key:,value:)] array in Embedded mode for same reason - Exports.find(function/memory:): StaticString byte-compare lookup - Trap/TrapReason/WasmKitError description conformances guarded for Embedded - TrapReason.Message error strings simplified (no array interpolation) - parseName() uses String(validating:as:UTF8.self) instead of char append Remaining 32 KB overhead: Unicode NFC normalization tables from Swift's String type (inherent; eliminating requires replacing String with [UInt8] throughout WasmKit's public API). Updated bridge code in this repo: - exports[function: "name"] / exports[memory: "memory"] replaced throughout with exports.find(function:) / exports.find(memory:) using StaticString to avoid String subscript hashing in WendyRuntime.swift / WendyImports.swift Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WvUkRGKaQLGvW5BwCnF6qn
WASI: - Replace broken WAMR-era C shim with complete WASI snapshot_preview1 implementation in Swift using WasmKit's Imports API (44 functions). - fd_write: full iovec loop via withUnsafeBufferPointer, stdout/stderr only. - random_get: esp_fill_random via withUnsafeMutableBufferPointer. - clock_time_get / clock_res_get: esp_timer monotonic (µs→ns). - fd_fdstat_get: 24-byte char-device stat for stdio fds 0-2. - All path_*, remaining fd_*, sock_*, poll_oneoff: correct WASI signatures returning ENOSYS/EBADF/ENOTSUP — no filesystem or WASI sockets. - Remove dead wendy_wasi_shim dependency from CMakeLists (init was never called with WasmKit; now eliminated entirely). - Add esp_random.h to WendyCBridge.h for esp_fill_random. Runtime fixes: - Engine stack size: respect configured stackSize instead of defaulting to WasmKit's 512 KB (which exceeds available DRAM on ESP32-C6). - Instantiation errors: pattern-match WasmKitError.kind to log the message text; log heap stats before and after instantiation for diagnostics. - wendy_main.c init/reinit: always call wendy_wasm_init() after reinit — previous code skipped init on subsequent runs, leaving the engine torn down. - WasmKitBridge.c: add wendy_format_version, wendy_format_mac, wendy_log_heap C helpers (variadic snprintf unavailable in Embedded Swift). - CMakeLists: forward all CONFIG_WENDY_* Kconfig flags as Swift -D defines so #if CONFIG_WENDY_WASI_SHIM etc. are evaluated correctly in WendyImports. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LysLik5cfWYSYXrDGde5yB
AI Security Review
posix_memalign WASM host-function wrapper (potential memory corruption within the WASM linear memory), missing input validation on buffer lengths passed from WASM guest code to host functions, a linker-script patch that silently modifies generated build artifacts during configure time (supply chain / integrity concern), and path-traversal risk from WASM module sources interacting with unvalidated storage keys. Additionally, the Swift compilation pipeline accepts an externally-controlled WASMKIT_ROOT path without any integrity verification (hash or signature), introducing third-party supply chain risk. Most findings are scoped to the embedded firmware threat model, but some (especially buffer handling at the WASM host-function boundary) represent genuine exploitable vectors if untrusted WASM modules are loaded at runtime. |
| Severity | Standards | File | Line(s) | Title |
|---|---|---|---|---|
| HIGH | ISO27001-A.8, NIST-SI-10, SOC2-CC6 | components/wendy_wasm/src/wendy_wasm.c |
posix_memalign_wrapper |
Unsafe pointer arithmetic may corrupt WASM linear memory |
| HIGH | ISO27001-A.8, NIST-SI-10 | components/wendy_spi/src/wendy_spi.c |
wendy_spi_guest_transfer |
WASM-guest-controlled tx_buf/rx_buf passed directly to hardware SPI without bounds-check against linear memory |
| HIGH | ISO27001-A.8, NIST-SI-10 | components/wendy_storage/src/wendy_storage.c |
wendy_storage_guest_* |
Unvalidated key/value lengths from WASM guest passed to NVS — potential OOB read and NVS corruption |
| HIGH | ISO27001-A.8, NIST-SI-10 | components/wendy_uart/src/wendy_uart.c |
wendy_uart_guest_write, wendy_uart_guest_read |
Unvalidated length from WASM guest; no upper-bound check before hardware I/O |
| MEDIUM | SOC2-CC9, ISO27001-A.8, NIST-SC-28 | components/wendy_wasmkit/CMakeLists.txt |
WASMKIT_ROOT resolution |
Externally-controlled WasmKit source tree loaded without integrity verification |
| MEDIUM | SOC2-CC8, ISO27001-A.8 | CMakeLists.txt |
Linker-script patch block | Configure-time mutation of generated build artifacts undermines build reproducibility and audit trail |
| MEDIUM | ISO27001-A.8, NIST-SI-10 | components/wendy_otel/src/wendy_otel.c |
wendy_otel_guest_log |
WASM-guest-controlled len passed to otel_log_wrapper without explicit bounds check |
| MEDIUM | ISO27001-A.8, NIST-SI-10 | components/wendy_wasmkit/CMakeLists.txt |
SWIFT_TOOLCHAIN_BIN resolution |
Compiler binary resolved from environment variable without path validation — build-time injection risk |
| LOW | SOC2-CC7, ISO27001-A.12 | components/wendy_wasm/src/wendy_wasm.c |
posix_memalign_wrapper |
Allocation failures return ENOMEM with no log — silent failure hides resource exhaustion |
| LOW | SOC2-CC7, ISO27001-A.12 | components/wendy_callback/src/wendy_callback.c |
wendy_callback_dequeue |
New dequeue path lacks logging of dropped/dequeued event types — reduces observability |
| INFORMATIONAL | SOC2-CC9 | components/wendy_wasm/idf_component.yml |
WAMR removal | WAMR dependency removed but no pinned WasmKit version or hash — transitive dependency risk |
Detailed Findings
[HIGH-1] Unsafe Pointer Arithmetic May Corrupt WASM Linear Memory
File: components/wendy_wasm/src/wendy_wasm.c
Function: posix_memalign_wrapper
Description:
The implementation over-allocates size + (align - 1) bytes from WASM linear memory, then performs pointer arithmetic to find an aligned address within that allocation and stores the aligned WASM address back to the guest. However, neither size nor align are clamped before this arithmetic. If a WASM guest passes a very large size (e.g., near UINT32_MAX), the addition (uint32_t)size + (uint32_t)(align - 1) will integer-overflow, causing wasm_runtime_module_malloc to allocate a much smaller buffer than intended. The returned aligned pointer will then point outside the allocated region, and subsequent guest writes will silently corrupt WASM linear memory or adjacent host metadata.
Additionally, *(uint32_t *)memptr = wasm_addr + ... writes to a pointer that WAMR is supposed to have already translated to a native host pointer into linear memory — but if the WAMR auto-translation assumptions are wrong (e.g., the signature "(*ii)i" means memptr is already translated), this could write an incorrect WASM address into host memory.
uint32_t wasm_addr = wasm_runtime_module_malloc(
inst, (uint32_t)size + (uint32_t)(align - 1), &native_ptr);
// ↑ integer overflow if size near UINT32_MAX
if (wasm_addr == 0 || !native_ptr)
return 12; /* ENOMEM */
uintptr_t raw = (uintptr_t)native_ptr;
uintptr_t aligned = (raw + (uintptr_t)(align - 1)) & ~(uintptr_t)(align - 1);
*(uint32_t *)memptr = wasm_addr + (uint32_t)(aligned - raw);
// ↑ no overflow check on (aligned - raw) before adding to wasm_addrRemediation:
- Validate
sizeandalignbefore arithmetic: reject ifsize == 0, ifsize > MAX_WASM_ALLOC(e.g., 64 KB), or ifalign > 4096. - Use checked arithmetic:
if (size > (UINT32_MAX - (uint32_t)(align - 1))) return 12; // overflow guard
- Add an assertion or check that
(aligned - raw) < (size_t)(align - 1)before the final store. - Review whether the WAMR signature
"(*ii)i"actually translatesmemptrto a host pointer; if so, the write target is a WASM-linear-memory location and is correct, but add a comment explaining this and validate the offset doesn't escape the allocated region.
Controls violated: ISO 27001 A.8.28 (Secure coding), NIST SP 800-53 SI-10 (Information Input Validation)
[HIGH-2] WASM-Guest-Controlled Pointers Passed Directly to Hardware SPI
File: components/wendy_spi/src/wendy_spi.c
Function: wendy_spi_guest_transfer
Description:
The new public wendy_spi_guest_transfer function accepts tx_buf and rx_buf as raw C pointers from the Swift (WasmKit) layer and passes them directly to spi_device_transmit. Unlike the WAMR wrapper path — where WAMR validates and translates WASM guest pointers into verified host linear-memory addresses — the WasmKit path calls this function with pointers the Swift runtime has translated. If the Swift/WasmKit translation is incomplete or bypassed, a malicious WASM module could pass a crafted pointer to cause:
- Reads from arbitrary host memory regions (information disclosure to WASM guest).
- Writes to arbitrary host memory via the SPI RX buffer.
The len validation (<= 4096) exists but does not validate that tx_buf + len and rx_buf + len stay within WASM linear memory bounds.
int wendy_spi_guest_transfer(int dev_id, int32_t len,
const uint8_t *tx_buf, uint8_t *rx_buf)
{
if (dev_id < 0 || dev_id >= MAX_SPI_DEVS || !s_spi_devs[dev_id].opened) { return -1; }
if (len <= 0 || len > 4096) { return -1; }
spi_transaction_t trans = {
.length = (size_t)len * 8,
.tx_buffer = tx_buf, // ← no linear-memory bounds verification
.rx_buffer = rx_buf, // ← no linear-memory bounds verification
};Remediation:
- The WasmKit
Importsbinding layer (inWendyImports.swift) must perform explicit bounds checking against the WASM instance's linear memory before calling these C wrappers. Add a helper function (e.g.,guard memory.isValidRange(ptr: txBuf, length: len) else { throw ... }). - In the C wrapper, add a documentation comment stating the precondition that buffers must be pre-validated by the Swift caller.
- Alternatively, pass
wasm_memory_base + offsetfrom WasmKit with explicit offset+length validation rather than raw pointers.
Controls violated: ISO 27001 A.8.28, NIST SP 800-53 SI-10, SOC2-CC6
[HIGH-3] Unvalidated Key/Value Lengths from WASM Guest in NVS Wrappers
File: components/wendy_storage/src/wendy_storage.c
Functions: wendy_storage_guest_get, wendy_storage_guest_set, wendy_storage_guest_delete, wendy_storage_guest_exists
Description:
The new public guest-callable wrappers pass key_len and val_len directly to the underlying storage_*_wrapper functions, which use these lengths to access NVS (Non-Volatile Storage). If the underlying wrappers use key_len for memcpy or strnlen-style operations without verifying that key + key_len stays within WASM linear memory, a WASM guest can:
- Trigger out-of-bounds reads from host memory (key/value buffers extend past linear memory).
- Write arbitrary data into NVS by providing a
valpointer and inflatedval_len. - Cause NVS corruption affecting firmware configuration persistence.
int wendy_storage_guest_get(const char *key, int key_len, char *val, int32_t val_len) {
return storage_get_wrapper(NULL, key, key_len, val, (int)val_len);
// ↑ key_len and val_len not validated against WASM linear memory bounds
}NVS keys in ESP-IDF have a maximum length of 15 bytes — passing key_len > 15 will cause undefined behavior in NVS.
Remediation:
- Add explicit maximum-length validation:
if (key_len <= 0 || key_len > 15) return -1;(NVS key max is 15 chars). - Validate
val_lenagainst a reasonable maximum (e.g., NVS string max ~4000 bytes). - The WasmKit
Importslayer must validate thatkeyandvalpointers + their respective lengths lie within WASM linear memory before calling these functions. - Null-terminate or use explicit length-aware string operations in the underlying wrappers.
Controls violated: ISO 27001 A.8.28, NIST SP 800-53 SI-10, SOC2-CC6
[HIGH-4] Unvalidated Buffer Length in UART Guest Wrappers
File: components/wendy_uart/src/wendy_uart.c
Functions: wendy_uart_guest_write, wendy_uart_guest_read
Description:
The new UART guest wrappers pass len from the WASM guest directly to uart_write_wrapper and uart_read_wrapper without any bounds validation. A malicious or buggy WASM module can pass:
- A very large
len(e.g.,INT32_MAX) causing the UART driver to attempt a massive DMA transfer, exhausting DRAM or causing a crash. - A
lenthat extendsdataorbufpast WASM linear memory, exposing host memory to read/write via hardware.
int wendy_uart_guest_write(int port, const uint8_t *data, int len) {
return uart_write_wrapper(NULL, port, (const char *)data, len);
// ↑ len not bounded; data not verified within WASM linear memory
}
int wendy_uart_guest_read(int port, uint8_t *buf, int len) {
return uart_read_wrapper(NULL, port, (char *)buf, len);
// ↑ same issue for the read-into-guest-buffer path
}Remediation:
- Add length validation:
if (len <= 0 || len > 4096) return -1;(or a suitable per-UART maximum). - Validate that
data/buf+lenremain within WASM linear memory bounds (must be enforced in the WasmKit Swift layer before calling these functions). - Document the precondition clearly in the header.
Controls violated: ISO 27001 A.8.28, NIST SP 800-53 SI-10, NIST SP 800-53 AU-9 (availability)
[MEDIUM-1] WasmKit Source Tree Loaded Without Integrity Verification
File: components/wendy_wasmkit/CMakeLists.txt
Variable: WASMKIT_ROOT
Description:
The build system resolves the WasmKit source tree from an environment variable or a relative path default (../../../../swiftwasm/WasmKit). No cryptographic hash, git commit SHA pinning, or signature verification is performed on any WasmKit source files before they are compiled directly into firmware. A compromised or substituted WasmKit source tree (e.g., through a supply-chain attack or accidental directory misconfiguration) would be silently compiled into production firmware. The PR description references a specific branch (jo/embedded-runtime-v2) but does not enforce it at build time.
if(NOT DEFINED WASMKIT_ROOT)
get_filename_component(WASMKIT_ROOT
"${CMAKE_CURRENT_LIST_DIR}/../../../../swiftwasm/WasmKit"
ABSOLUTE)
endif()
# ↑ No integrity check; any directory named WasmKit at this path is acceptedRemediation:
- Pin the expected git commit SHA in the CMakeLists and verify it at configure time:
execute_process(COMMAND git -C "${WASMKIT_ROOT}" rev-parse HEAD OUTPUT_VARIABLE _WK_SHA ...) if(NOT _WK_SHA STREQUAL "${EXPECTED_WASMKIT_SHA}") message(FATAL_ERROR "WasmKit SHA mismatch: expected ${EXPECTED_WASMKIT_SHA}, got ${_WK_SHA}") endif()
- Consider vendoring WasmKit as a git submodule with a pinned commit.
- Document the expected branch and commit in the README and
idf_component.yml.
Controls violated: SOC2-CC9 (Third-party risk), ISO 27001 A.8.8 (Management of technical vulnerabilities), NIST SP 800-53 SC-28, SA-12
[MEDIUM-2] Configure-Time Mutation of Generated Build Artifacts
File: CMakeLists.txt
Block: Linker-script patch section
Description:
The build system modifies sections.ld and sections.ld.in — ESP-IDF generated files — at CMake configure time using file(READ) / file(WRITE). This approach:
- Breaks reproducible builds: the patch is applied in-place to files under
CMAKE_BINARY_DIR, but re-running CMake on an already-patched tree silently skips re-patching (the_alreadyguard), which is correct, but any build-system clean or regeneration may produce an inconsistent state. - Silently modifies third-party generated artifacts: there is no record of this modification in version control, no hash of the pre/post state, and no rollback mechanism.
- Masks upstream changes: if ESP-IDF updates
sections.ld.into fix a related bug, the configure-time patch may silently fail or produce an incorrect result.
file(WRITE "${_f}" "${_ld_content}")
message(STATUS "wendy-lite: patched .got.plt placement in ${_f}")
# ↑ No hash verification of the original file; no record of what was changedRemediation:
- Prefer a proper linker fragment (
.lffile) or a customsections.ld.inchecked into the repo to override the ESP-IDF default — ESP-IDF supports project-level linker fragment overrides. - If the in-place patch is unavoidable, compute and log SHA256 of the original and patched files, and verify the original matches the expected hash before patching.
- Add an explicit CI step that validates the linker script content after configure.
Controls violated: SOC2-CC8 (Change management, audit trail), ISO 27001 A.8.32 (Change management)
[MEDIUM-3] WASM-Guest-Controlled Length Passed to OTel Log Without Bounds Check
File: components/wendy_otel/src/wendy_otel.c
Function: wendy_otel_guest_log
Description:
The wendy_otel_guest_log wrapper passes len from the WASM guest directly to otel_log_wrapper without validating it against a maximum or against WASM linear memory bounds. A large or negative len could cause otel_log_wrapper to read past the end of the msg buffer (which is a translated WASM pointer), disclosing host memory content in log output or causing a crash.
int wendy_otel_guest_log(int32_t level, const char *msg, int32_t len) {
return otel_log_wrapper(NULL, level, msg, (int)len);
// ↑ len from WASM guest; msg pointer not bounds-checked against linear memory
}Remediation:
- Add:
if (len <= 0 || len > MAX_LOG_MESSAGE_LEN) return -1; - Validate
msg+lenwithin WASM linear memory bounds in the WasmKit Swift layer before calling this function. - Consider using
strnlen(msg, len)rather than trusting the guest-provided length.
Controls violated: ISO 27001 A.8.28, NIST SP 800-53 SI-10
[MEDIUM-4] Compiler Binary Resolved from Environment Variable Without Validation
File: components/wendy_wasmkit/CMakeLists.txt
Description:
The Swift compiler path is taken directly from $ENV{SWIFT_TOOLCHAIN_BIN} without validating that it points to an expected binary or that the binary has expected properties (e.g., version check). In a CI/CD environment or developer machine where an attacker can control environment variables, this could allow substitution of a malicious swiftc binary that introduces backdoors into the compiled firmware at build time.
if(DEFINED ENV{SWIFT_TOOLCHAIN_BIN})
set(SWIFTC "$ENV{SWIFT_TOOLCHAIN_BIN}/swiftc")
else()
find_program(SWIFTC swiftc)
endif()
# ↑ No version check, no path canonicalisation, no signature verificationRemediation:
- After resolving
SWIFTC, execute"${SWIFTC}" --versionand validate that the output matches an expected version string. - Document the required Swift toolchain version explicitly in the README and build documentation.
- In CI, pin the toolchain download URL and verify its checksum before use.
Controls violated: ISO 27001 A.8.28, SOC2-CC9 (Supply chain)
[LOW-1] Silent Failure on WASM Allocation Errors — Reduces Observability
File: components/wendy_wasm/src/wendy_wasm.c
Function: posix_memalign_wrapper
Description:
When wasm_runtime_module_malloc returns 0 or native_ptr is null, the function returns ENOMEM silently. On an embedded device with severely limited DRAM (~200 KB free as noted in the PR), allocation failures may be a leading indicator of resource exhaustion and should be logged at WARNING level to assist diagnostics.
if (wasm_addr == 0 || !native_ptr)
return 12; /* ENOMEM */Remediation:
Add ESP_LOGW(TAG, "posix_memalign: WASM alloc failed (size=%d, align=%d)", size, align); before returning.
Controls violated: SOC2-CC7, ISO 27001 A.12.4 (Logging and monitoring)
[LOW-2] New Callback Dequeue Path Lacks Event-Type Logging
File: components/wendy_callback/src/wendy_callback.c
Function: wendy_callback_dequeue
Description:
The new wendy_callback_dequeue function (used by the WasmKit path for event dispatch) dequeues events from the FreeRTOS queue without any logging, even at DEBUG level. The existing wendy_callback_wait_and_dispatch path presumably has logging. If the WasmKit dispatch path drops events or misroutes them, there is no observability into which events were dequeued and whether they were successfully dispatched.
bool wendy_callback_dequeue(wendy_callback_event_t *event, uint32_t timeout_ticks)
{
if (!s_queue || !event) {
return false;
}
return xQueueReceive(s_queue, event, (TickType_t)timeout_ticks) == pdTRUE;
// ↑ no logging on dequeue success or failure
}Remediation:
Add ESP_LOGD(TAG, "dequeued event type=%d", event->type); on success, and ESP_LOGW(TAG, "dequeue called with null queue or event"); on null guard failure.
Controls violated: SOC2-CC7, ISO 27001 A.12.4
[INFORMATIONAL] No Pinned Version or Hash for WasmKit Dependency
File: components/wendy_wasm/idf_component.yml
Description:
The WAMR dependency (espressif/wasm-micro-runtime: ~2.4.0) was version-pinned in the IDF component manifest. WasmKit is now resolved via a filesystem path with no version, commit SHA, or hash recorded in any manifest or lock file. This makes the build non-reproducible and increases supply chain risk.
# jo/wasmkit-runtime: WAMR dependency removed; wendy_wasmkit replaces this component.
dependencies:
idf:
version: ">=5.3.0"
# ↑ WasmKit dependency not tracked in any manifestRemediation:
Add a comment block recording the
The full Swift runtime only builds on esp32c6 with both WasmKit sources and a Swift toolchain present. All other cases now fall back to a minimal C stub that returns ESP_ERR_NOT_SUPPORTED from all wendy_wasm_* functions, so the firmware still links and boots (without WASM support). This fixes CI: the build matrix includes esp32c5 and esp32p4 which are not esp32c6, and the CI container has no Swift toolchain. Previously CMakeLists used FATAL_ERROR for any of these conditions; now it warns and builds stubs. Changes: - CMakeLists.txt: replace FATAL_ERROR with a three-way guard (target ≠ esp32c6, WasmKit missing, Swift missing) that registers stubs and returns. Also change find_program(swiftc REQUIRED) → optional (handled by guard). - src/wendy_wasm_stub.c: new file with no-op implementations of all wendy_wasm_* functions declared in wendy_wasm.h. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LysLik5cfWYSYXrDGde5yB
Summary
Replaces the WAMR C runtime with WasmKit compiled as Embedded Swift for RISC-V (riscv32-none-none-eabi). This brings the WASM runtime into the same Swift codebase as the rest of the Wendy firmware and eliminates the WAMR dependency.
What's in this PR
Runtime replacement (
20adee2→3fe4447)ImportsAPI (GPIO, I2C, Timer, NeoPixel, RMT, UART, SPI, OTel, Storage, Sys, BLE, Net)-O,-wmo, section GC, linker script patches → ~17 KB flash reduction vs naïve portWASI Preview 1 (
6549a8d)wasi_snapshot_preview1surface (44 functions) implemented in Swift via WasmKit'sImportsAPI — no WAMR C shimfd_write(stdout/stderr, full iovec loop),random_get(esp_fill_random),clock_time_get/clock_res_get(esp_timer monotonic µs→ns),fd_fdstat_get(char-device for fds 0-2),proc_exit,sched_yield,environ/args(empty)path_*, remainingfd_*,poll_oneoff,sock_*— registered with correct WASI parameter signatures, returning ENOSYS/EBADF/ENOTSUPwendy_wasi_shimC component (WAMR-specific; its init was never called with WasmKit)Runtime fixes (
6549a8d)wendy_main.c: always callwendy_wasm_init()after reinit — prior code skipped init on subsequent WASM loads, leaving the engine in a torn-down stateWasmKitError.kindto surface the message textTest plan
idf.py buildwithWASMKIT_ROOTpointing to thejo/embedded-runtime-v2WasmKit branchswift_display) — verifywendy_printoutput reaches UARTrandom_getandclock_time_getreturn non-zero values from a WASM app that uses themwasm32-unknown-wasiinstantiates without missing-import errors🤖 Generated with Claude Code
https://claude.ai/code/session_01LysLik5cfWYSYXrDGde5yB