Skip to content

Commit 70f59fc

Browse files
committed
[lldb] Improve error handling in ObjectFileWasm (llvm#154433)
Improve error handling in ObjectFileWasm by using helpers that wrap their result in an llvm::Expected. The helper to read a Wasm string now return an Expected<std::string> and I created a helper to parse 32-bit ULEBs that returns an Expected<uint32_t>. (cherry picked from commit 7cd6179)
1 parent bb46125 commit 70f59fc

File tree

1 file changed

+102
-77
lines changed

1 file changed

+102
-77
lines changed

lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp

Lines changed: 102 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,41 @@ LLDB_PLUGIN_DEFINE(ObjectFileWasm)
3636
static const uint32_t kWasmHeaderSize =
3737
sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion);
3838

39+
/// Helper to read a 32-bit ULEB using LLDB's DataExtractor.
40+
static inline llvm::Expected<uint32_t> GetULEB32(DataExtractor &data,
41+
lldb::offset_t &offset) {
42+
const uint64_t value = data.GetULEB128(&offset);
43+
if (value > std::numeric_limits<uint32_t>::max())
44+
return llvm::createStringError("ULEB exceeds 32 bits");
45+
return value;
46+
}
47+
48+
/// Helper to read a 32-bit ULEB using LLVM's DataExtractor.
49+
static inline llvm::Expected<uint32_t>
50+
GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
51+
const uint64_t value = data.getULEB128(c);
52+
if (!c)
53+
return c.takeError();
54+
if (value > std::numeric_limits<uint32_t>::max())
55+
return llvm::createStringError("ULEB exceeds 32 bits");
56+
return value;
57+
}
58+
59+
/// Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes.
60+
static inline llvm::Expected<std::string>
61+
GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
62+
llvm::Expected<uint32_t> len = GetULEB32(data, c);
63+
if (!len)
64+
return len.takeError();
65+
66+
llvm::SmallVector<uint8_t, 32> str_storage;
67+
data.getU8(c, str_storage, *len);
68+
if (!c)
69+
return c.takeError();
70+
71+
return std::string(toStringRef(llvm::ArrayRef(str_storage)));
72+
}
73+
3974
/// Checks whether the data buffer starts with a valid Wasm module header.
4075
static bool ValidateModuleHeader(const DataBufferSP &data_sp) {
4176
if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize)
@@ -51,32 +86,6 @@ static bool ValidateModuleHeader(const DataBufferSP &data_sp) {
5186
return version == llvm::wasm::WasmVersion;
5287
}
5388

54-
// FIXME: Use lldb::DataExtractor instead of llvm::DataExtractor.
55-
static std::optional<std::string>
56-
GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
57-
// A Wasm string is encoded as a vector of UTF-8 codes.
58-
// Vectors are encoded with their u32 length followed by the element
59-
// sequence.
60-
uint64_t len = data.getULEB128(c);
61-
if (!c) {
62-
consumeError(c.takeError());
63-
return std::nullopt;
64-
}
65-
66-
if (len > std::numeric_limits<uint32_t>::max()) {
67-
return std::nullopt;
68-
}
69-
70-
llvm::SmallVector<uint8_t, 32> str_storage;
71-
data.getU8(c, str_storage, len);
72-
if (!c) {
73-
consumeError(c.takeError());
74-
return std::nullopt;
75-
}
76-
77-
return std::string(toStringRef(llvm::ArrayRef(str_storage)));
78-
}
79-
8089
char ObjectFileWasm::ID;
8190

8291
void ObjectFileWasm::Initialize() {
@@ -183,9 +192,12 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {
183192
// identifying the custom section, followed by an uninterpreted sequence
184193
// of bytes.
185194
lldb::offset_t prev_offset = c.tell();
186-
std::optional<std::string> sect_name = GetWasmString(data, c);
187-
if (!sect_name)
195+
llvm::Expected<std::string> sect_name = GetWasmString(data, c);
196+
if (!sect_name) {
197+
LLDB_LOG_ERROR(GetLog(LLDBLog::Object), sect_name.takeError(),
198+
"failed to parse section name: {0}");
188199
return false;
200+
}
189201

190202
if (payload_len < c.tell() - prev_offset)
191203
return false;
@@ -255,26 +267,26 @@ ParseFunctions(SectionSP code_section_sp) {
255267
code_section_sp->GetSectionData(data);
256268
lldb::offset_t offset = 0;
257269

258-
const uint64_t function_count = data.GetULEB128(&offset);
259-
if (function_count > std::numeric_limits<uint32_t>::max())
260-
return llvm::createStringError("function count overflows uint32_t");
270+
llvm::Expected<uint32_t> function_count = GetULEB32(data, offset);
271+
if (!function_count)
272+
return function_count.takeError();
261273

262274
std::vector<AddressRange> functions;
263-
functions.reserve(function_count);
275+
functions.reserve(*function_count);
264276

265-
for (uint32_t i = 0; i < function_count; ++i) {
266-
const uint64_t function_size = data.GetULEB128(&offset);
267-
if (function_size > std::numeric_limits<uint32_t>::max())
268-
return llvm::createStringError("function size overflows uint32_t");
277+
for (uint32_t i = 0; i < *function_count; ++i) {
278+
llvm::Expected<uint32_t> function_size = GetULEB32(data, offset);
279+
if (!function_size)
280+
return function_size.takeError();
269281
// llvm-objdump considers the ULEB with the function size to be part of the
270282
// function. We can't do that here because that would break symbolic
271283
// breakpoints, as that address is never executed.
272-
functions.emplace_back(code_section_sp, offset, function_size);
284+
functions.emplace_back(code_section_sp, offset, *function_size);
273285

274286
std::optional<lldb::offset_t> next_offset =
275-
llvm::checkedAddUnsigned(offset, function_size);
287+
llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
276288
if (!next_offset)
277-
return llvm::createStringError("function offset overflows uint64_t");
289+
return llvm::createStringError("function offset overflows 64 bits");
278290
offset = *next_offset;
279291
}
280292

@@ -295,44 +307,44 @@ ParseData(SectionSP data_section_sp) {
295307

296308
lldb::offset_t offset = 0;
297309

298-
const uint64_t segment_count = data.GetULEB128(&offset);
299-
if (segment_count > std::numeric_limits<uint32_t>::max())
300-
return llvm::createStringError("segment count overflows uint32_t");
310+
llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset);
311+
if (!segment_count)
312+
return segment_count.takeError();
301313

302314
std::vector<WasmSegment> segments;
303-
segments.reserve(segment_count);
315+
segments.reserve(*segment_count);
304316

305-
for (uint32_t i = 0; i < segment_count; ++i) {
306-
const uint64_t flags = data.GetULEB128(&offset);
307-
if (flags > std::numeric_limits<uint32_t>::max())
308-
return llvm::createStringError("segment flags overflows uint32_t");
317+
for (uint32_t i = 0; i < *segment_count; ++i) {
318+
llvm::Expected<uint32_t> flags = GetULEB32(data, offset);
319+
if (!flags)
320+
return flags.takeError();
309321

310322
// Data segments have a mode that identifies them as either passive or
311323
// active. An active data segment copies its contents into a memory during
312324
// instantiation, as specified by a memory index and a constant expression
313325
// defining an offset into that memory.
314-
if (flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
315-
const uint64_t memidx = data.GetULEB128(&offset);
316-
if (memidx > std::numeric_limits<uint32_t>::max())
317-
return llvm::createStringError("memidx overflows uint32_t");
326+
if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
327+
llvm::Expected<uint32_t> memidx = GetULEB32(data, offset);
328+
if (!memidx)
329+
return memidx.takeError();
318330
}
319331

320-
if ((flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
332+
if ((*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
321333
// Skip over the constant expression.
322334
for (uint8_t b = 0; b != llvm::wasm::WASM_OPCODE_END;)
323335
b = data.GetU8(&offset);
324336
}
325337

326-
const uint64_t segment_size = data.GetULEB128(&offset);
327-
if (segment_size > std::numeric_limits<uint32_t>::max())
328-
return llvm::createStringError("segment size overflows uint32_t");
338+
llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset);
339+
if (!segment_size)
340+
return segment_size.takeError();
329341

330-
segments.emplace_back(data_section_sp, offset, segment_size);
342+
segments.emplace_back(data_section_sp, offset, *segment_size);
331343

332344
std::optional<lldb::offset_t> next_offset =
333-
llvm::checkedAddUnsigned(offset, segment_size);
345+
llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);
334346
if (!next_offset)
335-
return llvm::createStringError("segment offset overflows uint64_t");
347+
return llvm::createStringError("segment offset overflows 64 bits");
336348
offset = *next_offset;
337349
}
338350

@@ -351,9 +363,9 @@ ParseNames(SectionSP name_section_sp,
351363
std::vector<Symbol> symbols;
352364
while (c && c.tell() < data.size()) {
353365
const uint8_t type = data.getU8(c);
354-
const uint64_t size = data.getULEB128(c);
355-
if (size > std::numeric_limits<uint32_t>::max())
356-
return llvm::createStringError("size overflows uint32_t");
366+
llvm::Expected<uint32_t> size = GetULEB32(data, c);
367+
if (!size)
368+
return size.takeError();
357369

358370
switch (type) {
359371
case llvm::wasm::WASM_NAMES_FUNCTION: {
@@ -362,26 +374,34 @@ ParseNames(SectionSP name_section_sp,
362374
return llvm::createStringError("function count overflows uint32_t");
363375

364376
for (uint64_t i = 0; c && i < count; ++i) {
365-
const uint64_t idx = data.getULEB128(c);
366-
const std::optional<std::string> name = GetWasmString(data, c);
367-
if (!name || idx >= function_ranges.size())
377+
llvm::Expected<uint32_t> idx = GetULEB32(data, c);
378+
if (!idx)
379+
return idx.takeError();
380+
llvm::Expected<std::string> name = GetWasmString(data, c);
381+
if (!name)
382+
return name.takeError();
383+
if (*idx >= function_ranges.size())
368384
continue;
369385
symbols.emplace_back(
370386
symbols.size(), Mangled(*name), lldb::eSymbolTypeCode,
371387
/*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false,
372-
/*is_artificial=*/false, function_ranges[idx],
388+
/*is_artificial=*/false, function_ranges[*idx],
373389
/*size_is_valid=*/true, /*contains_linker_annotations=*/false,
374390
/*flags=*/0);
375391
}
376392
} break;
377393
case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {
378-
const uint64_t count = data.getULEB128(c);
379-
if (count > std::numeric_limits<uint32_t>::max())
380-
return llvm::createStringError("data count overflows uint32_t");
381-
for (uint64_t i = 0; c && i < count; ++i) {
382-
const uint64_t idx = data.getULEB128(c);
383-
const std::optional<std::string> name = GetWasmString(data, c);
384-
if (!name || idx >= segments.size())
394+
llvm::Expected<uint32_t> count = GetULEB32(data, c);
395+
if (!count)
396+
return count.takeError();
397+
for (uint32_t i = 0; c && i < *count; ++i) {
398+
llvm::Expected<uint32_t> idx = GetULEB32(data, c);
399+
if (!idx)
400+
return idx.takeError();
401+
llvm::Expected<std::string> name = GetWasmString(data, c);
402+
if (!name)
403+
return name.takeError();
404+
if (*idx >= segments.size())
385405
continue;
386406
// Update the segment name.
387407
segments[i].name = *name;
@@ -397,9 +417,10 @@ ParseNames(SectionSP name_section_sp,
397417
case llvm::wasm::WASM_NAMES_GLOBAL:
398418
case llvm::wasm::WASM_NAMES_LOCAL:
399419
default:
400-
std::optional<uint64_t> offset = llvm::checkedAddUnsigned(c.tell(), size);
420+
std::optional<lldb::offset_t> offset =
421+
llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);
401422
if (!offset)
402-
return llvm::createStringError("offset overflows uint64_t");
423+
return llvm::createStringError("offset overflows 64 bits");
403424
c.seek(*offset);
404425
}
405426
}
@@ -636,11 +657,15 @@ std::optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() {
636657
const uint32_t kBufferSize = 1024;
637658
DataExtractor section_header_data =
638659
ReadImageData(sect_info.offset, kBufferSize);
660+
639661
llvm::DataExtractor data = section_header_data.GetAsLLVM();
640662
llvm::DataExtractor::Cursor c(0);
641-
std::optional<std::string> symbols_url = GetWasmString(data, c);
642-
if (symbols_url)
643-
return FileSpec(*symbols_url);
663+
llvm::Expected<std::string> symbols_url = GetWasmString(data, c);
664+
if (!symbols_url) {
665+
llvm::consumeError(symbols_url.takeError());
666+
return std::nullopt;
667+
}
668+
return FileSpec(*symbols_url);
644669
}
645670
}
646671
return std::nullopt;

0 commit comments

Comments
 (0)