From d8eae45ad691edd2b2cad2d483fa1dbbd954b01a Mon Sep 17 00:00:00 2001 From: andreatp Date: Wed, 12 Aug 2026 13:23:42 +0200 Subject: [PATCH] Catch OutOfMemoryError at WasmGC allocation sites Convert OutOfMemoryError to WasmEngineException("out of memory") in all GC allocation instructions (struct.new, struct.new_default, array.new, array.new_default, array.new_fixed, array.new_data, array.new_elem) across the interpreter, compiler runtime, and constant expression evaluator paths. Follows the existing StackOverflowError precedent. Refs: #102 --- .../run/endive/testing/GcOutOfMemoryTest.java | 98 ++++++ .../run/endive/compiler/internal/Shaded.java | 178 ++++++---- .../endive/runtime/ConstantEvaluators.java | 253 +++++++------- .../endive/runtime/InterpreterMachine.java | 320 ++++++++++-------- .../resources/compiled/gc_oom_test.wat.wasm | Bin 0 -> 246 bytes 5 files changed, 530 insertions(+), 319 deletions(-) create mode 100644 compiler-tests/src/test/java/run/endive/testing/GcOutOfMemoryTest.java create mode 100644 wasm-corpus/src/main/resources/compiled/gc_oom_test.wat.wasm diff --git a/compiler-tests/src/test/java/run/endive/testing/GcOutOfMemoryTest.java b/compiler-tests/src/test/java/run/endive/testing/GcOutOfMemoryTest.java new file mode 100644 index 000000000..655158e96 --- /dev/null +++ b/compiler-tests/src/test/java/run/endive/testing/GcOutOfMemoryTest.java @@ -0,0 +1,98 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmEngineException; +import run.endive.wasm.WasmModule; + +public class GcOutOfMemoryTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_oom_test.wat.wasm")); + + private Instance interpreterInstance() { + return Instance.builder(MODULE).withMachineFactory(InterpreterMachine::new).build(); + } + + private Instance compilerInstance() { + return Instance.builder(MODULE).withMachineFactory(MachineFactoryCompiler::compile).build(); + } + + private void assertOom(Instance instance, String funcName) { + var fn = instance.export(funcName); + var ex = assertThrows(WasmEngineException.class, () -> fn.apply(Integer.MAX_VALUE)); + assertTrue(ex.getMessage().contains("out of memory")); + } + + // array.new_default with i32 elements + + @Test + public void arrayNewDefaultI32Interpreter() { + assertOom(interpreterInstance(), "array_new_default_i32"); + } + + @Test + public void arrayNewDefaultI32Compiler() { + assertOom(compilerInstance(), "array_new_default_i32"); + } + + // array.new with i32 fill value + + @Test + public void arrayNewI32Interpreter() { + assertOom(interpreterInstance(), "array_new_i32"); + } + + @Test + public void arrayNewI32Compiler() { + assertOom(compilerInstance(), "array_new_i32"); + } + + // array.new_default with reference elements + + @Test + public void arrayNewDefaultRefInterpreter() { + assertOom(interpreterInstance(), "array_new_default_ref"); + } + + @Test + public void arrayNewDefaultRefCompiler() { + assertOom(compilerInstance(), "array_new_default_ref"); + } + + // array.new with reference fill value + + @Test + public void arrayNewRefInterpreter() { + assertOom(interpreterInstance(), "array_new_ref"); + } + + @Test + public void arrayNewRefCompiler() { + assertOom(compilerInstance(), "array_new_ref"); + } + + // struct.new_default works normally (no OOM on small struct) + + @Test + public void structNewDefaultNormalInterpreter() { + var instance = interpreterInstance(); + var result = instance.export("struct_new_default").apply(); + assertEquals(0, result[0]); + } + + @Test + public void structNewDefaultNormalCompiler() { + var instance = compilerInstance(); + var result = instance.export("struct_new_default").apply(); + assertEquals(0, result[0]); + } +} diff --git a/compiler/src/main/java/run/endive/compiler/internal/Shaded.java b/compiler/src/main/java/run/endive/compiler/internal/Shaded.java index dec325599..9de122acd 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/Shaded.java +++ b/compiler/src/main/java/run/endive/compiler/internal/Shaded.java @@ -467,6 +467,10 @@ public static RuntimeException throwCallStackExhausted(StackOverflowError e) { throw new WasmEngineException("call stack exhausted", e); } + public static RuntimeException throwOutOfMemory(OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); + } + public static RuntimeException throwIndirectCallTypeMismatch() { return new WasmEngineException("indirect call type mismatch"); } @@ -951,22 +955,38 @@ public static void memoryAtomicFence(Memory memory) { public static Object structNew( long[] fields, Object[] fieldRefs, int typeIdx, Instance instance) { - return WasmStruct.builder().typeIdx(typeIdx).fields(fields).fieldRefs(fieldRefs).build(); + try { + return WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(fieldRefs) + .build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); + } } public static Object structNewDefault(int typeIdx, Instance instance) { - var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); - var fields = new long[st.fieldTypes().length]; - var fieldRefs = new Object[st.fieldTypes().length]; - for (int i = 0; i < st.fieldTypes().length; i++) { - var ft = st.fieldTypes()[i]; - if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - fields[i] = REF_NULL_VALUE; + try { + var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); + var fields = new long[st.fieldTypes().length]; + var fieldRefs = new Object[st.fieldTypes().length]; + for (int i = 0; i < st.fieldTypes().length; i++) { + var ft = st.fieldTypes()[i]; + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + fields[i] = REF_NULL_VALUE; + } } + return WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(fieldRefs) + .build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); } - return WasmStruct.builder().typeIdx(typeIdx).fields(fields).fieldRefs(fieldRefs).build(); } public static long structGet(Object ref, int typeIdx, int fieldIdx, Instance instance) { @@ -1037,82 +1057,118 @@ public static void structSetRef( } public static Object arrayNew(long initVal, int len, int typeIdx, Instance instance) { - var elems = new long[len]; - Arrays.fill(elems, initVal); - return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + try { + var elems = new long[len]; + Arrays.fill(elems, initVal); + return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); + } } public static Object arrayNewRef(Object initVal, int len, int typeIdx, Instance instance) { - var elems = new long[len]; - var elemRefs = new Object[len]; - Arrays.fill(elemRefs, initVal); - return WasmArray.builder().typeIdx(typeIdx).elements(elems).elementRefs(elemRefs).build(); - } - - public static Object arrayNewDefault(int len, int typeIdx, Instance instance) { - var elems = new long[len]; - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - var ft = at.fieldType(); - if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { + try { + var elems = new long[len]; + var elemRefs = new Object[len]; + Arrays.fill(elemRefs, initVal); return WasmArray.builder() .typeIdx(typeIdx) .elements(elems) - .elementRefs(new Object[len]) + .elementRefs(elemRefs) .build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); } - if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - Arrays.fill(elems, REF_NULL_VALUE); + } + + public static Object arrayNewDefault(int len, int typeIdx, Instance instance) { + try { + var elems = new long[len]; + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var ft = at.fieldType(); + if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { + return WasmArray.builder() + .typeIdx(typeIdx) + .elements(elems) + .elementRefs(new Object[len]) + .build(); + } + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + Arrays.fill(elems, REF_NULL_VALUE); + } + return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); } - return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); } public static Object arrayNewFixed(long[] vals, int typeIdx, Instance instance) { - return WasmArray.builder().typeIdx(typeIdx).elements(vals).build(); + try { + return WasmArray.builder().typeIdx(typeIdx).elements(vals).build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); + } } public static Object arrayNewFixedRefs(Object[] vals, int typeIdx, Instance instance) { - var elems = new long[vals.length]; - return WasmArray.builder().typeIdx(typeIdx).elements(elems).elementRefs(vals).build(); + try { + var elems = new long[vals.length]; + return WasmArray.builder().typeIdx(typeIdx).elements(elems).elementRefs(vals).build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); + } } public static Object arrayNewData( int offset, int len, int typeIdx, int dataIdx, Instance instance) { - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - var elemSize = at.fieldType().storageType().byteSize(); - var data = instance.dataSegmentData(dataIdx); - if ((long) offset + (long) len * elemSize > data.length) { - throw new TrapException("out of bounds memory access"); - } - var elems = new long[len]; - for (int i = 0; i < len; i++) { - var byteOff = offset + i * elemSize; - elems[i] = readFromData(data, byteOff, elemSize); + try { + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var elemSize = at.fieldType().storageType().byteSize(); + var data = instance.dataSegmentData(dataIdx); + if ((long) offset + (long) len * elemSize > data.length) { + throw new TrapException("out of bounds memory access"); + } + var elems = new long[len]; + for (int i = 0; i < len; i++) { + var byteOff = offset + i * elemSize; + elems[i] = readFromData(data, byteOff, elemSize); + } + return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); } - return WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); } public static Object arrayNewElem( int offset, int len, int typeIdx, int elemIdx, Instance instance) { - var element = instance.element(elemIdx); - if (element == null || offset + len > element.elementCount()) { - throw new TrapException("out of bounds table access"); - } - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - boolean isRef = at.fieldType().storageType().isObjectRef(); - var elems = new long[len]; - var elemRefs = new Object[len]; - for (int i = 0; i < len; i++) { - var init = element.initializers().get(offset + i); - var result = ConstantEvaluators.computeConstant(instance, init); - if (isRef) { - elemRefs[i] = result.ref(); - } else { - elems[i] = result.longValue(); + try { + var element = instance.element(elemIdx); + if (element == null || offset + len > element.elementCount()) { + throw new TrapException("out of bounds table access"); } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); + var elems = new long[len]; + var elemRefs = new Object[len]; + for (int i = 0; i < len; i++) { + var init = element.initializers().get(offset + i); + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + elemRefs[i] = result.ref(); + } else { + elems[i] = result.longValue(); + } + } + return WasmArray.builder() + .typeIdx(typeIdx) + .elements(elems) + .elementRefs(elemRefs) + .build(); + } catch (OutOfMemoryError e) { + throw throwOutOfMemory(e); } - return WasmArray.builder().typeIdx(typeIdx).elements(elems).elementRefs(elemRefs).build(); } public static long arrayGet(Object ref, int idx, int typeIdx, Instance instance) { diff --git a/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java b/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java index 705818f30..6f2efc24f 100644 --- a/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java +++ b/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java @@ -7,6 +7,7 @@ import java.util.List; import run.endive.wasm.InvalidException; import run.endive.wasm.MalformedException; +import run.endive.wasm.WasmEngineException; import run.endive.wasm.types.Instruction; import run.endive.wasm.types.ValType; import run.endive.wasm.types.Value; @@ -149,146 +150,166 @@ public static ConstantResult computeConstant(Instance instance, List= 0; i--) { - var entry = stack.pop(); - var ft = structType.fieldTypes()[i]; - if (ft.storageType().isObjectRef()) { - fieldRefs[i] = entry.ref(); - } else { - fields[i] = entry.longValue(); + try { + var typeIdx = (int) instruction.operand(0); + var structType = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .structType(); + var fieldCount = structType.fieldTypes().length; + var fields = new long[fieldCount]; + var fieldRefs = new Object[fieldCount]; + for (int i = fieldCount - 1; i >= 0; i--) { + var entry = stack.pop(); + var ft = structType.fieldTypes()[i]; + if (ft.storageType().isObjectRef()) { + fieldRefs[i] = entry.ref(); + } else { + fields[i] = entry.longValue(); + } } + var struct = + WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(fieldRefs) + .build(); + stack.push(new ConstantResult(new long[] {0}, struct)); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var struct = - WasmStruct.builder() - .typeIdx(typeIdx) - .fields(fields) - .fieldRefs(fieldRefs) - .build(); - stack.push(new ConstantResult(new long[] {0}, struct)); break; } case STRUCT_NEW_DEFAULT: { - var typeIdx = (int) instruction.operand(0); - var structType = - instance.module() - .typeSection() - .getSubType(typeIdx) - .compType() - .structType(); - var fieldCount = structType.fieldTypes().length; - var fields = new long[fieldCount]; - var fieldRefs = new Object[fieldCount]; - // Non-GC reference fields default to REF_NULL_VALUE - for (int i = 0; i < fieldCount; i++) { - var ft = structType.fieldTypes()[i]; - if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - fields[i] = Value.REF_NULL_VALUE; + try { + var typeIdx = (int) instruction.operand(0); + var structType = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .structType(); + var fieldCount = structType.fieldTypes().length; + var fields = new long[fieldCount]; + var fieldRefs = new Object[fieldCount]; + // Non-GC reference fields default to REF_NULL_VALUE + for (int i = 0; i < fieldCount; i++) { + var ft = structType.fieldTypes()[i]; + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + fields[i] = Value.REF_NULL_VALUE; + } } + var struct = + WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(fieldRefs) + .build(); + stack.push(new ConstantResult(new long[] {0}, struct)); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var struct = - WasmStruct.builder() - .typeIdx(typeIdx) - .fields(fields) - .fieldRefs(fieldRefs) - .build(); - stack.push(new ConstantResult(new long[] {0}, struct)); break; } case ARRAY_NEW: { - var typeIdx = (int) instruction.operand(0); - var len = (int) stack.pop().longValue(); - var fillEntry = stack.pop(); - var at = - instance.module() - .typeSection() - .getSubType(typeIdx) - .compType() - .arrayType(); - var elements = new long[len]; - var elementRefs = new Object[len]; - if (at.fieldType().storageType().isObjectRef()) { - Arrays.fill(elementRefs, fillEntry.ref()); - } else { - Arrays.fill(elements, fillEntry.longValue()); + try { + var typeIdx = (int) instruction.operand(0); + var len = (int) stack.pop().longValue(); + var fillEntry = stack.pop(); + var at = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .arrayType(); + var elements = new long[len]; + var elementRefs = new Object[len]; + if (at.fieldType().storageType().isObjectRef()) { + Arrays.fill(elementRefs, fillEntry.ref()); + } else { + Arrays.fill(elements, fillEntry.longValue()); + } + var array = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elements) + .elementRefs(elementRefs) + .build(); + stack.push(new ConstantResult(new long[] {0}, array)); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var array = - WasmArray.builder() - .typeIdx(typeIdx) - .elements(elements) - .elementRefs(elementRefs) - .build(); - stack.push(new ConstantResult(new long[] {0}, array)); break; } case ARRAY_NEW_DEFAULT: { - var typeIdx = (int) instruction.operand(0); - var len = (int) stack.pop().longValue(); - var elements = new long[len]; - var elementRefs = new Object[len]; - var at = - instance.module() - .typeSection() - .getSubType(typeIdx) - .compType() - .arrayType(); - var ft = at.fieldType(); - if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - Arrays.fill(elements, Value.REF_NULL_VALUE); + try { + var typeIdx = (int) instruction.operand(0); + var len = (int) stack.pop().longValue(); + var elements = new long[len]; + var elementRefs = new Object[len]; + var at = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .arrayType(); + var ft = at.fieldType(); + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + Arrays.fill(elements, Value.REF_NULL_VALUE); + } + var array = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elements) + .elementRefs(elementRefs) + .build(); + stack.push(new ConstantResult(new long[] {0}, array)); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var array = - WasmArray.builder() - .typeIdx(typeIdx) - .elements(elements) - .elementRefs(elementRefs) - .build(); - stack.push(new ConstantResult(new long[] {0}, array)); break; } case ARRAY_NEW_FIXED: { - var typeIdx = (int) instruction.operand(0); - var len = (int) instruction.operand(1); - var at = - instance.module() - .typeSection() - .getSubType(typeIdx) - .compType() - .arrayType(); - var elements = new long[len]; - var elementRefs = new Object[len]; - boolean isGcRef = at.fieldType().storageType().isObjectRef(); - for (int i = len - 1; i >= 0; i--) { - var entry = stack.pop(); - if (isGcRef) { - elementRefs[i] = entry.ref(); - } else { - elements[i] = entry.longValue(); + try { + var typeIdx = (int) instruction.operand(0); + var len = (int) instruction.operand(1); + var at = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .arrayType(); + var elements = new long[len]; + var elementRefs = new Object[len]; + boolean isGcRef = at.fieldType().storageType().isObjectRef(); + for (int i = len - 1; i >= 0; i--) { + var entry = stack.pop(); + if (isGcRef) { + elementRefs[i] = entry.ref(); + } else { + elements[i] = entry.longValue(); + } } + var array = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elements) + .elementRefs(elementRefs) + .build(); + stack.push(new ConstantResult(new long[] {0}, array)); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var array = - WasmArray.builder() - .typeIdx(typeIdx) - .elements(elements) - .elementRefs(elementRefs) - .build(); - stack.push(new ConstantResult(new long[] {0}, array)); break; } case ANY_CONVERT_EXTERN: diff --git a/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java b/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java index 6849fa0f5..91a1cf183 100644 --- a/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java +++ b/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java @@ -3463,48 +3463,60 @@ private static void I31_GET_U(MStack stack) { } private static void STRUCT_NEW(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); - var fields = new long[st.fieldTypes().length]; - var fieldRefs = new Object[st.fieldTypes().length]; - // Pop fields in reverse order (last field on top) - for (int i = fields.length - 1; i >= 0; i--) { - var ft = st.fieldTypes()[i]; - if (ft.storageType().isObjectRef()) { - fieldRefs[i] = stack.popRef(); - } else { - fields[i] = stack.pop(); + try { + var typeIdx = (int) operands.get(0); + var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); + var fields = new long[st.fieldTypes().length]; + var fieldRefs = new Object[st.fieldTypes().length]; + // Pop fields in reverse order (last field on top) + for (int i = fields.length - 1; i >= 0; i--) { + var ft = st.fieldTypes()[i]; + if (ft.storageType().isObjectRef()) { + fieldRefs[i] = stack.popRef(); + } else { + fields[i] = stack.pop(); + } } + var struct = + WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(fieldRefs) + .build(); + stack.pushRef(struct); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var struct = - WasmStruct.builder().typeIdx(typeIdx).fields(fields).fieldRefs(fieldRefs).build(); - stack.pushRef(struct); } private static void STRUCT_NEW_DEFAULT(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); - var fields = new long[st.fieldTypes().length]; - boolean hasObjRefFields = false; - for (int i = 0; i < fields.length; i++) { - var ft = st.fieldTypes()[i]; - if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { - hasObjRefFields = true; - } else if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - fields[i] = REF_NULL_VALUE; + try { + var typeIdx = (int) operands.get(0); + var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); + var fields = new long[st.fieldTypes().length]; + boolean hasObjRefFields = false; + for (int i = 0; i < fields.length; i++) { + var ft = st.fieldTypes()[i]; + if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { + hasObjRefFields = true; + } else if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + fields[i] = REF_NULL_VALUE; + } } + var struct = + hasObjRefFields + ? WasmStruct.builder() + .typeIdx(typeIdx) + .fields(fields) + .fieldRefs(new Object[fields.length]) + .build() + : WasmStruct.builder().typeIdx(typeIdx).fields(fields).build(); + stack.pushRef(struct); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var struct = - hasObjRefFields - ? WasmStruct.builder() - .typeIdx(typeIdx) - .fields(fields) - .fieldRefs(new Object[fields.length]) - .build() - : WasmStruct.builder().typeIdx(typeIdx).fields(fields).build(); - stack.pushRef(struct); } private static void STRUCT_GET( @@ -3562,71 +3574,146 @@ private static void STRUCT_SET(MStack stack, Instance instance, Operands operand } private static void ARRAY_NEW(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - boolean isRef = - at.fieldType().storageType().valType() != null - && at.fieldType().storageType().isObjectRef(); - var len = (int) stack.pop(); - if (isRef) { - var initRef = stack.popRef(); + try { + var typeIdx = (int) operands.get(0); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); + var len = (int) stack.pop(); + if (isRef) { + var initRef = stack.popRef(); + var elems = new long[len]; + var elemRefs = new Object[len]; + java.util.Arrays.fill(elemRefs, initRef); + var arr = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elems) + .elementRefs(elemRefs) + .build(); + stack.pushRef(arr); + } else { + var initVal = stack.pop(); + var elems = new long[len]; + java.util.Arrays.fill(elems, initVal); + var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + stack.pushRef(arr); + } + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); + } + } + + private static void ARRAY_NEW_DEFAULT(MStack stack, Instance instance, Operands operands) { + try { + var typeIdx = (int) operands.get(0); + var len = (int) stack.pop(); var elems = new long[len]; - var elemRefs = new Object[len]; - java.util.Arrays.fill(elemRefs, initRef); - var arr = - WasmArray.builder() - .typeIdx(typeIdx) - .elements(elems) - .elementRefs(elemRefs) - .build(); - stack.pushRef(arr); - } else { - var initVal = stack.pop(); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var ft = at.fieldType(); + if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { + var arr = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elems) + .elementRefs(new Object[len]) + .build(); + stack.pushRef(arr); + } else if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + java.util.Arrays.fill(elems, Value.REF_NULL_VALUE); + var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + stack.pushRef(arr); + } else { + var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + stack.pushRef(arr); + } + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); + } + } + + private static void ARRAY_NEW_FIXED(MStack stack, Instance instance, Operands operands) { + try { + var typeIdx = (int) operands.get(0); + var len = (int) operands.get(1); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); var elems = new long[len]; - java.util.Arrays.fill(elems, initVal); - var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); - stack.pushRef(arr); + if (isRef) { + var elemRefs = new Object[len]; + for (int i = len - 1; i >= 0; i--) { + elemRefs[i] = stack.popRef(); + } + var arr = + WasmArray.builder() + .typeIdx(typeIdx) + .elements(elems) + .elementRefs(elemRefs) + .build(); + stack.pushRef(arr); + } else { + for (int i = len - 1; i >= 0; i--) { + elems[i] = stack.pop(); + } + var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); + stack.pushRef(arr); + } + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } } - private static void ARRAY_NEW_DEFAULT(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var len = (int) stack.pop(); - var elems = new long[len]; - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - var ft = at.fieldType(); - if (ft.storageType().valType() != null && ft.storageType().isObjectRef()) { - var arr = - WasmArray.builder() - .typeIdx(typeIdx) - .elements(elems) - .elementRefs(new Object[len]) - .build(); - stack.pushRef(arr); - } else if (ft.storageType().valType() != null - && ft.storageType().valType().isReference() - && !ft.storageType().isObjectRef()) { - java.util.Arrays.fill(elems, Value.REF_NULL_VALUE); - var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); - stack.pushRef(arr); - } else { + private static void ARRAY_NEW_DATA(MStack stack, Instance instance, Operands operands) { + try { + var typeIdx = (int) operands.get(0); + var dataIdx = (int) operands.get(1); + var len = (int) stack.pop(); + var offset = (int) stack.pop(); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var elemSize = at.fieldType().storageType().byteSize(); + var data = instance.dataSegmentData(dataIdx); + if ((long) offset + (long) len * elemSize > data.length) { + throw new TrapException("out of bounds memory access"); + } + var elems = new long[len]; + for (int i = 0; i < len; i++) { + var byteOff = offset + i * elemSize; + elems[i] = readFromData(data, byteOff, elemSize); + } var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); stack.pushRef(arr); + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } } - private static void ARRAY_NEW_FIXED(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var len = (int) operands.get(1); - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - boolean isRef = - at.fieldType().storageType().valType() != null - && at.fieldType().storageType().isObjectRef(); - var elems = new long[len]; - if (isRef) { + private static void ARRAY_NEW_ELEM(MStack stack, Instance instance, Operands operands) { + try { + var typeIdx = (int) operands.get(0); + var elemIdx = (int) operands.get(1); + var len = (int) stack.pop(); + var offset = (int) stack.pop(); + var element = instance.element(elemIdx); + if (element == null || offset + len > element.elementCount()) { + throw new TrapException("out of bounds table access"); + } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); + var elems = new long[len]; var elemRefs = new Object[len]; - for (int i = len - 1; i >= 0; i--) { - elemRefs[i] = stack.popRef(); + for (int i = 0; i < len; i++) { + var init = element.initializers().get(offset + i); + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + elemRefs[i] = result.ref(); + } else { + elems[i] = result.longValue(); + } } var arr = WasmArray.builder() @@ -3635,60 +3722,9 @@ private static void ARRAY_NEW_FIXED(MStack stack, Instance instance, Operands op .elementRefs(elemRefs) .build(); stack.pushRef(arr); - } else { - for (int i = len - 1; i >= 0; i--) { - elems[i] = stack.pop(); - } - var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); - stack.pushRef(arr); - } - } - - private static void ARRAY_NEW_DATA(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var dataIdx = (int) operands.get(1); - var len = (int) stack.pop(); - var offset = (int) stack.pop(); - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - var elemSize = at.fieldType().storageType().byteSize(); - var data = instance.dataSegmentData(dataIdx); - if ((long) offset + (long) len * elemSize > data.length) { - throw new TrapException("out of bounds memory access"); - } - var elems = new long[len]; - for (int i = 0; i < len; i++) { - var byteOff = offset + i * elemSize; - elems[i] = readFromData(data, byteOff, elemSize); - } - var arr = WasmArray.builder().typeIdx(typeIdx).elements(elems).build(); - stack.pushRef(arr); - } - - private static void ARRAY_NEW_ELEM(MStack stack, Instance instance, Operands operands) { - var typeIdx = (int) operands.get(0); - var elemIdx = (int) operands.get(1); - var len = (int) stack.pop(); - var offset = (int) stack.pop(); - var element = instance.element(elemIdx); - if (element == null || offset + len > element.elementCount()) { - throw new TrapException("out of bounds table access"); - } - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - boolean isRef = at.fieldType().storageType().isObjectRef(); - var elems = new long[len]; - var elemRefs = new Object[len]; - for (int i = 0; i < len; i++) { - var init = element.initializers().get(offset + i); - var result = ConstantEvaluators.computeConstant(instance, init); - if (isRef) { - elemRefs[i] = result.ref(); - } else { - elems[i] = result.longValue(); - } + } catch (OutOfMemoryError e) { + throw new WasmEngineException("out of memory", e); } - var arr = - WasmArray.builder().typeIdx(typeIdx).elements(elems).elementRefs(elemRefs).build(); - stack.pushRef(arr); } private static void ARRAY_GET( diff --git a/wasm-corpus/src/main/resources/compiled/gc_oom_test.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_oom_test.wat.wasm new file mode 100644 index 0000000000000000000000000000000000000000..bc065c96393d4fa21cfee99012a59d6107ddd717 GIT binary patch literal 246 zcmZvWF%N<;5QX2h1p;VHOk7-aa&Xke)jvY1S`1Mb3dA_|hj+1+K?d)Z@4Z~^J*ciB z0JM$@OT~kvuo$-_OSS}A9!K1dNUCV1j8Sjd>X*FfN)69m*2e=d|M@}FpF5)~u*qG} zeA)G8xc1YB5ZNh8v4uUA;(U%s?lP@k@!ZT8-2lt LvFuXc<(T&Y?S(_Z literal 0 HcmV?d00001