[MC][WebAssembly] Fix crash when relocation addend underlows U32
authorSam Clegg <sbc@chromium.org>
Thu, 24 Feb 2022 06:23:05 +0000 (22:23 -0800)
committerSam Clegg <sbc@chromium.org>
Fri, 25 Feb 2022 15:13:15 +0000 (07:13 -0800)
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
lld/wasm/InputChunks.cpp
lld/wasm/InputFiles.cpp
lld/wasm/InputFiles.h
llvm/lib/MC/WasmObjectWriter.cpp
llvm/test/MC/WebAssembly/reloc-code.s

index acd57ce..4b23ed1 100644 (file)
@@ -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
index 7973dce..2423785 100644 (file)
@@ -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<uint32_t>(value), loc, 5);
       break;
     case R_WASM_MEMORY_ADDR_LEB64:
       encodeULEB128(value, loc, 10);
index 0758bb9..1e69f16 100644 (file)
@@ -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:
index cf2c173..c14d763 100644 (file)
@@ -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];
   };
index e55635d..27023f1 100644 (file)
@@ -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 <int W>
-void writePatchableLEB(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) {
+template <typename T, int W>
+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 <int W>
-void writePatchableSLEB(raw_pwrite_stream &Stream, int64_t X, uint64_t Offset) {
+template <typename T, int W>
+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<uint32_t, 5>(Stream, Value, Offset);
+}
+
+static void writePatchableS32(raw_pwrite_stream &Stream, int32_t Value,
+                              uint64_t Offset) {
+  writePatchableSLEB<int32_t, 5>(Stream, Value, Offset);
+}
+
+static void writePatchableU64(raw_pwrite_stream &Stream, uint64_t Value,
+                              uint64_t Offset) {
+  writePatchableSLEB<uint64_t, 10>(Stream, Value, Offset);
+}
+
+static void writePatchableS64(raw_pwrite_stream &Stream, int64_t Value,
+                              uint64_t Offset) {
+  writePatchableSLEB<int64_t, 10>(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<raw_pwrite_stream &>(W->OS), Size,
-                       Section.SizeOffset);
+  writePatchableU32(static_cast<raw_pwrite_stream &>(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");
index cd30109..c7b05f8 100644 (file)
@@ -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)