From 4c75521ce0b1ac55fba1a91ef5156fc811f5dfcb Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 23 Feb 2022 22:23:05 -0800 Subject: [PATCH] [MC][WebAssembly] Fix crash when relocation addend underlows U32 For the object file writer we need to allow the underflow (ar write zero), but for the final linker output we should probably generate an error (I've left that as a TODO for now). Fixes: https://github.com/llvm/llvm-project/issues/54012 Differential Revision: https://reviews.llvm.org/D120522 --- lld/test/wasm/reloc-addend.s | 48 +++++++++++++++++++++ lld/wasm/InputChunks.cpp | 7 ++- lld/wasm/InputFiles.cpp | 2 +- lld/wasm/InputFiles.h | 2 +- llvm/lib/MC/WasmObjectWriter.cpp | 62 ++++++++++++++++++--------- llvm/test/MC/WebAssembly/reloc-code.s | 12 +++--- 6 files changed, 103 insertions(+), 30 deletions(-) diff --git a/lld/test/wasm/reloc-addend.s b/lld/test/wasm/reloc-addend.s index acd57ce357a7..4b23ed1120fa 100644 --- a/lld/test/wasm/reloc-addend.s +++ b/lld/test/wasm/reloc-addend.s @@ -1,6 +1,7 @@ # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s # RUN: wasm-ld -r -o %t.wasm %t.o # RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump --disassemble-symbols=_start --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS .hidden foo .hidden bar @@ -30,6 +31,39 @@ negative_addend: .int32 foo-16 .size negative_addend, 4 +.globl _start +.section .text,"",@ +_start: + .functype _start () -> () + i32.const 0 + i32.load foo + 10 + drop + i32.const 0 + i32.load foo - 10 + drop + i32.const 0 + # This will underflow because i32.load (and the + # corresponding relocation type) take an unsgiend (U32) + # immediate. + i32.load foo - 2048 + drop + end_function + +# CHECK: - Type: CODE +# CHECK-NEXT: Relocations: +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x7 +# CHECK-NEXT: Addend: 10 +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x11 +# CHECK-NEXT: Addend: -10 +# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Offset: 0x1B +# CHECK-NEXT: Addend: -2048 + # CHECK: - Type: DATA # CHECK-NEXT: Relocations: # CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_I32 @@ -40,3 +74,17 @@ negative_addend: # CHECK-NEXT: Index: 0 # CHECK-NEXT: Offset: 0xF # CHECK-NEXT: Addend: -16 + +# DIS: <_start>: +# DIS-EMPTY: +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.load 26 +# DIS-NEXT: drop +# DIS-NEXT: i32.const 0 +# DIS-NEXT: i32.load 6 +# DIS-NEXT: drop +# DIS-NEXT: i32.const 0 +# TODO(sbc): We should probably error here rather than allowing u32 to wrap +# DIS-NEXT: i32.load 4294965264 +# DIS-NEXT: drop +# DIS-NEXT: end diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp index 7973dce6827e..242378504c3b 100644 --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -116,7 +116,10 @@ void InputChunk::relocate(uint8_t *buf) const { LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName()); LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index << " offset=" << rel.Offset << "\n"); - auto value = file->calcNewValue(rel, tombstone, this); + // TODO(sbc): Check that the value is within the range of the + // relocation type below. Most likely we must error out here + // if its not with range. + uint64_t value = file->calcNewValue(rel, tombstone, this); switch (rel.Type) { case R_WASM_TYPE_INDEX_LEB: @@ -125,7 +128,7 @@ void InputChunk::relocate(uint8_t *buf) const { case R_WASM_TAG_INDEX_LEB: case R_WASM_MEMORY_ADDR_LEB: case R_WASM_TABLE_NUMBER_LEB: - encodeULEB128(value, loc, 5); + encodeULEB128(static_cast(value), loc, 5); break; case R_WASM_MEMORY_ADDR_LEB64: encodeULEB128(value, loc, 10); diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index 0758bb922a3b..1e69f16cc5f9 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -106,7 +106,7 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &reloc) const { // Relocations can contain addend for combined sections. This function takes a // relocation and returns updated addend by offset in the output section. -uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { +int64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { switch (reloc.Type) { case R_WASM_MEMORY_ADDR_LEB: case R_WASM_MEMORY_ADDR_LEB64: diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h index cf2c1739e322..c14d763ff30c 100644 --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -119,7 +119,7 @@ public: uint32_t calcNewIndex(const WasmRelocation &reloc) const; uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, const InputChunk *chunk) const; - uint64_t calcNewAddend(const WasmRelocation &reloc) const; + int64_t calcNewAddend(const WasmRelocation &reloc) const; Symbol *getSymbol(const WasmRelocation &reloc) const { return symbols[reloc.Index]; }; diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index e55635d165dd..27023f103d99 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -137,36 +137,58 @@ raw_ostream &operator<<(raw_ostream &OS, const WasmRelocationEntry &Rel) { } #endif -// Write X as an (unsigned) LEB value at offset Offset in Stream, padded +// Write Value as an (unsigned) LEB value at offset Offset in Stream, padded // to allow patching. -template -void writePatchableLEB(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) { +template +void writePatchableULEB(raw_pwrite_stream &Stream, T Value, uint64_t Offset) { uint8_t Buffer[W]; - unsigned SizeLen = encodeULEB128(X, Buffer, W); + unsigned SizeLen = encodeULEB128(Value, Buffer, W); assert(SizeLen == W); Stream.pwrite((char *)Buffer, SizeLen, Offset); } -// Write X as an signed LEB value at offset Offset in Stream, padded +// Write Value as an signed LEB value at offset Offset in Stream, padded // to allow patching. -template -void writePatchableSLEB(raw_pwrite_stream &Stream, int64_t X, uint64_t Offset) { +template +void writePatchableSLEB(raw_pwrite_stream &Stream, T Value, uint64_t Offset) { uint8_t Buffer[W]; - unsigned SizeLen = encodeSLEB128(X, Buffer, W); + unsigned SizeLen = encodeSLEB128(Value, Buffer, W); assert(SizeLen == W); Stream.pwrite((char *)Buffer, SizeLen, Offset); } -// Write X as a plain integer value at offset Offset in Stream. -static void patchI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { +static void writePatchableU32(raw_pwrite_stream &Stream, uint32_t Value, + uint64_t Offset) { + writePatchableULEB(Stream, Value, Offset); +} + +static void writePatchableS32(raw_pwrite_stream &Stream, int32_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +static void writePatchableU64(raw_pwrite_stream &Stream, uint64_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +static void writePatchableS64(raw_pwrite_stream &Stream, int64_t Value, + uint64_t Offset) { + writePatchableSLEB(Stream, Value, Offset); +} + +// Write Value as a plain integer value at offset Offset in Stream. +static void patchI32(raw_pwrite_stream &Stream, uint32_t Value, + uint64_t Offset) { uint8_t Buffer[4]; - support::endian::write32le(Buffer, X); + support::endian::write32le(Buffer, Value); Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); } -static void patchI64(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) { +static void patchI64(raw_pwrite_stream &Stream, uint64_t Value, + uint64_t Offset) { uint8_t Buffer[8]; - support::endian::write64le(Buffer, X); + support::endian::write64le(Buffer, Value); Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); } @@ -420,8 +442,8 @@ void WasmObjectWriter::endSection(SectionBookkeeping &Section) { // Write the final section size to the payload_len field, which follows // the section id byte. - writePatchableLEB<5>(static_cast(W->OS), Size, - Section.SizeOffset); + writePatchableU32(static_cast(W->OS), Size, + Section.SizeOffset); } // Emit the Wasm header. @@ -752,7 +774,7 @@ void WasmObjectWriter::applyRelocations( RelEntry.Offset; LLVM_DEBUG(dbgs() << "applyRelocation: " << RelEntry << "\n"); - auto Value = getProvisionalValue(RelEntry, Layout); + uint64_t Value = getProvisionalValue(RelEntry, Layout); switch (RelEntry.Type) { case wasm::R_WASM_FUNCTION_INDEX_LEB: @@ -761,10 +783,10 @@ void WasmObjectWriter::applyRelocations( case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_TAG_INDEX_LEB: case wasm::R_WASM_TABLE_NUMBER_LEB: - writePatchableLEB<5>(Stream, Value, Offset); + writePatchableU32(Stream, Value, Offset); break; case wasm::R_WASM_MEMORY_ADDR_LEB64: - writePatchableLEB<10>(Stream, Value, Offset); + writePatchableU64(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_I32: @@ -784,14 +806,14 @@ void WasmObjectWriter::applyRelocations( case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: - writePatchableSLEB<5>(Stream, Value, Offset); + writePatchableS32(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_SLEB64: case wasm::R_WASM_TABLE_INDEX_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_SLEB64: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB64: - writePatchableSLEB<10>(Stream, Value, Offset); + writePatchableS64(Stream, Value, Offset); break; default: llvm_unreachable("invalid relocation type"); diff --git a/llvm/test/MC/WebAssembly/reloc-code.s b/llvm/test/MC/WebAssembly/reloc-code.s index cd301098f9a8..c7b05f81a7ff 100644 --- a/llvm/test/MC/WebAssembly/reloc-code.s +++ b/llvm/test/MC/WebAssembly/reloc-code.s @@ -14,12 +14,12 @@ f1: # Call functions at `a` and `b` indirectly. i32.const 0 - i32.load a + i32.load a - 10 call_indirect () -> (i64) drop i32.const 0 - i32.load b + i32.load b + 20 call_indirect () -> (i32) drop @@ -49,7 +49,7 @@ b: # CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # CHECK-NEXT: Offset: 0x7 # CHECK-NEXT: Symbol: a -# CHECK-NEXT: Addend: 0 +# CHECK-NEXT: Addend: -10 # CHECK-NEXT: } # CHECK-NEXT: Relocation { # CHECK-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -60,7 +60,7 @@ b: # CHECK-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # CHECK-NEXT: Offset: 0x18 # CHECK-NEXT: Symbol: b -# CHECK-NEXT: Addend: 0 +# CHECK-NEXT: Addend: 20 # CHECK-NEXT: } # CHECK-NEXT: Relocation { # CHECK-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -87,7 +87,7 @@ b: # REF-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # REF-NEXT: Offset: 0x7 # REF-NEXT: Symbol: a -# REF-NEXT: Addend: 0 +# REF-NEXT: Addend: -10 # REF-NEXT: } # REF-NEXT: Relocation { # REF-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6) @@ -103,7 +103,7 @@ b: # REF-NEXT: Type: R_WASM_MEMORY_ADDR_LEB (3) # REF-NEXT: Offset: 0x1C # REF-NEXT: Symbol: b -# REF-NEXT: Addend: 0 +# REF-NEXT: Addend: 20 # REF-NEXT: } # REF-NEXT: Relocation { # REF-NEXT: Type: R_WASM_TYPE_INDEX_LEB (6)