[GR-79472] `struct.new_default` leaves `v128` fields uninitialized (NullPointerException on first read)
Describe the issue
struct.new_default does not materialize the default value for v128 fields. The
field is left as a Java null instead of the zero vector, and the first struct.get
of that field throws:
java.lang.NullPointerException: Cannot invoke "org.graalvm.wasm.vector.Vector128.getBytes()" because "vector128" is nullThis reaches the embedder as an internal error rather than as a guest value.
The module is valid and GraalWasm validates it. v128 is a defaultable type, so
struct.new_default on a struct with a v128 field is legal and must produce a
struct whose field reads back as i32x4 0 0 0 0.
Two observations suggest a single missed code path rather than a general gap:
array.new_defaultis not affected- The
nullis latent. It faults only when the field is actually read, so a struct that is default-created and then overwritten, or whosev128field is never touched, behaves correctly. An invalid-by-construction struct can therefore be carried arbitrarily far from its allocation site before anything surfaces.
The module under test:
(module
(type $s (struct (field (mut v128))))
(func (export "run") (result i32)
struct.new_default $s
struct.get $s 0
i32x4.extract_lane 0))Expected: run() returns 0. Actual: NullPointerException.
For comparison, Wasmtime 49.0.0-dev
(wasmtime run --invoke run -W gc,function-references,simd) returns 0 on the same
bytes, and wasm-tools validate --features all accepts the module.
Steps to reproduce the issue
GraalWasm ships no standalone wasm launcher, so the reproducer drives the polyglot
API with the module embedded as base64. It needs no wat2wasm/wasm-tools, no build
system, and only three jars. Requires a JDK >= 21.
- Fetch the jars:
mkdir repro && cd repro && mkdir lib V=25.3.4.1 && B=https://repo1.maven.org/maven2/org/graalvm curl -sSL -o lib/polyglot.jar "$B/polyglot/polyglot/$V/polyglot-$V.jar" curl -sSL -o lib/truffle-api.jar "$B/truffle/truffle-api/$V/truffle-api-$V.jar" curl -sSL -o lib/wasm-language.jar "$B/wasm/wasm-language/$V/wasm-language-$V.jar" - Save
Repro.java(below) into that directory. - Run it — no separate compile step is needed:
java -cp 'lib/*' Repro.java
truffle-runtime/truffle-compiler are deliberately absent, so this runs on the
plain Truffle interpreter with no optimizing runtime. The bug reproduces there, so it
is not a compiler or partial-evaluation issue.
import java.util.Base64;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.ByteSequence;
/**
* GraalWasm: struct.new_default leaves a v128 field null.
*
* The embedded module is the assembled form of:
*
* (module
* (type $s (struct (field (mut v128))))
* (func (export "run") (result i32)
* struct.new_default $s
* struct.get $s 0
* i32x4.extract_lane 0))
*
* Per the Wasm GC spec the default value of a v128 field is 0, so run()
* must return 0. GraalWasm throws a NullPointerException instead.
*
* Run with the three jars from Maven Central (JDK >= 21):
* java -cp 'lib/*' Repro.java
*/
public final class Repro {
// wat2wasm / `wasm-tools parse` output for the module above.
private static final String MODULE_BASE64 =
"AGFzbQEAAAABCQJfAXsBYAABfwMCAQEHBwEDcnVuAAAKDgEMAPsBAPsCAAD9GwALAAsEbmFtZQQEAQABcw==";
public static void main(String[] args) {
byte[] wasm = Base64.getDecoder().decode(MODULE_BASE64);
try (Context ctx = Context.newBuilder("wasm")
.allowExperimentalOptions(true)
.option("wasm.GC", "true")
.option("wasm.TypedFunctionReferences", "true")
.build()) {
Source src = Source.newBuilder("wasm", ByteSequence.create(wasm), "repro").build();
Value exports = ctx.eval(src).newInstance().getMember("exports");
int result = exports.invokeMember("run").asInt();
System.out.println("run() returned " + result + " (expected 0)");
} catch (java.io.IOException e) {
throw new RuntimeException(e);
}
}
}Describe GraalVM and your environment:
- GraalVM version: CE 25.3.4.1 — also reproduced on CE 25.2.4 and CE 25.1.3.
- JDK major version: 25
- OS: Ubuntu 22.04.5 LTS
- Architecture: AMD64
Output of java -Xinternalversion:
OpenJDK 64-Bit Server VM (25.0.4.1+1-jvmci-25.3-b22) for linux-amd64 JRE (25.0.4.1+1-jvmci-25.3-b22), built on 2026-08-19T00:46:28Z with gcc 14.2.0More details
Exception in thread "main" org.graalvm.polyglot.PolyglotException: java.lang.NullPointerException: Cannot invoke "org.graalvm.wasm.vector.Vector128.getBytes()" because "vector128" is null
at org.graalvm.wasm.vector.Vector128OpsFallback.fromVector128(Vector128OpsFallback.java:1501)
at org.graalvm.wasm.vector.Vector128OpsFallback.fromVector128(Vector128OpsFallback.java:66)
at org.graalvm.wasm.nodes.WasmFunctionNode.executeAggregate(WasmFunctionNode.java:4943)
at org.graalvm.wasm.nodes.WasmFunctionNode.aggregateHandler(WasmFunctionNode.java:4519)
at org.graalvm.wasm.nodes.WasmFunctionNode.executeBodyFromOffset(WasmFunctionNode.java:1411)
at org.graalvm.wasm.nodes.WasmFunctionNode.execute(WasmFunctionNode.java:294)
at org.graalvm.wasm.nodes.WasmInstrumentableFunctionNode.execute(WasmInstrumentableFunctionNode.java:138)
at org.graalvm.wasm.nodes.WasmFunctionBaseNode.execute(WasmFunctionBaseNode.java:61)
at org.graalvm.wasm.nodes.WasmFixedMemoryImplFunctionNode.doDispatched(WasmFixedMemoryImplFunctionNode.java:104)
at org.graalvm.wasm.nodes.WasmFunctionRootNode.executeWithInstance(WasmFunctionRootNode.java:152)
at org.graalvm.wasm.nodes.WasmRootNode.execute(WasmRootNode.java:145)Additional cases tried, all on 25.3.4.1:
| case | result |
|---|---|
struct.new_default, (field (mut v128)) |
NPE |
struct.new_default, (field v128) (immutable) |
NPE |
struct.new_default, v128 as 2nd field |
NPE |
struct.new_default, mixed struct, read only the i32 field |
ok |
struct.new_default then struct.set before any read |
ok |
struct.new with an explicit v128 operand |
ok |
array.new_default + array.get |
ok, reads back as zero |
array.new with an explicit v128 operand |
ok |
global (mut v128) |
ok |
ACK:
Found by differential fuzzing of Wasm GC modules across engines, reduced to the three-instruction case above. This section will be updated
Source: oracle/graal