Repository navigation
rust-lldb: Incorrectly display Option::None as Option::Some #152743
Description
Activity
- addedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Feb 17, 2026 Smells like UB. Are you able to provide (hopefully minimized) example code that reproduces this error?
@rustbot label +A-debuggers-lldb +S-needs-repro
- addedA-debuggers-lldbArea: lldbArea: lldbS-needs-reproStatus: This issue has no reproduction and needs a reproduction to make progress.Status: This issue has no reproduction and needs a reproduction to make progress.
on Feb 17, 2026 Sorry, I can't. My experiments all failed. But I managed to make lldb raise out a lot of errors. The basic idea is to write something like
0xfeto the flag byte ofOption, and write meaningless bytes into the following regions. However, I can't see why such things can happen, due to the fact that the data in the origin issue is stored in the heap, and there's no unsafe operation that could have messed it up.write something like
0xfeto the flag byte ofOptionBut that causes undefined behaviour.
I just realized that it was in the heap, but after representing the number rust-lldb printed in hex, I found that it's not that meaningless, because it has something to do with the address the kernel was loaded. However, I still can't see why rust-lldb thought it was
Option::Some.If the enum layout was “niche optimized”, then it is likely (but not guaranteed) that any value other than whatever was assigned to
Nonewould be treated asSome, at least as far as the compiled Rust code is concerned (I don't know how rust-lldb works). So, either rust-lldb is printing as accurately, or rust-lldb is printing invalid data as if it were valid data, but we don’t know without seeing the full definition of the type stored in theOption. Can you share it?Sorry, I can't. My experiments all failed. But I managed to make lldb raise out a lot of errors. The basic idea is to write something like 0xfe to the flag byte of Option, and write meaningless bytes into the following regions. However, I can't see why such things can happen, due to the fact that the data in the origin issue is stored in the heap, and there's no unsafe operation that could have messed it up.
There is no guarantee that
Optionuses a "flag byte". TheGenericAddresstype thatMappedGascontains this enum which would permit a niche optimization for anyOption<MappedGas>: https://docs.rs/acpi/latest/acpi/address/enum.AddressSpace.htmlDo not assume the layout of
Optionwhere it is not in the very limited set of guaranteed cases, as it may adopt radically different layouts based onT.- addedC-gubCategory: the reverse of a compiler bug is generally UBCategory: the reverse of a compiler bug is generally UBand removedC-bugCategory: This is a bug.Category: This is a bug.
on Feb 18, 2026 - removedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Feb 18, 2026 - removedC-gubCategory: the reverse of a compiler bug is generally UBCategory: the reverse of a compiler bug is generally UB
on Feb 21, 2026 Hm, something's off here still, actually, that was a red herring now that I think about it. My apologies.
@zzjrabbit Now that I look properly, the PR which added enum support to rust-lldb unfortunately made several tests not run in CI: #124781
See #151966 for what I mean about "tests not running in CI".
I believe this bug may be a duplicate of #150392 though.
It's okay, after all, you have to deal with so many issues. But I don't think this is a duplicate of #150392. Because in that issue's example, the compiler can easily optimize the layout of
Option. However, in my case, this cannot happen, because there is no way to know whether the pm1 register block is present or not during compile time. So what's happening here might not be quite the same as what happened in that issue.I strongly suspect this is due to the implementation of the provider. Specifically, when it searches for a variant of the index that matches the discriminant. If it cant find a match, it defaults to 0th variant.
If my hunch is right, the untagged variant isnt guaranteed to be the 0th variant, thus in some cases it can end up determining the wrong variant. For a more correct implementation, see this code.
I'll look into this more as soon as i'm able, but if possible would you be able to modify
getCurrentVariantIndexin yourlldb_providers.pyand see if the error still occurs?Sorry, I can't, because a new term is about to start. And I think I have no time to do this in a few months.
Oh, I'm an idiot. I've done this same investigation like 3 times now and I keep forgetting it's still broken. The current visualizer script is written in a way that looks wrong at first, but it'll technically do the correct thing.
The underlying issue is LLDB's fault. I opened an issue for it a month ago.
TL;DR, when LLDB reads variant member DWARF info, they read it into a
uint32_t. When they create the variant names, they directly append this discriminant value to the variant name. Discriminant values that rustc outputs can be up to 128 bits.StringwrapsVec, andVecis effectively a{ptr: NonNull<u8> , cap: NoHighBit<usize> , len: usize}. For whatever reason rustc niche-optimizes off of theNoHighBitrather than theNonNull. This means the null-variant's value is1<<63. C++ silently truncates this value to0to fit it in theuint32_t. When thediscris read at debug-time, it's read as anunsigned long long, since that's the type the DWARF data reports it as. It accurately reads9223372036854775808. The visualizer scripts compare that against0and don't find a match, so it defaults to the untagged variant (which isSome).Since
None's expected variant is0, if we have anOption<String>with capacity 0, thediscrat run-time is0. This causes a match on theNonevariant, which is why empty strings are also visualized incorrectly.Since LLDB is just straight up reading the number wrong, there's not much I can do to modify the visualizer scripts or rustc output to fix this without causing even bigger issues (e.g.
discr = discr & u32::MAXbefore comparison)This also explains why it's an intermittent issue. Technically, it affects all sum-type enums on
*-gnutargets, including non-niche enums, but it's very often not a problem because usuallydiscr < u32::MAX, or defaulting to the untagged variant is coincidentally correct.
I am developing a kernel, and when I tried debugging with rust-lldb, I found this:
While I unwrapped pm1b with the following code:
And there's the output.
What made me sure that this is a bug of rust-lldb is the length of the region, which is too large to be valid.
And I am using
1.95.0-nightly (ce69df6f7 2026-02-12)toolchain, while the version of lldb is 21.1.8 from nixpkgs.