From 3db2f8e9b17027592a24cf677656078b9324d816 Mon Sep 17 00:00:00 2001 From: Matt Wozniski Date: Wed, 11 Sep 2024 13:43:21 -0400 Subject: [PATCH] Fix decoding Python 3.13 stacks without symbols The eager `isValid` check that we implemented originally fails when symbols aren't available, as we're unable to look up `PyCode_Type` to compare the type of `f_executable` against it. Instead, let's treat every `f_executable` as though it's a `PyCode_Type` without checking in advance. If it's not able to be interpreted as a `PyCode_Type`, we'll expect a `RemoteMemCopyError` from chasing an invalid pointer at some point, and can handle that the way that we have been handling `!isValid`. Signed-off-by: Matt Wozniski --- src/pystack/_pystack/pycode.cpp | 39 ++++++++------------------------ src/pystack/_pystack/pycode.h | 3 +-- src/pystack/_pystack/pyframe.cpp | 9 +++++++- 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/pystack/_pystack/pycode.cpp b/src/pystack/_pystack/pycode.cpp index 2420937f..8d8c9505 100644 --- a/src/pystack/_pystack/pycode.cpp +++ b/src/pystack/_pystack/pycode.cpp @@ -162,41 +162,11 @@ getLocationInfo( return location_info; } -static bool -isValid(const std::shared_ptr& manager, remote_addr_t addr) -{ - if (manager->versionIsAtLeast(3, 13)) { - // In Python 3.13, the frame f_executable field can be a code object or a bunch - // of other possible types (including None). We consider valid only the cases - // where it is a code object. - remote_addr_t pycodeobject_addr = manager->getAddressFromCache("PyCode_Type"); - if (pycodeobject_addr == 0) { - Object code_obj(manager, addr); - if (code_obj.objectType() == Object::ObjectType::CODE) { - manager->registerAddressInCache("PyCode_Type", code_obj.typeAddr()); - return true; - } - return false; - } else { - Structure obj(manager, addr); - return obj.getField(&py_object_v::o_ob_type) == pycodeobject_addr; - } - } - return true; -} - CodeObject::CodeObject( const std::shared_ptr& manager, remote_addr_t addr, uintptr_t lasti) { - if (!isValid(manager, addr)) { - d_filename = "???"; - d_scope = "???"; - d_location_info = LocationInfo{0, 0, 0, 0}; - d_narguments = 0; - return; - } LOG(DEBUG) << std::hex << std::showbase << "Copying code struct from address " << addr; Structure code(manager, addr); @@ -235,6 +205,15 @@ CodeObject::CodeObject( }); } +CodeObject::CodeObject(std::string filename, std::string scope, LocationInfo location_info) +: d_filename(filename) +, d_scope(scope) +, d_location_info(location_info) +, d_narguments() +, d_varnames() +{ +} + std::string CodeObject::Filename() const { diff --git a/src/pystack/_pystack/pycode.h b/src/pystack/_pystack/pycode.h index 32c5531e..c4a2706b 100644 --- a/src/pystack/_pystack/pycode.h +++ b/src/pystack/_pystack/pycode.h @@ -27,6 +27,7 @@ class CodeObject const std::shared_ptr& manager, remote_addr_t addr, uintptr_t lastli); + CodeObject(std::string filename, std::string scope, LocationInfo location_info); // Getters std::string Filename() const; @@ -41,8 +42,6 @@ class CodeObject std::string d_scope; LocationInfo d_location_info; int d_narguments; - - private: std::vector d_varnames; }; } // namespace pystack diff --git a/src/pystack/_pystack/pyframe.cpp b/src/pystack/_pystack/pyframe.cpp index c752ddd9..e51b43f9 100644 --- a/src/pystack/_pystack/pyframe.cpp +++ b/src/pystack/_pystack/pyframe.cpp @@ -71,7 +71,14 @@ FrameObject::getCode( } else { last_instruction = frame.getField(&py_frame_v::o_lasti); } - return std::make_unique(manager, py_code_addr, last_instruction); + try { + return std::make_unique(manager, py_code_addr, last_instruction); + } catch (const RemoteMemCopyError& ex) { + // This may not have been a code object at all, or it may have been + // trashed by memory corruption. Either way, indicate that we failed + // to understand what code this frame is running. + return std::make_unique("???", "???", LocationInfo{0, 0, 0, 0}); + } } bool