Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds build-time selectable interception (interpose or linker wrap) of the C++ ABI function Changes
Sequence Diagram(s)mermaid Thrower->>Interposer: throw(exc, tinfo, dest) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/libutil-tests/cxa-throw.cc (1)
6-35: Good coverage ofstd::logic_errorfamily, but missing negative test.The death tests comprehensively cover
std::logic_errorand its standard subclasses. However, there's no test verifying that non-logic-error exceptions (e.g.,std::runtime_error) are not aborted and can be caught normally. This would validate that the mechanism is selective.Suggested additional test
TEST(CxaThrow, doesNotCatchRuntimeError) { // Verify that non-logic_error exceptions are not intercepted bool caught = false; try { throw std::runtime_error("test"); } catch (const std::runtime_error &) { caught = true; } ASSERT_TRUE(caught); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/libutil-tests/cxa-throw.cc` around lines 6 - 35, Add a negative test named TEST(CxaThrow, doesNotCatchRuntimeError) that verifies non-logic_error exceptions are not intercepted: in the test throw std::runtime_error("test") inside a try/catch, set a local bool (e.g., caught) to true in the catch(const std::runtime_error&) block, and ASSERT_TRUE(caught) at the end; this complements the existing TEST(CxaThrow,... ) cases (catchesLogicErrorFromStdlib, catchesLogicError, catchesOutOfRange, catchesInvalidArgument, catchesDomainError, catchesLengthError) to ensure runtime_error is handled normally rather than causing abortion.nix-meson-build-support/common/cxa-throw/wrap-cxa-throw.cc (1)
24-32: Consider adding[[noreturn]]attribute.Same as the interpose version -
__wrap___cxa_thrownever returns normally.Proposed fix
-extern "C" void __wrap___cxa_throw(void * exc, std::type_info * tinfo, void (*dest)(void *)) +extern "C" [[noreturn]] void __wrap___cxa_throw(void * exc, std::type_info * tinfo, void (*dest)(void *))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/wrap-cxa-throw.cc` around lines 24 - 32, The wrapper function __wrap___cxa_throw never returns, so add the [[noreturn]] attribute to its declaration/definition (extern "C" [[noreturn]] void __wrap___cxa_throw(...)) to match the interpose version; keep the existing logic (is_logic_error check, CXA_THROW_ON_LOGIC_ERROR(), call to __real___cxa_throw(exc, tinfo, dest) and the __builtin_unreachable()) but mark the function [[noreturn]] so callers and the compiler know it does not return.nix-meson-build-support/common/cxa-throw/meson.build (1)
18-24: Hardcoded-ldlin test may cause false negatives on musl/BSD systems.Line 20 uses
args : [ '-ldl' ]unconditionally, but on systems like musl libc or some BSDs,dlsymis in libc and-ldldoesn't exist. This could cause the interpose test to fail compilation even when interposition would work, incorrectly falling through to the wrap approach.Consider using
cxx.find_library('dl', required : false)for the test as well:Proposed fix
+dl_dep = cxx.find_library('dl', required : false) +dl_args = dl_dep.found() ? [ '-ldl' ] : [] + can_interpose_cxa_throw_result = cxx.run( can_interpose_cxa_throw_test_code, - args : [ '-ldl' ], + args : dl_args, include_directories : include_directories('.'), name : 'can interpose __cxa_throw (catches libstdc++ throws)', )Or use the dependency directly if
cxx.run()supports it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/meson.build` around lines 18 - 24, The test for interposing __cxa_throw uses a hardcoded args : [ '-ldl' ] which can break on musl/BSD where libdl isn't separate; update the cxx.run invocation that produces can_interpose_cxa_throw_result to use a discovered optional dl library instead: call cxx.find_library('dl', required : false) (or the project's equivalent) and pass that library/dependency into cxx.run (or its deps/args) only when found, so can_interpose_cxa_throw_result.compiled() reflects the true ability to link dlsym without forcing -ldl; adjust code that sets can_interpose_cxa_throw to use the new result unchanged.nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc (1)
25-37: Consider adding[[noreturn]]attribute.The
__cxa_throwfunction never returns normally (it either aborts or throws). Adding the[[noreturn]]attribute would allow the compiler to better optimize callers and provide better diagnostics.Proposed fix
-extern "C" void __cxa_throw(void * exc, std::type_info * tinfo, void (*dest)(void *)) +extern "C" [[noreturn]] void __cxa_throw(void * exc, std::type_info * tinfo, void (*dest)(void *))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc` around lines 25 - 37, Add the C++ [[noreturn]] attribute to the interposed function and matching function-pointer typedef so the compiler knows __cxa_throw never returns; specifically modify the extern "C" void __cxa_throw(...) definition to be extern "C" [[noreturn]] void __cxa_throw(void *exc, std::type_info *tinfo, void (*dest)(void *)) and update the cxa_throw_type alias (the type used for orig) to a pointer-to-[[noreturn]] function signature so the orig lookup and subsequent call have the noreturn annotation as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc`:
- Around line 13-23: The wrap implementation’s explicit typeid comparisons must
be replaced to match interpose’s recursive detection: modify the logic in
wrap-cxa-throw.cc that checks exception types (the block doing explicit typeid
comparisons against std::logic_error subclasses) to use the same recursive
traversal as is_logic_error in interpose-cxa-throw.cc — perform a dynamic_cast
to __cxxabiv1::__si_class_type_info, follow __base_type recursively and compare
*tinfo == typeid(std::logic_error) so custom subclasses are detected;
alternatively, if you prefer the explicit-list behavior, update
interpose-cxa-throw.cc to mirror the explicit comparisons instead (choose one
approach and make both files consistent).
---
Nitpick comments:
In `@nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc`:
- Around line 25-37: Add the C++ [[noreturn]] attribute to the interposed
function and matching function-pointer typedef so the compiler knows __cxa_throw
never returns; specifically modify the extern "C" void __cxa_throw(...)
definition to be extern "C" [[noreturn]] void __cxa_throw(void *exc,
std::type_info *tinfo, void (*dest)(void *)) and update the cxa_throw_type alias
(the type used for orig) to a pointer-to-[[noreturn]] function signature so the
orig lookup and subsequent call have the noreturn annotation as well.
In `@nix-meson-build-support/common/cxa-throw/meson.build`:
- Around line 18-24: The test for interposing __cxa_throw uses a hardcoded args
: [ '-ldl' ] which can break on musl/BSD where libdl isn't separate; update the
cxx.run invocation that produces can_interpose_cxa_throw_result to use a
discovered optional dl library instead: call cxx.find_library('dl', required :
false) (or the project's equivalent) and pass that library/dependency into
cxx.run (or its deps/args) only when found, so
can_interpose_cxa_throw_result.compiled() reflects the true ability to link
dlsym without forcing -ldl; adjust code that sets can_interpose_cxa_throw to use
the new result unchanged.
In `@nix-meson-build-support/common/cxa-throw/wrap-cxa-throw.cc`:
- Around line 24-32: The wrapper function __wrap___cxa_throw never returns, so
add the [[noreturn]] attribute to its declaration/definition (extern "C"
[[noreturn]] void __wrap___cxa_throw(...)) to match the interpose version; keep
the existing logic (is_logic_error check, CXA_THROW_ON_LOGIC_ERROR(), call to
__real___cxa_throw(exc, tinfo, dest) and the __builtin_unreachable()) but mark
the function [[noreturn]] so callers and the compiler know it does not return.
In `@src/libutil-tests/cxa-throw.cc`:
- Around line 6-35: Add a negative test named TEST(CxaThrow,
doesNotCatchRuntimeError) that verifies non-logic_error exceptions are not
intercepted: in the test throw std::runtime_error("test") inside a try/catch,
set a local bool (e.g., caught) to true in the catch(const std::runtime_error&)
block, and ASSERT_TRUE(caught) at the end; this complements the existing
TEST(CxaThrow,... ) cases (catchesLogicErrorFromStdlib, catchesLogicError,
catchesOutOfRange, catchesInvalidArgument, catchesDomainError,
catchesLengthError) to ensure runtime_error is handled normally rather than
causing abortion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 15eb542e-b1d2-481d-b597-0dd5a3660fba
📒 Files selected for processing (7)
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.ccnix-meson-build-support/common/cxa-throw/meson.buildnix-meson-build-support/common/cxa-throw/wrap-cxa-throw.ccnix-meson-build-support/common/meson.buildsrc/libutil-tests/cxa-throw.ccsrc/libutil-tests/meson.buildsrc/libutil/meson.build
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc
Outdated
Show resolved
Hide resolved
e29e81d to
debf93d
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
nix-meson-build-support/common/cxa-throw/meson.build (1)
54-72: Consider portability of linker flags.The
--wrapand-ulinker flags are GNU ld / lld specific. This should be fine since the feature test will naturally fail on systems with incompatible linkers (e.g., macOS ld64), correctly disabling the feature. Just noting for awareness.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/meson.build` around lines 54 - 72, The linker flags '-Wl,--wrap=__cxa_throw' and '-Wl,-u,__wrap___cxa_throw' are GNU/LLD-specific; update the feature gating around wrap_cxa_throw_args/can_wrap_cxa_throw_result/cxa_throw_dep so we only attempt the cxx.run test and create the static_library/declare_dependency when the detected linker/platform supports those flags (e.g., skip on Darwin or check cc.get_id()/compiler.get_linker_name() or host_machine.system() before using wrap_cxa_throw_args). Ensure the platform/linker check is performed before defining wrap_cxa_throw_args or invoking cxx.run so the test won’t run with incompatible linkers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@nix-meson-build-support/common/cxa-throw/meson.build`:
- Around line 54-72: The linker flags '-Wl,--wrap=__cxa_throw' and
'-Wl,-u,__wrap___cxa_throw' are GNU/LLD-specific; update the feature gating
around wrap_cxa_throw_args/can_wrap_cxa_throw_result/cxa_throw_dep so we only
attempt the cxx.run test and create the static_library/declare_dependency when
the detected linker/platform supports those flags (e.g., skip on Darwin or check
cc.get_id()/compiler.get_linker_name() or host_machine.system() before using
wrap_cxa_throw_args). Ensure the platform/linker check is performed before
defining wrap_cxa_throw_args or invoking cxx.run so the test won’t run with
incompatible linkers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1b26fdd5-bd9c-496e-a275-7cf76cafc5f4
📒 Files selected for processing (8)
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.ccnix-meson-build-support/common/cxa-throw/is-logic-error.hhnix-meson-build-support/common/cxa-throw/meson.buildnix-meson-build-support/common/cxa-throw/wrap-cxa-throw.ccnix-meson-build-support/common/meson.buildsrc/libutil-tests/cxa-throw.ccsrc/libutil-tests/meson.buildsrc/libutil/meson.build
✅ Files skipped from review due to trivial changes (2)
- src/libutil-tests/meson.build
- src/libutil/meson.build
🚧 Files skipped from review as they are similar to previous changes (1)
- nix-meson-build-support/common/meson.build
debf93d to
ff6468d
Compare
This will fail until the next commit.
ff6468d to
7d4e727
Compare
|
At the very least, the test does indeed appear to properly abort now (giving us an actual backtrace): Where it wouldn't have before: |
There are many places where a thrown `std::logic_error` is handled like a normal error, making it very difficult to get a good stack trace so we can fix that logic error. So, we wrap the __cxa_throw function such that we can check if the thrown exception was a `std::logic_error`, and if so, abort the process so we can figure out exactly where it happened. Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
7d4e727 to
4cfdfb2
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
nix-meson-build-support/common/cxa-throw/meson.build (1)
12-13: Prefer a defined stdlib throw here unless the null-string backtrace is the point.
std::string(nullptr)is undefined by the C++ API and only throws here because libstdc++ adds a check. If the goal is just to exercise a stdlibstd::logic_errorpath, a defined throw site such asstd::string{}.at(1)is much less brittle.Example change
- const char * volatile p = nullptr; - std::string s(p); + std::string s; + s.at(1);- const char * volatile p = nullptr; - std::string s(p); + std::string s; + s.at(1);Also applies to: 48-49
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/meson.build` around lines 12 - 13, Replace the undefined call std::string s(p) (where p is declared as const char * volatile p = nullptr) with a defined standard-library throw site; for example, remove the null-pointer construction and use a bounds-checked string access like std::string{}.at(1) to reliably trigger a std::out_of_range (or explicitly throw a std::logic_error if that exact exception type is required). Apply the same change at the other occurrence referenced (the code around the symbols at the second occurrence corresponding to the previous std::string(nullptr) usage on lines 48-49).src/libutil-tests/cxa-throw.cc (1)
6-35: Tighten this suite around both the abort and pass-through paths.These tests only prove that some logic-error paths die. They do not cover the inverse requirement from the PR (
std::runtime_errorand other non-logic_error throws must still propagate normally), andASSERT_DEATH(..., "")also accepts deaths that are not specificallyabort(). An over-broad matcher or a different fatal path would still go green here.Example tightening
+#include <csignal>- ASSERT_DEATH({ throw std::logic_error("test"); }, ""); + ASSERT_EXIT({ throw std::logic_error("test"); }, + ::testing::KilledBySignal(SIGABRT), "");+TEST(CxaThrow, ignoresRuntimeError) +{ + EXPECT_THROW([] { throw std::runtime_error("test"); }(), std::runtime_error); +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/libutil-tests/cxa-throw.cc` around lines 6 - 35, Replace the broad ASSERT_DEATH(...) checks in the CxaThrow tests with precise checks that the process aborts via SIGABRT (use gtest's KilledBySignal(SIGABRT) / a regex that matches "Aborted") for the logic_error variants (tests named CxaThrow::catchesLogicError, catchesOutOfRange, catchesInvalidArgument, catchesDomainError, catchesLengthError and the stdlib string-nullptr case), and add a new test (e.g., CxaThrow::catchesRuntimeErrorPropagates) that uses ASSERT_THROW({ throw std::runtime_error("test"); }, std::runtime_error) to verify std::runtime_error and other non-logic_error exceptions propagate instead of causing abort; update only the assertions in those tests (no behavior changes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nix-meson-build-support/common/cxa-throw/is-logic-error.hh`:
- Around line 12-14: The current is_logic_error function only handles
single-inheritance RTTI (__si_class_type_info) and misses multiple-inheritance
cases; update is_logic_error to also detect __vmi_class_type_info, iterate over
all its base class entries (accessing each base type pointer from the vmi
structure) and recursively call is_logic_error on each base_type, returning true
if any base is a logic_error subclass; keep the existing si branch, add nullptr
checks for the vmi base array/count, and ensure you return false only after all
bases have been checked.
---
Nitpick comments:
In `@nix-meson-build-support/common/cxa-throw/meson.build`:
- Around line 12-13: Replace the undefined call std::string s(p) (where p is
declared as const char * volatile p = nullptr) with a defined standard-library
throw site; for example, remove the null-pointer construction and use a
bounds-checked string access like std::string{}.at(1) to reliably trigger a
std::out_of_range (or explicitly throw a std::logic_error if that exact
exception type is required). Apply the same change at the other occurrence
referenced (the code around the symbols at the second occurrence corresponding
to the previous std::string(nullptr) usage on lines 48-49).
In `@src/libutil-tests/cxa-throw.cc`:
- Around line 6-35: Replace the broad ASSERT_DEATH(...) checks in the CxaThrow
tests with precise checks that the process aborts via SIGABRT (use gtest's
KilledBySignal(SIGABRT) / a regex that matches "Aborted") for the logic_error
variants (tests named CxaThrow::catchesLogicError, catchesOutOfRange,
catchesInvalidArgument, catchesDomainError, catchesLengthError and the stdlib
string-nullptr case), and add a new test (e.g.,
CxaThrow::catchesRuntimeErrorPropagates) that uses ASSERT_THROW({ throw
std::runtime_error("test"); }, std::runtime_error) to verify std::runtime_error
and other non-logic_error exceptions propagate instead of causing abort; update
only the assertions in those tests (no behavior changes).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a6142eee-baa0-4937-abdd-0dd079300b61
📒 Files selected for processing (8)
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.ccnix-meson-build-support/common/cxa-throw/is-logic-error.hhnix-meson-build-support/common/cxa-throw/meson.buildnix-meson-build-support/common/cxa-throw/wrap-cxa-throw.ccnix-meson-build-support/common/meson.buildsrc/libutil-tests/cxa-throw.ccsrc/libutil-tests/meson.buildsrc/libutil/meson.build
✅ Files skipped from review due to trivial changes (2)
- src/libutil-tests/meson.build
- nix-meson-build-support/common/meson.build
🚧 Files skipped from review as they are similar to previous changes (1)
- src/libutil/meson.build
| auto * si = dynamic_cast<const __cxxabiv1::__si_class_type_info *>(tinfo); | ||
| if (si) | ||
| return is_logic_error(si->__base_type); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "is-logic-error.hh" | head -20Repository: DeterminateSystems/nix-src
Length of output: 130
🏁 Script executed:
cat -n ./nix-meson-build-support/common/cxa-throw/is-logic-error.hhRepository: DeterminateSystems/nix-src
Length of output: 549
🏁 Script executed:
# Look for context around this function - check if there are tests or usages
rg "is_logic_error" --type-listRepository: DeterminateSystems/nix-src
Length of output: 459
🏁 Script executed:
# Search for usages of is_logic_error
rg "is_logic_error" -A 3 -B 3Repository: DeterminateSystems/nix-src
Length of output: 2527
🏁 Script executed:
# Look at the wrap-cxa-throw.cc to understand the context better
cat -n ./nix-meson-build-support/common/cxa-throw/wrap-cxa-throw.ccRepository: DeterminateSystems/nix-src
Length of output: 683
🏁 Script executed:
# Search for any other RTTI type handling in the codebase
rg "__si_class_type_info|__vmi_class_type_info" -B 2 -A 2Repository: DeterminateSystems/nix-src
Length of output: 525
🏁 Script executed:
# Search for cxxabi.h includes to see if there's any documentation
find . -type f -name "*.hh" -o -name "*.h" | xargs grep -l "cxxabi" | head -10Repository: DeterminateSystems/nix-src
Length of output: 130
🏁 Script executed:
# Check if there are any tests or documentation about exception handling
find . -path "*/test*" -type f | grep -i "cxa\|throw\|logic" | head -20Repository: DeterminateSystems/nix-src
Length of output: 259
🏁 Script executed:
# Let's search for information about cxxabi RTTI types online
# First, let me check the cxxabi.h header on this system
cat /usr/include/cxxabi.h | grep -A 20 "__si_class_type_info\|__vmi_class_type_info" | head -50Repository: DeterminateSystems/nix-src
Length of output: 123
🌐 Web query:
Itanium C++ ABI RTTI __si_class_type_info __vmi_class_type_info inheritance
💡 Result:
Under the Itanium C++ ABI, the RTTI object referenced by a polymorphic class’s vtable “typeinfo pointer” (vtable slot -1) is an instance of one of the __class_type_info-family types, chosen based on the class’s inheritance shape. [1]
Which RTTI class is used
__cxxabiv1::__class_type_info: used for class types with no base classes (and is also the base of the other class RTTI types). [1]__cxxabiv1::__si_class_type_info(“single inheritance”): used only when the class has exactly one direct base and that base is public, non-virtual, and at offset 0 (i.e., the derived is dynamic iff the base is).
It adds one field:const __class_type_info* __base_type;. [1]__cxxabiv1::__vmi_class_type_info(“virtual/multiple inheritance”): used for classes whose bases do not meet the__si_class_type_infoconstraints (i.e., any multiple inheritance, any virtual base, or a non-zero/ non-public single base). [1][2]
How inheritance is encoded in __vmi_class_type_info
__vmi_class_type_info contains: [1]
unsigned int __flags;with bits:0x1= non-diamond repeated inheritance present0x2= diamond-shaped inheritance present
unsigned int __base_count;= number of direct proper bases__base_class_type_info __base_info[__base_count];where each entry is:const __class_type_info* __base_type;(points to the base’s RTTI)long __offset_flags;packing:- low 8 bits = flags:
0x1= base is virtual0x2= base is public- and
__offset_shift = 8
- high bits (everything above the low byte) = a signed offset:
- for a non-virtual base: byte offset of that base subobject within the complete object
- for a virtual base: a (negative) offset into the vtable where the virtual-base offset is found [1]
- low 8 bits = flags:
So, in practice: if you can use __si_class_type_info, you only get the single __base_type. Otherwise you use __vmi_class_type_info and walk __base_info[], interpreting __offset_flags to determine (1) which direct bases exist, and (2) whether each base is public/virtual and where it sits (directly in the object, or via a vtable virtual-base-offset slot). [1]
Sources: Itanium C++ ABI RTTI layout/specification. [1], plus ABI issue discussion clarifying si vs vmi usage. [2]
Handle __vmi_class_type_info too.
This only walks single-inheritance RTTI. A class with multiple bases like struct X : std::logic_error, Tag uses __vmi_class_type_info, so is_logic_error() returns false even though one base is a logic_error subclass, bypassing the abort.
Proposed fix
auto * si = dynamic_cast<const __cxxabiv1::__si_class_type_info *>(tinfo);
if (si)
return is_logic_error(si->__base_type);
+
+ auto * vmi = dynamic_cast<const __cxxabiv1::__vmi_class_type_info *>(tinfo);
+ if (vmi) {
+ for (unsigned i = 0; i < vmi->__base_count; ++i) {
+ if (is_logic_error(vmi->__base_info[i].__base_type))
+ return true;
+ }
+ }
return false;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| auto * si = dynamic_cast<const __cxxabiv1::__si_class_type_info *>(tinfo); | |
| if (si) | |
| return is_logic_error(si->__base_type); | |
| auto * si = dynamic_cast<const __cxxabiv1::__si_class_type_info *>(tinfo); | |
| if (si) | |
| return is_logic_error(si->__base_type); | |
| auto * vmi = dynamic_cast<const __cxxabiv1::__vmi_class_type_info *>(tinfo); | |
| if (vmi) { | |
| for (unsigned i = 0; i < vmi->__base_count; ++i) { | |
| if (is_logic_error(vmi->__base_info[i].__base_type)) | |
| return true; | |
| } | |
| } | |
| return false; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nix-meson-build-support/common/cxa-throw/is-logic-error.hh` around lines 12 -
14, The current is_logic_error function only handles single-inheritance RTTI
(__si_class_type_info) and misses multiple-inheritance cases; update
is_logic_error to also detect __vmi_class_type_info, iterate over all its base
class entries (accessing each base type pointer from the vmi structure) and
recursively call is_logic_error on each base_type, returning true if any base is
a logic_error subclass; keep the existing si branch, add nullptr checks for the
vmi base array/count, and ensure you return false only after all bases have been
checked.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
nix-meson-build-support/common/cxa-throw/is-logic-error.hh (1)
18-22:⚠️ Potential issue | 🟠 Major
is_logic_errorstill misses multiple-inheritance RTTI paths.Line 18 only walks
__si_class_type_info; astd::logic_error-derived type using multiple inheritance can bypass detection. This was already raised earlier and appears unresolved.Proposed fix
static bool is_logic_error(const std::type_info * tinfo) { if (*tinfo == typeid(std::logic_error)) return true; auto * si = dynamic_cast<const __cxxabiv1::__si_class_type_info *>(tinfo); if (si) return is_logic_error(si->__base_type); + + auto * vmi = dynamic_cast<const __cxxabiv1::__vmi_class_type_info *>(tinfo); + if (vmi) { + for (unsigned i = 0; i < vmi->__base_count; ++i) { + if (is_logic_error(vmi->__base_info[i].__base_type)) + return true; + } + } return false; }#!/bin/bash set -euo pipefail rg -n '__si_class_type_info|__vmi_class_type_info|is_logic_error' nix-meson-build-support/common/cxa-throw/is-logic-error.hh -C3
🧹 Nitpick comments (1)
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc (1)
11-19: Add a non-logic_errorpassthrough regression test.This forwarding path is core to the “don’t abort other exceptions” objective, but current tests only cover death on
logic_errorvariants (src/libutil-tests/cxa-throw.cc, Lines 1-35). Please add one test that throws/catches something likestd::runtime_errorand verifies no abort.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc` around lines 11 - 19, Add a regression test that verifies non-logic_error exceptions are forwarded and do not trigger abort: create a test (e.g., cxa_throw_passthrough_non_logic_error) that throws a std::runtime_error (or another non-logic_error type) so the interpose path in is_logic_error(...) is exercised, ensure the exception is caught by the test (or the child process exits normally) and assert no abort occurred; this validates the orig (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw") forwarding path and that abort_on_exception(exc, tinfo) is not invoked for non-logic_error types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nix-meson-build-support/common/cxa-throw/is-logic-error.hh`:
- Around line 3-11: The header currently relies on transitive includes for
abort() and snprintf() — update the includes to explicitly add <cstdlib> (for
abort) and <cstdio> (for snprintf) so CXA_THROW_ON_LOGIC_ERROR() and any uses of
snprintf() are portable; modify the top-of-file include list where <unistd.h>,
<cxxabi.h>, <stdexcept>, <typeinfo>, and <cstring> are declared to also include
<cstdlib> and <cstdio> so the symbols abort() and snprintf() are provided
explicitly.
In `@nix-meson-build-support/common/cxa-throw/wrap-cxa-throw.cc`:
- Around line 10-13: The call to abort_on_exception has incorrect arity; update
the call in the cxa-throw wrapper so abort_on_exception is invoked with both
parameters (the exception pointer and its type_info) instead of just tinfo.
Specifically, in the block that checks is_logic_error(tinfo) before delegating
to __real___cxa_throw, change the call to pass exc and tinfo to
abort_on_exception, ensuring the symbol abort_on_exception(void *exc, const
std::type_info *tinfo) is used correctly and __real___cxa_throw(exc, tinfo,
dest) remains unchanged.
---
Nitpick comments:
In `@nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.cc`:
- Around line 11-19: Add a regression test that verifies non-logic_error
exceptions are forwarded and do not trigger abort: create a test (e.g.,
cxa_throw_passthrough_non_logic_error) that throws a std::runtime_error (or
another non-logic_error type) so the interpose path in is_logic_error(...) is
exercised, ensure the exception is caught by the test (or the child process
exits normally) and assert no abort occurred; this validates the orig
(cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw") forwarding path and that
abort_on_exception(exc, tinfo) is not invoked for non-logic_error types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d09b48db-0163-4d5e-a19a-8094ce1a9d17
📒 Files selected for processing (3)
nix-meson-build-support/common/cxa-throw/interpose-cxa-throw.ccnix-meson-build-support/common/cxa-throw/is-logic-error.hhnix-meson-build-support/common/cxa-throw/wrap-cxa-throw.cc
| #include <unistd.h> | ||
| #include <cxxabi.h> | ||
| #include <stdexcept> | ||
| #include <typeinfo> | ||
| #include <cstring> | ||
|
|
||
| #ifndef CXA_THROW_ON_LOGIC_ERROR | ||
| # define CXA_THROW_ON_LOGIC_ERROR() abort() | ||
| #endif |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n '^#include|abort\s*\(|snprintf\s*\(' nix-meson-build-support/common/cxa-throw/is-logic-error.hh -C1Repository: DeterminateSystems/nix-src
Length of output: 336
🏁 Script executed:
cat -n nix-meson-build-support/common/cxa-throw/is-logic-error.hhRepository: DeterminateSystems/nix-src
Length of output: 1268
Add explicit includes <cstdlib> and <cstdio> for directly used symbols.
Line 10 uses abort() and line 31 uses snprintf(), neither of which are explicitly included. While <unistd.h> may transitively provide these symbols on many POSIX systems, this is not standard and reduces portability. Include <cstdlib> for abort() and <cstdio> for snprintf() explicitly.
Proposed fix
`#include` <unistd.h>
+#include <cstdlib>
+#include <cstdio>
`#include` <cxxabi.h>
`#include` <stdexcept>
`#include` <typeinfo>
`#include` <cstring>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #include <unistd.h> | |
| #include <cxxabi.h> | |
| #include <stdexcept> | |
| #include <typeinfo> | |
| #include <cstring> | |
| #ifndef CXA_THROW_ON_LOGIC_ERROR | |
| # define CXA_THROW_ON_LOGIC_ERROR() abort() | |
| #endif | |
| `#include` <unistd.h> | |
| `#include` <cstdlib> | |
| `#include` <cstdio> | |
| `#include` <cxxabi.h> | |
| `#include` <stdexcept> | |
| `#include` <typeinfo> | |
| `#include` <cstring> | |
| `#ifndef` CXA_THROW_ON_LOGIC_ERROR | |
| # define CXA_THROW_ON_LOGIC_ERROR() abort() | |
| `#endif` |
🧰 Tools
🪛 Clang (14.0.6)
[error] 3-3: 'unistd.h' file not found
(clang-diagnostic-error)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nix-meson-build-support/common/cxa-throw/is-logic-error.hh` around lines 3 -
11, The header currently relies on transitive includes for abort() and
snprintf() — update the includes to explicitly add <cstdlib> (for abort) and
<cstdio> (for snprintf) so CXA_THROW_ON_LOGIC_ERROR() and any uses of snprintf()
are portable; modify the top-of-file include list where <unistd.h>, <cxxabi.h>,
<stdexcept>, <typeinfo>, and <cstring> are declared to also include <cstdlib>
and <cstdio> so the symbols abort() and snprintf() are provided explicitly.
Example: Aborting on unexpected exception of type 'St16invalid_argument', error: bitset::_M_copy_from_ptr
042f5a5 to
f69b305
Compare
There are many places where a thrown
std::logic_erroris handled like a normal error, making it very difficult to get a good stack trace so we can fix that logic error.So, we wrap the __cxa_throw function such that we can check if the thrown exception was a
std::logic_error, and if so, abort the process so we can figure out exactly where it happened.Closes #331
Motivation
Context
Summary by CodeRabbit
New Features
Tests
Chores