commit 7edb4aefe48ae7c856034c528ec4ff1e0801520b
parent 11fddf5b8aa3a6634ba00d053ca73d291f8b1e29
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 12 Jun 2026 12:17:29 -0700
fix(wasm): accept the DataCount section in its standard position
The binary decoder enforced strictly-increasing section ids, but the
bulk-memory DataCount section (id 12) is spec-placed between Element (9)
and Code (10), so a valid module's ids run ...,9,12,10,11. Toolchains
(clang/wasm-ld, emscripten) emit it whenever passive data segments or
memory.init are used, and kit rejected those modules with 'sections out
of order'. Rank the ids so DataCount sorts after Element and before Code
while still catching genuinely out-of-order or duplicated sections.
Regression: decode/datacount-order/wasm (a hand-built binary fixture).
Diffstat:
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/wasm/decode.c b/src/wasm/decode.c
@@ -737,6 +737,23 @@ static void decode_body_insn(BinReader* rp, WasmModule* out, WasmFunc* f,
*rp = r;
}
+/* Canonical known-section ordering rank. Section ids otherwise appear in
+ * strictly increasing order, except the bulk-memory DataCount section (id 12),
+ * which the spec places between the Element (9) and Code (10) sections. Map ids
+ * to a rank so the monotonicity check accepts the standard sequence
+ * ...,9,12,10,11 (emitted by clang/wasm-ld and emscripten whenever passive data
+ * segments or memory.init are used) while still rejecting genuinely
+ * out-of-order or duplicated sections. Custom sections (id 0) are unordered and
+ * handled separately by the caller. */
+static uint32_t wasm_section_rank(uint8_t id) {
+ switch (id) {
+ case 12: return 10; /* DataCount: after Element, before Code */
+ case 10: return 11; /* Code */
+ case 11: return 12; /* Data */
+ default: return id; /* 1..9 keep their natural rank */
+ }
+}
+
void wasm_decode_binary(KitCompiler* c, const KitSlice* input,
WasmModule* out) {
BinReader r;
@@ -756,7 +773,7 @@ void wasm_decode_binary(KitCompiler* c, const KitSlice* input,
size_t end;
bin_need(&r, size);
end = r.pos + size;
- if (id != 0 && id <= last_id)
+ if (id != 0 && wasm_section_rank(id) <= wasm_section_rank(last_id))
wasm_error(c, wasm_loc(0, 0), "wasm: sections out of order");
if (id != 0) last_id = id;
if (id == 0) {
diff --git a/test/wasm/run.sh b/test/wasm/run.sh
@@ -346,6 +346,14 @@ if [ "$RUN_DIAG" -eq 1 ]; then
wf_fail "err/malformed-section/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/malformed-section.wasm" -o "$KIT_WORK/bad-section.o"
printf '\000asm\001\000\000\000\001\200\200\200\200\020' > "$KIT_WORK/malformed-leb.wasm"
wf_fail "err/malformed-leb/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/malformed-leb.wasm" -o "$KIT_WORK/bad-leb.o"
+ # Positive: the bulk-memory DataCount section (id 12) is spec-placed between
+ # Element (9) and Code (10), so a standard module's section ids run
+ # ...,12,10,11 — not monotonic. Toolchains (clang/wasm-ld, emscripten) emit it
+ # whenever passive data segments or memory.init are used; the decoder must
+ # accept it rather than report "sections out of order". Module below:
+ # type ()->(), one func {end}, memory 1, DataCount 1, code, active data @0.
+ printf '\000asm\001\000\000\000\001\004\001\140\000\000\003\002\001\000\005\003\001\000\001\014\001\001\012\004\001\002\000\013\013\007\001\000\101\000\013\001\252' > "$KIT_WORK/datacount-order.wasm"
+ wf_zero "decode/datacount-order/wasm" "$KIT_BIN" cc -target "$target_triple" -c "$KIT_WORK/datacount-order.wasm" -o "$KIT_WORK/datacount-order.o"
fi
# ---- multi-source wasm (cross-TU call + cross-TU data reference) -----------