From 0b701f2f2c0ec137f91580f3851d5317f896ebb7 Mon Sep 17 00:00:00 2001 From: harisibrar1 <207127467+harisibrar1@users.noreply.github.com> Date: Sat, 24 Oct 2026 01:44:08 -0400 Subject: [PATCH] fix(libc): reject a catch or upcast to a base that a private path repeats Bases() counted only base subobjects reached through public paths, so a class with the same base once publicly and once privately looked like it had a unique base. Catch matching, the RTTI upcast callbacks and __dynamic_cast's cross-cast then accepted it, where C++ requires an unambiguous public base and libsupc++'s __do_upcast/__do_dyncast reject it as ambiguous. Count every path to the base for ambiguity, and accept the single subobject only when at least one path to it is public. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/exception/Runtime.cpp | 7 +++++-- core/libs/tests/ExceptionRuntime.cpp | 4 ++++ core/libs/tests/WindowsExceptions.cpp | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libc/src/exception/Runtime.cpp b/core/libs/prx/libc/src/exception/Runtime.cpp index e790d1a2db..6d1cd738bc 100644 --- a/core/libs/prx/libc/src/exception/Runtime.cpp +++ b/core/libs/prx/libc/src/exception/Runtime.cpp @@ -200,12 +200,15 @@ void VisitBases(const std::type_info* type, BaseLocation location, bool publicPa void Bases(const std::type_info* type, void* object, Search& search) { std::uintptr_t identity = 0; + bool reachedPublicly = false; auto visitor = [&](const std::type_info* candidate, BaseLocation location, bool publicPath) { - if (!publicPath || !Equal(candidate, search.wanted)) return; - if (!search.count) { search.found = location.object; identity = location.identity; search.count = 1; } + if (!Equal(candidate, search.wanted)) return; + if (!search.count) { search.found = location.object; identity = location.identity; search.count = 1; reachedPublicly = publicPath; } else if (search.found != location.object || identity != location.identity) search.count = 2; + else if (publicPath) reachedPublicly = true; }; VisitBases(type, {object, 0}, true, visitor); + if (search.count == 1 && !reachedPublicly) search.count = 0; } bool PointerMatch(const __cxxabiv1::__pbase_type_info* caught, const __cxxabiv1::__pbase_type_info* thrown, diff --git a/core/libs/tests/ExceptionRuntime.cpp b/core/libs/tests/ExceptionRuntime.cpp index 8f8b1c41fe..900037f876 100644 --- a/core/libs/tests/ExceptionRuntime.cpp +++ b/core/libs/tests/ExceptionRuntime.cpp @@ -63,6 +63,7 @@ struct Virtual : virtual Base {}; struct Left : Base {}; struct Right : Base {}; struct Repeated : Left, Right {}; +struct MixedRepeated : Left, private Right {}; struct LeftVirtual : virtual Base {}; struct RightVirtual : virtual Base {}; struct Diamond : LeftVirtual, RightVirtual {}; @@ -82,6 +83,9 @@ void CheckRtti() { try { throw static_cast(nullptr); } catch (Base*) { assert(false); } catch (Repeated* p) { assert(p == nullptr); } + try { throw MixedRepeated(); } + catch (Base&) { assert(false); } + catch (MixedRepeated&) {} try { throw static_cast(nullptr); } catch (Base* p) { assert(p == nullptr); } try { throw nullptr; } diff --git a/core/libs/tests/WindowsExceptions.cpp b/core/libs/tests/WindowsExceptions.cpp index c83acd49c2..5adb55cac4 100644 --- a/core/libs/tests/WindowsExceptions.cpp +++ b/core/libs/tests/WindowsExceptions.cpp @@ -56,6 +56,17 @@ static void TestTypeInfoVtables() { if (!upcastType(&multiple, &base, &object) || object != storage.data() + 16) throw std::runtime_error("multiple RTTI upcast failed"); object = storage.data(); if (catchType(&other, &single, &object, 0)) throw std::runtime_error("unrelated RTTI catch succeeded"); + TypeRecord shared {classTable + 2 * sizeof(void*), "6Shared"}; + SingleRecord publicPath {{singleTable + 2 * sizeof(void*), "10PublicPath"}, &shared}; + SingleRecord privatePath {{singleTable + 2 * sizeof(void*), "11PrivatePath"}, &shared}; + MultipleRecord mixed {{multipleTable + 2 * sizeof(void*), "5Mixed"}, 1, 2, {{&publicPath.type, 2}, {&privatePath.type, 16 << 8}}}; + MultipleRecord hidden {{multipleTable + 2 * sizeof(void*), "6Hidden"}, 0, 1, {{&privatePath.type, 0}, {}}}; + object = storage.data(); + if (catchType(&shared, &mixed, &object, 0) || object != storage.data()) throw std::runtime_error("RTTI catch of a base repeated through a private path succeeded"); + if (upcastType(&mixed, &shared, &object) || object != storage.data()) throw std::runtime_error("RTTI upcast to a base repeated through a private path succeeded"); + if (!catchType(&publicPath, &mixed, &object, 0) || object != storage.data()) throw std::runtime_error("RTTI catch of the public path failed"); + object = storage.data(); + if (catchType(&shared, &hidden, &object, 0)) throw std::runtime_error("RTTI catch of a privately inherited base succeeded"); } extern "C" [[noreturn]] void NotImplemented_nid_no_patch(const char*);