diff options
-rw-r--r-- | src/wasm-interpreter.h | 35 | ||||
-rw-r--r-- | test/lit/exec/gc-cycle-leak.wast | 28 |
2 files changed, 54 insertions, 9 deletions
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index b5b03743d..a7d350abe 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -36,6 +36,10 @@ #include "wasm-traversal.h" #include "wasm.h" +#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) +#include <sanitizer/lsan_interface.h> +#endif + namespace wasm { struct WasmException { @@ -177,6 +181,23 @@ protected: return Flow(); } + // This small function is mainly useful to put all GCData allocations in a + // single place. We need that because LSan reports leaks on cycles in this + // data, as we don't have a cycle collector. Those leaks are not a serious + // problem as Binaryen is not really used in long-running tasks, so we ignore + // this function in LSan. + Literal makeGCData(const Literals& data, Type type) { + auto allocation = std::make_shared<GCData>(type.getHeapType(), data); +#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) + // GC data with cycles will leak, since shared_ptrs do not handle cycles. + // Binaryen is generally not used in long-running programs so we just ignore + // such leaks for now. + // TODO: Add a cycle collector? + __lsan_ignore_object(allocation.get()); +#endif + return Literal(allocation, type.getHeapType()); + } + public: // Indicates no limit of maxDepth or maxLoopIterations. static const Index NO_LIMIT = 0; @@ -1552,8 +1573,7 @@ public: data[i] = truncateForPacking(value.getSingleValue(), field); } } - return Literal(std::make_shared<GCData>(curr->type.getHeapType(), data), - curr->type.getHeapType()); + return makeGCData(data, curr->type); } Flow visitStructGet(StructGet* curr) { NOTE_ENTER("StructGet"); @@ -1632,8 +1652,7 @@ public: data[i] = value; } } - return Literal(std::make_shared<GCData>(curr->type.getHeapType(), data), - curr->type.getHeapType()); + return makeGCData(data, curr->type); } Flow visitArrayNewSeg(ArrayNewSeg* curr) { WASM_UNREACHABLE("unimp"); } Flow visitArrayNewFixed(ArrayNewFixed* curr) { @@ -1663,8 +1682,7 @@ public: } data[i] = truncateForPacking(value.getSingleValue(), field); } - return Literal(std::make_shared<GCData>(curr->type.getHeapType(), data), - curr->type.getHeapType()); + return makeGCData(data, curr->type); } Flow visitArrayGet(ArrayGet* curr) { NOTE_ENTER("ArrayGet"); @@ -1867,8 +1885,7 @@ public: contents.push_back(ptrDataValues[i]); } } - auto heapType = curr->type.getHeapType(); - return Literal(std::make_shared<GCData>(heapType, contents), heapType); + return makeGCData(contents, curr->type); } default: // TODO: others @@ -3623,7 +3640,7 @@ public: default: WASM_UNREACHABLE("unexpected op"); } - return Literal(std::make_shared<GCData>(heapType, contents), heapType); + return self()->makeGCData(contents, curr->type); } Flow visitArrayInit(ArrayInit* curr) { NOTE_ENTER("ArrayInit"); diff --git a/test/lit/exec/gc-cycle-leak.wast b/test/lit/exec/gc-cycle-leak.wast new file mode 100644 index 000000000..75c329392 --- /dev/null +++ b/test/lit/exec/gc-cycle-leak.wast @@ -0,0 +1,28 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. + +;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s + +(module + (type $A (struct (field (mut (ref null $A))))) + + ;; CHECK: [fuzz-exec] calling test + (func "test" + (local $a (ref $A)) + ;; This function makes a self-cycle where the local $a's ref field points to + ;; itself. This test checks that we do not error, even in sanitizers, when + ;; such cycles are created (at the time of creating this test, we represent + ;; GC data using std::shared_ptr, which does not handle cycles, and so by + ;; default leak checks will error if not suppressed). + (local.set $a + (struct.new $A + (ref.null $A) + ) + ) + (struct.set $A 0 + (local.get $a) + (local.get $a) + ) + ) +) +;; CHECK: [fuzz-exec] calling test +;; CHECK-NEXT: [fuzz-exec] comparing test |