https://github.com/t-rasmud updated https://github.com/llvm/llvm-project/pull/200294
>From 0770839c45c0f859bbcaa41ceb74dd3588b0a685 Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Thu, 28 May 2026 15:47:58 -0700 Subject: [PATCH 1/8] [Webkit Checkers][SaferCpp] Detect base-to-derived downcasts laundered through void* in MemoryUnsafeCastChecker Adds a matcher for static_cast<Derived*>(static_cast<void*>(base)), which previously evaded detection because the outer cast's immediate source expression is void*, not Base*. rdar://173770143 --- .../WebKit/MemoryUnsafeCastChecker.cpp | 16 +++++++++++---- .../Checkers/WebKit/memory-unsafe-cast.mm | 20 ++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index eeaccf9b70524..5461ce66f085f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -122,10 +122,18 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, .bind(DerivedNode)))))), unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())), hasType(templateTypeParmDecl())))); - - auto ExplicitCast = explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, - MatchExprPtrObjC)) - .bind(WarnRecordDecl); + auto MatchExprPtrVoidCast = cxxStaticCastExpr( + hasSourceExpression(cxxStaticCastExpr( + hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), + hasTypePointingTo( + cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))).bind(DerivedNode))); + + auto ExplicitCast = + explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, MatchExprPtrObjC, + MatchExprPtrVoidCast)) + .bind(WarnRecordDecl); auto Cast = stmt(ExplicitCast); auto Matches = diff --git a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm index f9046d7981784..c4208ec7aeac5 100644 --- a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm +++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm @@ -1,12 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s -@protocol NSObject -+alloc; --init; -@end - -@interface NSObject <NSObject> {} -@end +#include "mock-types.h" +#include "objc-mock-types.h" @interface BaseClass : NSObject @end @@ -62,3 +57,14 @@ void testUnrelated(Class1 *c1) { // expected-warning@-1{{Unsafe cast from type 'Class1' to an unrelated type 'Class2'}} Class1 *c1_same = reinterpret_cast<Class1*>(c1); // no warning } + +struct Base : RefCountable { virtual ~Base() {} }; +struct Derived : Base { int extra; }; + +void fn_cast_01(Base* base) { + auto* d1 = static_cast<Derived*>(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + + auto* d2 = static_cast<Derived*>(static_cast<void*>(base)); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} +} >From 0df94218070d192159529a606e33e8a5dabc8ef9 Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Thu, 28 May 2026 16:04:54 -0700 Subject: [PATCH 2/8] Fix code formatting --- .../Checkers/WebKit/MemoryUnsafeCastChecker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index 5461ce66f085f..4793b28fd5e56 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -127,12 +127,12 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, hasType(pointerType(pointee(voidType()))), hasSourceExpression(ignoringImpCasts( hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), - hasTypePointingTo( - cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))).bind(DerivedNode))); + hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))) + .bind(DerivedNode))); auto ExplicitCast = - explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, MatchExprPtrObjC, - MatchExprPtrVoidCast)) + explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, + MatchExprPtrObjC, MatchExprPtrVoidCast)) .bind(WarnRecordDecl); auto Cast = stmt(ExplicitCast); >From 65b87980a0787ebef3168cec628ab14da9ab0a9f Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Thu, 28 May 2026 16:44:42 -0700 Subject: [PATCH 3/8] Add matchers for funciton args and returns --- .../WebKit/MemoryUnsafeCastChecker.cpp | 29 ++++++++++++++++--- .../Checkers/WebKit/memory-unsafe-cast.mm | 15 +++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index 4793b28fd5e56..265059408116f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -123,10 +123,15 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())), hasType(templateTypeParmDecl())))); auto MatchExprPtrVoidCast = cxxStaticCastExpr( - hasSourceExpression(cxxStaticCastExpr( - hasType(pointerType(pointee(voidType()))), - hasSourceExpression(ignoringImpCasts( - hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), + anyOf( + hasSourceExpression(cxxStaticCastExpr( + hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), + hasSourceExpression(callExpr( + hasType(pointerType(pointee(voidType()))), + hasAnyArgument(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))))), hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))) .bind(DerivedNode))); @@ -141,6 +146,22 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this, BT); + // Match calls returning derived type where an argument is static_cast<void*>(Base*) + auto MatchCallPtrVoidArgCast = callExpr( + hasAnyArgument( + cxxStaticCastExpr( + hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))) + .bind(WarnRecordDecl)), + hasTypePointingTo( + cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))).bind(DerivedNode))); + auto CallArgCast = stmt(MatchCallPtrVoidArgCast); + auto MatchesCallArgCast = + match(stmt(forEachDescendant(CallArgCast)), *D->getBody(), AM.getASTContext()); + for (BoundNodes Match : MatchesCallArgCast) + emitDiagnostics(Match, BR, ADC, this, BT); + // Match casts between unrelated types and warn auto MatchExprPtrUnrelatedTypes = allOf( hasSourceExpression( diff --git a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm index c4208ec7aeac5..5a887c49d3cd8 100644 --- a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm +++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm @@ -61,10 +61,23 @@ void testUnrelated(Class1 *c1) { struct Base : RefCountable { virtual ~Base() {} }; struct Derived : Base { int extra; }; +void* returnCast(Base* base) { + return static_cast<void *>(base); +} + +Derived* fnArgCast(void* base) { + return static_cast<Derived*>(base); +} + void fn_cast_01(Base* base) { auto* d1 = static_cast<Derived*>(base); // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} - auto* d2 = static_cast<Derived*>(static_cast<void*>(base)); // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d3 = static_cast<Derived*>(returnCast(base)); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d4 = fnArgCast(static_cast<void*>(base)); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + fnArgCast(static_cast<void*>(base)); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} } >From 9b517452d258ec38f65baa18cf113d4cda015a0a Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Tue, 2 Jun 2026 11:24:26 -0700 Subject: [PATCH 4/8] AST matchers for missed void* laundering cases --- .../WebKit/MemoryUnsafeCastChecker.cpp | 20 ++++++++++--------- .../Checkers/WebKit/memory-unsafe-cast.mm | 16 +++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index 265059408116f..ae3fc2df05545 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -122,9 +122,9 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, .bind(DerivedNode)))))), unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())), hasType(templateTypeParmDecl())))); - auto MatchExprPtrVoidCast = cxxStaticCastExpr( + auto MatchExprPtrVoidCast = allOf( anyOf( - hasSourceExpression(cxxStaticCastExpr( + hasSourceExpression(explicitCastExpr( hasType(pointerType(pointee(voidType()))), hasSourceExpression(ignoringImpCasts( hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), @@ -146,14 +146,16 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this, BT); - // Match calls returning derived type where an argument is static_cast<void*>(Base*) + // Match calls returning derived type where an argument is a void pointer + auto VoidPtrCast = castExpr( + hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))) + .bind(WarnRecordDecl); auto MatchCallPtrVoidArgCast = callExpr( - hasAnyArgument( - cxxStaticCastExpr( - hasType(pointerType(pointee(voidType()))), - hasSourceExpression(ignoringImpCasts( - hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))) - .bind(WarnRecordDecl)), + hasAnyArgument(anyOf( + VoidPtrCast, + explicitCastExpr(hasSourceExpression(VoidPtrCast)))), hasTypePointingTo( cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))).bind(DerivedNode))); auto CallArgCast = stmt(MatchCallPtrVoidArgCast); diff --git a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm index 5a887c49d3cd8..55b02b2657230 100644 --- a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm +++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm @@ -80,4 +80,20 @@ void fn_cast_01(Base* base) { // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} fnArgCast(static_cast<void*>(base)); // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d5 = (Derived*)(void*)base; + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d6 = static_cast<Derived*>((void*)base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d7 = (Derived*)reinterpret_cast<void*>(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d8 = (Derived*)returnCast(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + fnArgCast((void*)base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + fnArgCast(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d9 = reinterpret_cast<Derived*>(static_cast<void*>(base)); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + auto* d10 = reinterpret_cast<Derived*>((void*)base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} } >From 4e1f37c29077bcbc11880cc1d7bbddd888bb87c8 Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Tue, 2 Jun 2026 11:25:26 -0700 Subject: [PATCH 5/8] Fix code formatting --- .../StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index ae3fc2df05545..cee974c1139bc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -146,7 +146,8 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this, BT); - // Match calls returning derived type where an argument is a void pointer + // Match calls returning derived type where an argument is + // a void pointer auto VoidPtrCast = castExpr( hasType(pointerType(pointee(voidType()))), hasSourceExpression(ignoringImpCasts( >From a22ed4384f36d199e83a96e104f3dcfc0f77feb6 Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Tue, 2 Jun 2026 11:31:03 -0700 Subject: [PATCH 6/8] Fix formatting --- .../WebKit/MemoryUnsafeCastChecker.cpp | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index cee974c1139bc..eb55d643e2567 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -148,20 +148,19 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, // Match calls returning derived type where an argument is // a void pointer - auto VoidPtrCast = castExpr( - hasType(pointerType(pointee(voidType()))), - hasSourceExpression(ignoringImpCasts( - hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))) - .bind(WarnRecordDecl); + auto VoidPtrCast = + castExpr(hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))) + .bind(WarnRecordDecl); auto MatchCallPtrVoidArgCast = callExpr( - hasAnyArgument(anyOf( - VoidPtrCast, - explicitCastExpr(hasSourceExpression(VoidPtrCast)))), - hasTypePointingTo( - cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))).bind(DerivedNode))); + hasAnyArgument(anyOf(VoidPtrCast, + explicitCastExpr(hasSourceExpression(VoidPtrCast)))), + hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))) + .bind(DerivedNode))); auto CallArgCast = stmt(MatchCallPtrVoidArgCast); - auto MatchesCallArgCast = - match(stmt(forEachDescendant(CallArgCast)), *D->getBody(), AM.getASTContext()); + auto MatchesCallArgCast = match(stmt(forEachDescendant(CallArgCast)), + *D->getBody(), AM.getASTContext()); for (BoundNodes Match : MatchesCallArgCast) emitDiagnostics(Match, BR, ADC, this, BT); >From 3a384c409cb5484d19f3b9d7b9b544f2793ad487 Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Tue, 2 Jun 2026 12:57:03 -0700 Subject: [PATCH 7/8] Fix formatting --- .../WebKit/MemoryUnsafeCastChecker.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index eb55d643e2567..d7224effc6a8d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -123,20 +123,19 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())), hasType(templateTypeParmDecl())))); auto MatchExprPtrVoidCast = allOf( - anyOf( - hasSourceExpression(explicitCastExpr( - hasType(pointerType(pointee(voidType()))), - hasSourceExpression(ignoringImpCasts( - hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), - hasSourceExpression(callExpr( - hasType(pointerType(pointee(voidType()))), - hasAnyArgument(ignoringImpCasts( - hasTypePointingTo(cxxRecordDecl().bind(BaseNode))))))), + anyOf(hasSourceExpression(explicitCastExpr( + hasType(pointerType(pointee(voidType()))), + hasSourceExpression(ignoringImpCasts( + hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))), + hasSourceExpression( + callExpr(hasType(pointerType(pointee(voidType()))), + hasAnyArgument(ignoringImpCasts(hasTypePointingTo( + cxxRecordDecl().bind(BaseNode))))))), hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode))) .bind(DerivedNode))); auto ExplicitCast = - explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, + explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef, MatchExprPtrObjC, MatchExprPtrVoidCast)) .bind(WarnRecordDecl); auto Cast = stmt(ExplicitCast); >From d7ffe6efbede52bc1e06b0c2839cf2f840c7efaf Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru <[email protected]> Date: Thu, 4 Jun 2026 13:45:48 -0700 Subject: [PATCH 8/8] Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Balázs Benics <[email protected]> --- .../StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp index d7224effc6a8d..86c682367c850 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp @@ -145,8 +145,7 @@ void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D, for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this, BT); - // Match calls returning derived type where an argument is - // a void pointer + // Match calls returning derived type where an argument is a void pointer. auto VoidPtrCast = castExpr(hasType(pointerType(pointee(voidType()))), hasSourceExpression(ignoringImpCasts( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
