Author: Yao Qi Date: 2026-08-28T11:03:15+01:00 New Revision: 2dad524fefb4a2b51d66bfefba489e5270cd58be
URL: https://github.com/llvm/llvm-project/commit/2dad524fefb4a2b51d66bfefba489e5270cd58be DIFF: https://github.com/llvm/llvm-project/commit/2dad524fefb4a2b51d66bfefba489e5270cd58be.diff LOG: [lldb] Reject an unusable memory-cache-line-size (#219217) `target.process.memory-cache-line-size` is an unbounded `UInt64`, so `settings set` accepts 0, and `MemoryCache` keeps the value in a `uint32_t`, so it also accepts any multiple of 2^32, which truncates to 0. Every consumer then takes a remainder by 0. `Process::ReadCStringFromMemory` divides on the first iteration of its loop, before it touches inferior memory, so any address reproduces it. On an x86_64 host that is a `SIGFPE` and lldb dies; on AArch64 `udiv` by zero yields 0, so `addr % 0` evaluates to `addr` and the subtraction underflows to a huge chunk size, and the bug hides. ``` $ lldb -b \ -o 'settings set target.process.memory-cache-line-size 0' \ -o 'target create --core linux-x86_64.core' Floating point exception: 8 ``` Under UBSan on any host, with `lldb/test/API/tools/lldb-dap/coreFile/linux-x86_64.core`: ``` lldb/source/Target/Process.cpp:2388:40: runtime error: division by zero #0 lldb_private::Process::ReadCStringFromMemory #2 ProcessElfCore::GetMainExecutableModuleSpec #3 ProcessElfCore::DoLoadCore #4 lldb_private::Process::LoadCore ``` The truncating case crashes the same way while `settings show` reports a value that is not 0, so nothing in the UI hints at the cause: ``` (lldb) settings set target.process.memory-cache-line-size 4294967296 (lldb) settings show target.process.memory-cache-line-size target.process.memory-cache-line-size (unsigned) = 4294967296 ``` The cache uses 0. 4294967297 truncates to 1, so the cache uses a one-byte line. Bound the property once instead of at each use, from 1 to `UINT32_MAX`, the way `Debugger` bounds `term-width` and `term-height`. Rejecting beats clamping, because `settings show` reads the stored value back and would otherwise report a number the cache does not use: ``` (lldb) settings set target.process.memory-cache-line-size 4294967296 error: 4294967296 is out of range, valid values must be between 1 and 4294967295. ``` `OptionValueProperties::CreateLocalCopy` deep-copies the option values, so the bounds also apply to each process's own collection, not just the global one. Added: Modified: lldb/source/Target/Process.cpp lldb/unittests/Target/MemoryTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index fdc56e1c310eb..033638f680934 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -38,6 +38,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" @@ -173,6 +174,13 @@ ProcessProperties::ProcessProperties(lldb_private::Process *process) // Global process properties, set them up one time m_collection_sp = std::make_shared<ProcessOptionValueProperties>("process"); m_collection_sp->Initialize(g_process_properties_def); + // MemoryCache divides by the cache line size and holds it in a uint32_t, so + // reject a value it could not use. + OptionValueUInt64 *line_size = + m_collection_sp->GetPropertyAtIndexAsOptionValueUInt64( + ePropertyMemCacheLineSize); + line_size->SetMinimumValue(1); + line_size->SetMaximumValue(UINT32_MAX); m_collection_sp->AppendProperty( "thread", "Settings specific to threads.", true, Thread::GetGlobalProperties().GetValueProperties()); diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp index 73f17ca4ce122..97402c4cb6e03 100644 --- a/lldb/unittests/Target/MemoryTest.cpp +++ b/lldb/unittests/Target/MemoryTest.cpp @@ -445,6 +445,45 @@ TEST_F(MemoryTest, TestReadStopsAtAnInvalidRange) { EXPECT_TRUE(inside_error.Fail()); } +TEST_F(MemoryTest, TestUnusableCacheLineSize) { + ArchSpec arch("arm64-apple-macosx"); + + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); + + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + + // A Process copies the global properties when it is constructed, so the + // setting must be in place before CreateProcess, and put back afterwards. + struct SettingGuard { + ~SettingGuard() { + Process::GetGlobalProperties().SetPropertyValue( + nullptr, eVarSetOperationClear, "memory-cache-line-size", ""); + } + } restore_setting; + + auto set_line_size = [](const char *setting) { + return Process::GetGlobalProperties().SetPropertyValue( + nullptr, eVarSetOperationAssign, "memory-cache-line-size", setting); + }; + + // A usable setting must take effect, or the checks below prove nothing. + ASSERT_TRUE(set_line_size("256").Success()); + TargetSP target_sp = CreateTarget(debugger_sp, arch); + DummyProcess *process = + static_cast<DummyProcess *>(CreateProcess(target_sp).get()); + EXPECT_EQ(process->GetMemoryCacheLineSize(), 256u); + + for (const char *setting : {"0", "4294967296"}) { + SCOPED_TRACE(setting); + EXPECT_TRUE(set_line_size(setting).Fail()); + // Refused, so the last usable value is still in effect. + EXPECT_EQ(process->GetMemoryCacheLineSize(), 256u); + TargetSP later_target_sp = CreateTarget(debugger_sp, arch); + EXPECT_EQ(CreateProcess(later_target_sp)->GetMemoryCacheLineSize(), 256u); + } +} + TEST_F(MemoryTest, TestReadInteger) { ArchSpec arch("x86_64-apple-macosx-"); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
