Skip to content

Commit cbf10bc

Browse files
authored
[lld-macho] Avoid infinite recursion when parsing corrupted export tries (#152569)
If an export trie is encoded incorrectly, and one of the children offsets points back to one of the nodes earlier in the serialization, the current code will end up in an infinite recursion, and eventually fail exhausting the available memory. The failure can be avoided if, before recursing, one checks that the offset is valid, that is, that the offset is beyond the current position. This is similar to a check done by llvm-objdump which reports the trie being corrupted.
1 parent 1532116 commit cbf10bc

File tree

5 files changed

+43
-17
lines changed

5 files changed

+43
-17
lines changed

lld/MachO/ExportTrie.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "llvm/BinaryFormat/MachO.h"
4242
#include "llvm/Support/LEB128.h"
4343
#include <optional>
44+
#include <unordered_set>
4445

4546
using namespace llvm;
4647
using namespace lld;
@@ -296,23 +297,33 @@ namespace {
296297
// Parse a serialized trie and invoke a callback for each entry.
297298
class TrieParser {
298299
public:
299-
TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback)
300-
: start(buf), end(start + size), callback(callback) {}
300+
TrieParser(const std::string &fileName, const uint8_t *buf, size_t size,
301+
const TrieEntryCallback &callback)
302+
: fileName(fileName), start(buf), end(start + size), callback(callback) {}
301303

302-
void parse(const uint8_t *buf, const Twine &cumulativeString);
304+
void parse(const uint8_t *buf, const Twine &cumulativeString,
305+
std::unordered_set<size_t> &visited);
303306

304-
void parse() { parse(start, ""); }
307+
void parse() {
308+
std::unordered_set<size_t> visited;
309+
parse(start, "", visited);
310+
}
305311

312+
const std::string fileName;
306313
const uint8_t *start;
307314
const uint8_t *end;
308315
const TrieEntryCallback &callback;
309316
};
310317

311318
} // namespace
312319

313-
void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) {
320+
void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString,
321+
std::unordered_set<size_t> &visited) {
314322
if (buf >= end)
315-
fatal("Node offset points outside export section");
323+
fatal(fileName + ": export trie node offset points outside export section");
324+
325+
size_t currentOffset = buf - start;
326+
visited.insert(currentOffset);
316327

317328
unsigned ulebSize;
318329
uint64_t terminalSize = decodeULEB128(buf, &ulebSize);
@@ -331,14 +342,18 @@ void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) {
331342
buf += substring.size() + 1;
332343
offset = decodeULEB128(buf, &ulebSize);
333344
buf += ulebSize;
334-
parse(start + offset, cumulativeString + substring);
345+
if (visited.find(offset) != visited.end())
346+
fatal(fileName + ": export trie child node infinite loop");
347+
parse(start + offset, cumulativeString + substring, visited);
335348
}
349+
350+
visited.erase(currentOffset);
336351
}
337352

338-
void macho::parseTrie(const uint8_t *buf, size_t size,
339-
const TrieEntryCallback &callback) {
353+
void macho::parseTrie(const std::string &fileName, const uint8_t *buf,
354+
size_t size, const TrieEntryCallback &callback) {
340355
if (size == 0)
341356
return;
342357

343-
TrieParser(buf, size, callback).parse();
358+
TrieParser(fileName, buf, size, callback).parse();
344359
}

lld/MachO/ExportTrie.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class TrieBuilder {
4141
using TrieEntryCallback =
4242
llvm::function_ref<void(const llvm::Twine & /*name*/, uint64_t /*flags*/)>;
4343

44-
void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &);
44+
void parseTrie(const std::string &fileName, const uint8_t *buf, size_t size,
45+
const TrieEntryCallback &);
4546

4647
} // namespace lld::macho
4748

lld/MachO/InputFiles.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,12 +1789,13 @@ void DylibFile::parseExportedSymbols(uint32_t offset, uint32_t size) {
17891789
auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
17901790
std::vector<TrieEntry> entries;
17911791
// Find all the $ld$* symbols to process first.
1792-
parseTrie(buf + offset, size, [&](const Twine &name, uint64_t flags) {
1793-
StringRef savedName = saver().save(name);
1794-
if (handleLDSymbol(savedName))
1795-
return;
1796-
entries.push_back({savedName, flags});
1797-
});
1792+
parseTrie(toString(this), buf + offset, size,
1793+
[&](const Twine &name, uint64_t flags) {
1794+
StringRef savedName = saver().save(name);
1795+
if (handleLDSymbol(savedName))
1796+
return;
1797+
entries.push_back({savedName, flags});
1798+
});
17981799

17991800
// Process the "normal" symbols.
18001801
for (TrieEntry &entry : entries) {
8.55 KB
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
3+
# RUN: not %lld -o %t %t.o %S/Inputs/macho-trie-node-loop 2>&1 | FileCheck %s
4+
# CHECK: error:
5+
# CHECK-SAME: /Inputs/macho-trie-node-loop: export trie child node infinite loop
6+
7+
.globl _main
8+
_main:
9+
ret

0 commit comments

Comments
 (0)