From e264a92936000d2a3ed7b4a472a028f483d4bae6 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:53:22 -0500 Subject: [PATCH 1/2] gracefully handle invalid string/vec --- src/etc/lldb_providers.py | 68 ++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f20210323eb..e2d4ac5286ccd 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -305,16 +305,33 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) if length <= 0: return '""' + + no_hi_bit_max: int = 1 << ((pointer.GetByteSize() * 8) - 1) + # technically length isn't a NoHighBit, but length should always be <= capacity + if length >= no_hi_bit_max or capacity >= no_hi_bit_max: + return "" + if pointer.GetValueAsUnsigned() == 0: + return "" + error = SBError() process = pointer.GetProcess() - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - raise Exception("ReadMemory error: %s", error.GetCString()) + try: + data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) + if error.Success(): + return '"' + data.decode("utf8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -443,6 +460,9 @@ def has_children(self) -> bool: class StdStringSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) + self.update() def update(self): @@ -454,7 +474,25 @@ def update(self): .GetChildMemberWithName("pointer") .GetChildMemberWithName("pointer") ) - self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + + self.capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) + + # As of 4/18/2026, LLDB cannot accurately determine the difference between Some("") and None + # this just makes sure we're not trying to access data when the string is clearly in an + # invalid state. + if ( + self.capacity >= self.no_hi_bit_max + or self.data_ptr.GetValueAsUnsigned() == 0 + ): + self.capacity = 0 + self.length = 0 + else: + self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.data_ptr.GetType().GetPointeeType() def has_children(self) -> bool: @@ -928,6 +966,8 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) self.valobj = valobj self.element_type = None + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) self.update() def num_children(self) -> int: @@ -949,15 +989,19 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( - "inner" - ) - + buf: SBValue = self.valobj.GetChildMemberWithName("buf") self.data_ptr = unwrap_unique_or_non_null( - self.buf.GetChildMemberWithName("ptr") + buf.GetChildMemberWithName("inner").GetChildMemberWithName("ptr") ) + capacity: int = buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + + if capacity >= self.no_hi_bit_max or self.data_ptr.GetValueAsUnsigned() == 0: + self.capacity = 0 + self.length = 0 + else: + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) if not self.element_type.IsValid(): From 23bd862a63c5c5398e96902d1960a79084107aad Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Tue, 28 Apr 2026 00:12:47 -0500 Subject: [PATCH 2/2] move string read logic to standalone function --- src/etc/lldb_providers.py | 46 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index e2d4ac5286ccd..1cab5393eaa15 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -15,7 +15,7 @@ from rust_types import is_tuple_fields if TYPE_CHECKING: - from lldb import SBValue, SBType, SBTypeStaticField, SBTarget + from lldb import SBValue, SBType, SBTypeStaticField, SBTarget, SBProcess # from lldb.formatters import Logger @@ -289,6 +289,28 @@ def vec_to_string(vec: SBValue) -> str: ) +def read_string( + process: SBProcess, address: int, length: int, error: Optional[SBError] = None +) -> str: + """Reads a string from running process's memory. If `error` is passed in, it will be passed + to the `SBProcess.ReadMemory` call, and will reflect any errors after the function is called. + + If any error or exception occurs, a placeholder byte array of the form "" will + be returned instead.""" + + if error is None: + error = SBError() + try: + data = process.ReadMemory(address, length, error) + if error.Success(): + return '"' + data.decode("utf-8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" + + def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): inner_vec = ( valobj.GetNonSyntheticValue() @@ -321,17 +343,9 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): if pointer.GetValueAsUnsigned() == 0: return "" - error = SBError() process = pointer.GetProcess() - try: - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - return f"" - except Exception as e: - print(f"Unable to generate String summary: {e.__cause__}") - return "" + + return read_string(process, pointer.GetValueAsAddress(), length) def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -380,15 +394,9 @@ def StdPathSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: data_ptr = valobj.GetChildMemberWithName("data_ptr") start = data_ptr.GetValueAsUnsigned() - error = SBError() process = data_ptr.GetProcess() - data = process.ReadMemory(start, length, error) - if PY3: - try: - data = data.decode(encoding="UTF-8") - except UnicodeDecodeError: - return "%r" % data - return '"%s"' % data + + return read_string(process, start, length) def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque):