[lld] [ELF] Fix crash when INCLUDE'd linker script ends mid-assignment (PR #192198)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 15 00:06:43 PDT 2026


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/192198

`readAssignment` builds `commandString` from a `[oldS, curTok)` byte
span. If the expression is read from an INCLUDE'd script whose buffer is
exhausted before the terminator, the two pointers come from different
`MemoryBuffer`s, leading to `std::length_error`.

Fix #190376: Skip the map-file string if the
buffer changed.


>From 58d912aec114c541e9a37ef923e34866a8190a84 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 14 Apr 2026 23:48:05 -0700
Subject: [PATCH] [ELF] Fix crash when INCLUDE'd linker script ends
 mid-assignment

`readAssignment` builds `commandString` from a `[oldS, curTok)` byte
span. If the expression is read from an INCLUDE'd script whose buffer is
exhausted before the terminator, the two pointers come from different
`MemoryBuffer`s, leading to `std::length_error`.

Fix #190376: Skip the map-file string if the
buffer changed.
---
 lld/ELF/ScriptParser.cpp                      | 10 ++++--
 .../ELF/linkerscript/include-mid-assignment.s | 31 +++++++++++++++++++
 2 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 lld/test/ELF/linkerscript/include-mid-assignment.s

diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 0300b956e6071..c52982d525237 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -1157,6 +1157,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
     return make<SymbolAssignment>(".", readAssert(), 0, getCurrentLocation());
 
   const char *oldS = prevTok.data();
+  const char *oldBufBegin = curBuf.begin;
   SymbolAssignment *cmd = nullptr;
   bool savedSeenRelroEnd = ctx.script->seenRelroEnd;
   const StringRef op = peek();
@@ -1179,8 +1180,13 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
 
   if (cmd) {
     cmd->dataSegmentRelroEnd = !savedSeenRelroEnd && ctx.script->seenRelroEnd;
-    cmd->commandString = StringRef(oldS, curTok.data() - oldS).str();
-    squeezeSpaces(cmd->commandString);
+    // If the inner buffer of an INCLUDE was exhausted while reading the
+    // expression, oldS lives in a popped buffer and the [oldS, curTok) span is
+    // not a valid pointer range.
+    if (curBuf.begin == oldBufBegin) {
+      cmd->commandString = StringRef(oldS, curTok.data() - oldS).str();
+      squeezeSpaces(cmd->commandString);
+    }
     expect(";");
   }
   return cmd;
diff --git a/lld/test/ELF/linkerscript/include-mid-assignment.s b/lld/test/ELF/linkerscript/include-mid-assignment.s
new file mode 100644
index 0000000000000..783e795bb4c77
--- /dev/null
+++ b/lld/test/ELF/linkerscript/include-mid-assignment.s
@@ -0,0 +1,31 @@
+# REQUIRES: x86
+## When recording `commandString`, an assignment in an INCLUDEd linker script
+## that is missing the trailing ';' must not crash lld. Same for a PROVIDE
+## whose inner buffer is exhausted before the closing ')'.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: not ld.lld a.o -T outer.lds 2>&1 | FileCheck %s
+# RUN: not ld.lld a.o -T outer2.lds 2>&1 | FileCheck %s --check-prefix=CHECK2
+
+# CHECK: error: outer.lds:2: ; expected, but got {{.*}}
+# CHECK2: error: outer2.lds:2: ) expected, but got {{.*}}
+
+#--- outer.lds
+INCLUDE "inc.lds"
+SECTIONS { .text : { *(.text*) } }
+
+#--- inc.lds
+foo = 1
+
+#--- outer2.lds
+INCLUDE "inc2.lds"
+SECTIONS { .text : { *(.text*) } }
+
+#--- inc2.lds
+PROVIDE(bar = 1
+
+#--- a.s
+.globl _start
+_start:
+  ret



More information about the llvm-commits mailing list