diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 034774a252b118..8994d5de9c0b54 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -20,6 +20,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/Builtins.h" @@ -392,10 +394,19 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, } } -/// TODO: Handle explicit casts. -/// Handle C++ casts. -/// -/// Precondition: the cast is between ObjCObjectPointers. +// For some reason a CastExpr casting to an lvalue does not have a reference +// type. This function adds that reference to the expr type. +static QualType getTypeOfExpression(const Expr *E, const ASTContext &Ctx) { + QualType Ty = E->getType().getCanonicalType(); + if (Ty->isPointerType() || Ty->isReferenceType()) + return Ty; + if (E->isLValue()) + return Ctx.getLValueReferenceType(Ty); + if (E->isXValue()) + return Ctx.getRValueReferenceType(Ty); + return Ty; +} + ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { // We only track type info for regions. @@ -403,8 +414,20 @@ ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( if (!ToR) return C.getPredecessor(); - if (isa(CE)) + if (CE->getCastKind() == CK_BaseToDerived) { + bool CastSucceeds = true; + const ASTContext &Ctx = C.getASTContext(); + QualType FromTy = getTypeOfExpression(CE->getSubExpr(), Ctx); + QualType ToTy = getTypeOfExpression(CE, Ctx); + ToR = ToR->StripCasts(/*StripBaseAndDerivedCasts=*/true); + State = setDynamicTypeAndCastInfo(State, ToR, FromTy, ToTy, CastSucceeds); + return C.addTransition(State); + } + + // TODO: Handle the rest of explicit casts, inluding the regular C++ casts. + if (isa(CE)) { return C.getPredecessor(); + } if (const Type *NewTy = getBetterObjCType(CE, C)) { State = setDynamicTypeInfo(State, ToR, QualType(NewTy, 0)); @@ -609,6 +632,9 @@ storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym, /// symbol and the destination type of the cast are unrelated, report an error. void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); + if (CE->getCastKind() != CK_BitCast) return; @@ -621,9 +647,6 @@ void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, if (!OrigObjectPtrType || !DestObjectPtrType) return; - ProgramStateRef State = C.getState(); - ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); - ASTContext &ASTCtxt = C.getASTContext(); // This checker detects the subtyping relationships using the assignment diff --git a/clang/test/Analysis/cast-trust-base-to-derived.cpp b/clang/test/Analysis/cast-trust-base-to-derived.cpp new file mode 100644 index 00000000000000..117680acb7c148 --- /dev/null +++ b/clang/test/Analysis/cast-trust-base-to-derived.cpp @@ -0,0 +1,154 @@ +// RUN: %clang_analyze_cc1 %s -verify \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=alpha.security.taint.TaintPropagation \ +// RUN: -analyzer-checker=debug.ExprInspection + +// See issue https://github.com/llvm/llvm-project/issues/62663 + +template void clang_analyzer_dump(T); +void clang_analyzer_warnIfReached(); +void clang_analyzer_numTimesReached(); +void clang_analyzer_isTainted(int); + +extern int scanf(const char *format, ...); + +class ActionHandler { +public: + virtual ~ActionHandler() = default; + virtual void onAction(int x, int &) { + clang_analyzer_dump(x + 1); // expected-warning {{101}} + } +}; + +class MyHandler final : public ActionHandler { +public: + void onAction(int x, int &) override { + clang_analyzer_dump(x + 2); // expected-warning {{202}} + } +}; + +class MyOtherHandler final : public ActionHandler { +public: + void onAction(int x, int &) override { + clang_analyzer_dump(x + 3); // expected-warning {{403}} + } +}; + +void trust_static_types(ActionHandler *p) { + // This variable will help to see if conservative call evaluation happened or not. + int invalidation_detector; + + // At this point we don't know anything about the dynamic type of `*p`, thus the `onAction` call might be resolved to the default implementation, matching the static type. + invalidation_detector = 1000; + p->onAction(100, invalidation_detector); + clang_analyzer_dump(invalidation_detector); + // expected-warning@-1 {{1000}} on this path we trusted the type, and resolved the call to `ActionHandler::onAction(int, int&)` + // expected-warning@-2 {{conj}} on this path we conservatively evaluated the previous call + + clang_analyzer_numTimesReached(); // expected-warning {{2}} we only have that 2 paths here + // 1) inlined `ActionHandler::onAction(int, int&)` + // 2) conservatively eval called + + // Trust that the `*p` refers to an object with `MyHandler` static type (or to some other sub-class). + auto *q = static_cast(p); + (void)q; // Discard the result of the cast. We already learned the type `p` might refer to. + + invalidation_detector = 2000; + p->onAction(200, invalidation_detector); + clang_analyzer_dump(invalidation_detector); + // expected-warning@-1 {{2000}} on this path we trusted the type, and resolved the call to `MyHandler::onAction(int, int&)` + // expected-warning@-2 {{conj}} on this path we conservatively evaluated the previous call + + clang_analyzer_numTimesReached(); // expected-warning {{3}} we only have 3 paths here, not 4 + // 1) inlined 2 different callees + // 2) inlined only 1st + // 3) none were inlined + // 4) inlined only the second: This can't happen because if we conservative called a specific function on a path, we will always evaluate it like that. + // See ExprEngine::BifurcateCall and DynamicDispatchBifurcationMap. +} + + +void conflicting_casts(ActionHandler *p) { + (void)static_cast(p); + (void)static_cast(p); + int invalidation_detector = 4000; + p->onAction(400, invalidation_detector); + clang_analyzer_dump(invalidation_detector); + // expected-warning@-1 {{4000}} + // expected-warning@-2 {{conj}} +} + +// ------- + +class Base { +public: + virtual void OnRecvCancel(int port) = 0; +}; +class Handler final : public Base { +public: + void OnRecvCancel(int port) override { + clang_analyzer_dump(100 + port); // expected-warning {{+ 150}} + clang_analyzer_isTainted(port); // expected-warning {{YES}} + } +}; +class PParent { +public: + bool OnMessageReceived(); +}; +class Actor { +public: + explicit Actor(Base* aRequest) : m(aRequest) {} +protected: + Base* m; +}; + +class Parent : public Actor, public PParent { +public: + explicit Parent(Base* aRequest) : Actor(aRequest) {} + bool RecvCancel(int port) { + clang_analyzer_dump(200 + port); // expected-warning {{+ 200}} + clang_analyzer_isTainted(port); // expected-warning {{YES}} + Handler* foo = (Handler*)m; + foo->OnRecvCancel(port + 50); + return true; + } +}; + +bool PParent::OnMessageReceived() { + int port; + scanf("%i", &port); + clang_analyzer_isTainted(port); // expected-warning {{YES}} + Parent* foo = static_cast(this); + return foo->RecvCancel(port); +} + +namespace BaseToDerivedRef { +struct Base { + virtual int number() const { return 1; } +}; +struct Derived : Base { + int number() const override { return 2; } +}; +void top(Base &B) { + clang_analyzer_dump(B.number()); // expected-warning{{1}} expected-warning{{conj}} + Derived &&D = static_cast(B); + clang_analyzer_dump(B.number()); // expected-warning{{2}} expected-warning{{conj}} + clang_analyzer_dump(D.number()); // expected-warning{{2}} expected-warning{{conj}} +} +} // namespace BaseToDerivedRef + + +namespace BaseToDerivedPtr { +struct Base { + virtual int number() const { return 1; } +}; +struct Derived : Base { + int number() const override { return 2; } +}; +void top(Base *B) { + clang_analyzer_dump(B->number()); // expected-warning{{1}} expected-warning{{conj}} + auto *D = static_cast(B); + clang_analyzer_dump(B->number()); // expected-warning{{2}} expected-warning{{conj}} + clang_analyzer_dump(D->number()); // expected-warning{{2}} expected-warning{{conj}} +} +} // namespace BaseToDerivedPtr diff --git a/clang/test/Analysis/cast-value-logic.cpp b/clang/test/Analysis/cast-value-logic.cpp index 52a94f24fba670..fa359f2f13d510 100644 --- a/clang/test/Analysis/cast-value-logic.cpp +++ b/clang/test/Analysis/cast-value-logic.cpp @@ -7,6 +7,7 @@ void clang_analyzer_numTimesReached(); void clang_analyzer_warnIfReached(); void clang_analyzer_eval(bool); +void clang_analyzer_dump(int); namespace clang { struct Shape { @@ -17,20 +18,50 @@ struct Shape { const T *getAs() const; virtual double area(); + virtual void print(int n) const { + clang_analyzer_dump(n + 1); // expected-warning {{+ 1}} this was analyzed in top-level-context + } }; -class Triangle : public Shape {}; -class Rectangle : public Shape {}; -class Hexagon : public Shape {}; -class Circle : public Shape { +struct Triangle : public Shape { + void print(int n) const override { + clang_analyzer_dump(n + 2); // expected-warning {{+ 2}} this was analyzed in top-level-context + } +}; +struct Rectangle : public Shape { + void print(int n) const override { + clang_analyzer_dump(n + 3); + // expected-warning@-1 {{103}} this was inlined from 'test_conflicting_type_information' 'A->print(100)' + // expected-warning@-2 {{303}} this was inlined from 'test_conflicting_type_information' 'R->print(300)' + } +}; +struct Hexagon : public Shape { + void print(int n) const override { + clang_analyzer_dump(n + 4); // expected-warning {{+ 4}} + } +}; +struct Circle : public Shape { public: ~Circle(); }; -class SuspiciouslySpecificCircle : public Circle {}; +struct SuspiciouslySpecificCircle : public Circle {}; } // namespace clang using namespace llvm; using namespace clang; +void test_conflicting_type_information(const Shape *A) { + if (auto const *T = dyn_cast(A)) { + // Now, we associate 'Triangle' with 'A'. + if (auto const *R = dyn_cast(A)) { + // Now, we associate 'Rectangle' with 'A'. + // This should be unreachable becasue 'A' cannot be a 'Triangle' and a 'Rectangle' at the same time. + A->print(100); // Splits into 2 paths: (1) conservative eval, (2) inlining `Rectangle::print` + T->print(200); // On path (2), it will conservative eval, because the dynamic type + R->print(300); // Splits on path (2) into other 2 paths: (3) conservative eval, (4) inlining `Rectangle::print` + } + } +} + void test_regions_dyn_cast(const Shape *A, const Shape *B) { if (dyn_cast(A) && !dyn_cast(B)) clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp index 2ae5752f440237..b4085813a72a1a 100644 --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -492,11 +492,13 @@ void check_required_cast() { void check_cast_behavior(OSObject *obj) { OSArray *arr1 = OSDynamicCast(OSArray, obj); - clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}} - // expected-note@-1{{TRUE}} - // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}} - // expected-warning@-3{{FALSE}} - // expected-note@-4 {{FALSE}} + clang_analyzer_eval(arr1 == obj); // #check_cast_behavior_1 + // expected-warning@#check_cast_behavior_1 {{TRUE}} + // expected-note@#check_cast_behavior_1 {{TRUE}} + // expected-note@#check_cast_behavior_1{{Assuming 'arr1' is equal to 'obj'}} + // expected-warning@#check_cast_behavior_1 {{FALSE}} + // expected-note@#check_cast_behavior_1 {{FALSE}} + // expected-note@#check_cast_behavior_1{{Assuming 'arr1' is not equal to 'obj'}} OSArray *arr2 = OSRequiredCast(OSArray, obj); clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}} // expected-note@-1{{TRUE}}