llvm / llvm/llvm-project

[lldb] misparse DWARF expressions after oversized LEB128 operands

Open
#202,293 1 comment 0 reactions 0 assignees View on GitHub
lldb
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

I found that LLDB/LLVM's DWARF expression parser can mishandle oversized LEB128 operands.

The issue is not limited to a single opcode. Any DWARF expression opcode whose operand is encoded as ULEB128 or SLEB128 can be affected if the encoded value does not fit in the parser's current `uint64_t` / `int64_t` representation.

In that case, the LEB decode error is not surfaced cleanly by `DWARFExpression::Operation::extract` (in [llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp](https://github.com/llvm/llvm-project/blob/main/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp)) . The operand may be treated as zero while the parser does not consume the full LEB payload. This can desynchronize the expression stream and cause later payload bytes to be interpreted as DWARF opcodes.

## Minimal Case

I am using a raw DWARF expression here because this issue is about DWARF operand decoding itself, and the reduced form is clearer than a source-level reproducer.

One minimal case is:

```text
DW_OP_constu [0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x02]
# the ULEB128 payload encodes 2^64, so on a 64-bit target it should truncate to 0
DW_OP_lit0
DW_OP_eq
DW_OP_stack_value
```

On a 64-bit target, `2^64` should truncate to generic zero, so the expression should behave exactly like:

```text
DW_OP_constu 0
DW_OP_lit0
DW_OP_eq
DW_OP_stack_value
```

and therefore evaluate to `1`.

GDB executes this correctly and produces the final result `1`.

LLDB does not. Instead, it reports:

```text

```

This strongly suggests that LLDB is not consuming the full, complete LEB payload. The terminating byte `0x02` appears to be left behind and then treated as the next DWARF opcode.

The same issue reproduces for `DW_OP_consts` with the same byte sequence interpreted as a legal SLEB128 encoding of `+2^64`.

## Source-Level Cause
`DWARFExpression::Operation::extract` (in [llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp](https://github.com/llvm/llvm-project/blob/main/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp)) decodes LEB operands through:

```cpp
Data.getULEB128(&Offset)
Data.getSLEB128(&Offset)
```

without passing an Error.

If decodeULEB128 / decodeSLEB128 detects that the value is too large for `uint64_t` / `int64_t`, the error is not surfaced. The helper returns zero, and the offset may not be advanced to the end of the LEB payload. Operation::extract then records the stale offset as the operation end and returns success, causing the next iterator step to parse from the wrong byte.

## Expected Behavior
For `DW_OP_constu` / `DW_OP_consts`, LLDB should consume the full well-formed LEB128 payload and truncate the value to the target generic stack element size.

For other LEB-operand opcodes where truncation is not valid, LLDB should emit an error or warning, or at least tell the user that parsing failed for a particular opcode.

Contributor guide

Open the contributing guide

Research direction

Start in llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp at DWARFExpression::Operation::extract, then trace the Data.getULEB128 and Data.getSLEB128 calls and their offset handling. Reproduce the raw DW_OP_constu and DW_OP_consts expressions from the issue. Done means oversized operands consume their complete LEB payload, preserve valid truncation where specified, and do not desynchronize later opcode parsing.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.