From ac82f6538c18c407e41d2b1df5b0be80f6d79cda Mon Sep 17 00:00:00 2001 From: wuhuiquan Date: Mon, 1 Sep 2025 14:16:37 +0800 Subject: [PATCH] [safeZone] Fix the issue of safe statement constant conversion error and nullibility type detection --- clang/include/clang/Sema/Sema.h | 4 +- clang/lib/AST/ExprConstant.cpp | 4 + .../lib/Analysis/BSC/BSCNullabilityCheck.cpp | 104 +++++++++++------- clang/lib/Sema/BSC/SemaBSCSafeZone.cpp | 57 +++++++--- .../unsafe_cast_expr/unsafe_cast_expr.cbs | 20 +++- .../unsafe_implicit_conversion.cbs | 16 ++- .../NullabilityCheck/owned_ptr_nullptr.cbs | 23 ++++ 7 files changed, 171 insertions(+), 57 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3c5e0e1e1ff6..d8de64a915e3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12424,8 +12424,8 @@ public: bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, BuiltinType::Kind DestType); - bool IsSafeConversion(QualType DestType, Expr *Expr); - bool IsSafeConstantValueConversion(QualType DestType, Expr *SrcExpr); + bool IsSafeConversion(QualType DestType, Expr *E); + bool IsSafeConstantValueConversion(QualType DestType, Expr *E); bool IsSafeFunctionPointerTypeCast(QualType DestType, Expr *SrcExpr); bool IsSafeFunctionPointerType(QualType Type); bool IsUnsafeType(QualType Type); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 92e3087d9dd4..c694f1b45838 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7437,6 +7437,10 @@ public: bool VisitParenExpr(const ParenExpr *E) { return StmtVisitorTy::Visit(E->getSubExpr()); } +#if ENABLE_BSC + bool VisitSafeExpr(const SafeExpr *E) + { return StmtVisitorTy::Visit(E->getSubExpr()); } +#endif bool VisitUnaryExtension(const UnaryOperator *E) { return StmtVisitorTy::Visit(E->getSubExpr()); } bool VisitUnaryPlus(const UnaryOperator *E) diff --git a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp index cf1183773009..6f3e586d6975 100644 --- a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp @@ -124,7 +124,7 @@ public: void VisitCallExpr(CallExpr *CE); void VisitReturnStmt(ReturnStmt *RS); void VisitCStyleCastExpr(CStyleCastExpr *CSCE); - NullabilityKind getExprPathNullability(Expr *E); + NullabilityKind getExprPathNullability(Expr *E, bool Point = false); void SetCFGBlocksByExpr(Expr *PtrE, const CFGBlock *NonNullBlock, const CFGBlock *NullableBlock); void PassConditionStatusToSuccBlocks(Stmt *Cond); @@ -189,17 +189,6 @@ static MemberExpr *getMemberExprFromExpr(Expr *E) { return nullptr; } -static CallExpr *getCallExprFromExpr(Expr *E) { - if (auto CE = dyn_cast(E)) { - return CE; - } else if (auto ICE = dyn_cast(E)) { - return getCallExprFromExpr(ICE->getSubExpr()); - } else if (auto PE = dyn_cast(E)) { - return getCallExprFromExpr(PE->getSubExpr()); - } - return nullptr; -} - // We can get PathNullability for these exprs: // 1. int *p = nullptr; // nullptr is NullExpr // 2. int *p = foo(); // foo() is CallExpr @@ -207,51 +196,88 @@ static CallExpr *getCallExprFromExpr(Expr *E) { // 4. int *p = p1; // p1 is VarDecl // 5. int *p = s.p; // s.p is MemberExpr // 6. int *p = a == 1 ? nullptr : &a; // ConditionOperator -NullabilityKind TransferFunctions::getExprPathNullability(Expr *E) { +NullabilityKind TransferFunctions::getExprPathNullability(Expr *E, bool Point) { QualType QT = E->getType(); QualType CanQT = QT.getCanonicalType(); - if (CanQT->isPointerType()) { - if (E->isNullExpr(Ctx)) - return NullabilityKind::Nullable; - if (auto CE = getCallExprFromExpr(E)) - return getDefNullability(CE->getType(), Ctx); - if (auto CSCE = dyn_cast(E)) - return getDefNullability(CSCE->getTypeAsWritten(), Ctx); - if (auto UO = dyn_cast(E)) { - UnaryOperator::Opcode Op = UO->getOpcode(); + if (Point || CanQT->isPointerType()) { + switch (E->getStmtClass()) { + case Expr::ParenExprClass: + return getExprPathNullability(cast(E)->getSubExpr(), true); + case Expr::SafeExprClass: + return getExprPathNullability(cast(E)->getSubExpr(), true); + case Expr::ImplicitCastExprClass: + return getExprPathNullability(cast(E)->getSubExpr(), + true); + case Expr::CallExprClass: + return getDefNullability(cast(E)->getType(), Ctx); + case Expr::ConditionalOperatorClass: { + NullabilityKind LHSNK = + getExprPathNullability(cast(E)->getLHS(), true); + NullabilityKind RHSNK = + getExprPathNullability(cast(E)->getRHS(), true); + if (LHSNK == NullabilityKind::Nullable || + RHSNK == NullabilityKind::Nullable) + return NullabilityKind::Nullable; + else if (LHSNK == NullabilityKind::NonNull && + RHSNK == NullabilityKind::NonNull) + return NullabilityKind::NonNull; + break; + } + case Expr::CStyleCastExprClass: + return getDefNullability(cast(E)->getTypeAsWritten(), + Ctx); + case Expr::UnaryOperatorClass: { + UnaryOperator::Opcode Op = cast(E)->getOpcode(); if (Op == UO_AddrOf || Op == UO_AddrMut || Op == UO_AddrConst || Op == UO_AddrMutDeref || Op == UO_AddrConstDeref) return NullabilityKind::NonNull; + break; } - if (VarDecl *VD = getVarDeclFromExpr(E)) { - NullabilityKind NK = getDefNullability(VD->getType(), Ctx); - if (NK == NullabilityKind::NonNull) - return NullabilityKind::NonNull; - else if (NK == NullabilityKind::Nullable && CurrStatusVD.count(VD)) - return CurrStatusVD[VD]; + case Expr::BinaryOperatorClass: { + auto *BO = cast(E); + if (BO->getOpcode() == BO_Comma) { + return getExprPathNullability(cast(E)->getRHS(), true); + } + break; } - if (MemberExpr *ME = getMemberExprFromExpr(E)) { - if (auto FD = dyn_cast(ME->getMemberDecl())) { + case Expr::DeclRefExprClass: { + if (VarDecl *VD = dyn_cast(cast(E)->getDecl())) { + NullabilityKind NK = getDefNullability(VD->getType(), Ctx); + if (NK == NullabilityKind::NonNull) + return NullabilityKind::NonNull; + else if (NK == NullabilityKind::Nullable && CurrStatusVD.count(VD)) + return CurrStatusVD[VD]; + } + break; + } + case Expr::MemberExprClass: { + if (auto FD = dyn_cast(cast(E)->getMemberDecl())) { NullabilityKind NK = getDefNullability(FD->getType(), Ctx); if (NK == NullabilityKind::NonNull) return NullabilityKind::NonNull; else if (NK == NullabilityKind::Nullable) { FieldPath FP; - VisitMEForFieldPath(ME, FP); + VisitMEForFieldPath(cast(E), FP); if (CurrStatusFP.count(FP)) return CurrStatusFP[FP]; } } + break; } - if (ConditionalOperator *CO = dyn_cast(E)) { - NullabilityKind LHSNK = getExprPathNullability(CO->getLHS()); - NullabilityKind RHSNK = getExprPathNullability(CO->getRHS()); - if (LHSNK == NullabilityKind::Nullable || - RHSNK == NullabilityKind::Nullable) + case Expr::IntegerLiteralClass: { + if (cast(E)->getValue().getZExtValue() == 0) + return NullabilityKind::Nullable; + break; + } + default: + break; + } + if (QT->isNullPtrType()) { + return NullabilityKind::Nullable; + } + if (Optional I = E->getIntegerConstantExpr(Ctx)) { + if (*I == 0) return NullabilityKind::Nullable; - else if (LHSNK == NullabilityKind::NonNull || - RHSNK == NullabilityKind::NonNull) - return NullabilityKind::NonNull; } } // For no-pointer type, we treat it as Unspecified. diff --git a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp index 57199522e9f3..1e53906233e0 100644 --- a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp +++ b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #if ENABLE_BSC +#include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" @@ -112,12 +113,12 @@ bool Sema::IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, return false; } -bool Sema::IsSafeConstantValueConversion(QualType DestType, Expr *Expr) { - QualType SrcType = Expr->getType(); +bool Sema::IsSafeConstantValueConversion(QualType DestType, Expr *E) { + QualType SrcType = E->getType(); if (SrcType->isIntegralType(Context) && DestType->isIntegralType(Context)) { - QualType IntTy = Expr->getType().getUnqualifiedType(); + QualType IntTy = E->getType().getUnqualifiedType(); Expr::EvalResult EVResult; - bool CstInt = Expr->EvaluateAsInt(EVResult, Context); + bool CstInt = E->EvaluateAsInt(EVResult, Context); bool IntSigned = IntTy->hasSignedIntegerRepresentation(); bool OtherIntSigned = DestType->hasSignedIntegerRepresentation(); @@ -148,10 +149,10 @@ bool Sema::IsSafeConstantValueConversion(QualType DestType, Expr *Expr) { } } if (DestType->isRealFloatingType()) { - if (SrcType->isRealFloatingType() && !Expr->isValueDependent()) { + if (SrcType->isRealFloatingType() && !E->isValueDependent()) { // lose of the precision conversion is not allowed llvm::APFloat Result(0.0); - bool CstFloat = Expr->EvaluateAsFloat(Result, Context); + bool CstFloat = E->EvaluateAsFloat(Result, Context); if (CstFloat) { bool Truncated = true; Result.convert(Context.getFloatTypeSemantics(DestType), @@ -227,7 +228,34 @@ bool Sema::IsSafeFunctionPointerTypeCast(QualType DestType, Expr *SrcExpr) { return true; } -bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { +DeclRefExpr *getDeclRefExprForEnumCoversion(Expr *E) { + if (!E) { + return nullptr; + } + switch (E->getStmtClass()) { + case Expr::ParenExprClass: + return getDeclRefExprForEnumCoversion(cast(E)->getSubExpr()); + case Expr::SafeExprClass: + return getDeclRefExprForEnumCoversion(cast(E)->getSubExpr()); + case Expr::ImplicitCastExprClass: + return getDeclRefExprForEnumCoversion( + cast(E)->getSubExpr()); + case Expr::DeclRefExprClass: + return dyn_cast(E); + case Expr::BinaryOperatorClass: { + auto *BO = cast(E); + if (BO->getOpcode() == BO_Comma) { + return getDeclRefExprForEnumCoversion(cast(E)->getRHS()); + } + break; + } + default: + break; + } + return nullptr; +} + +bool Sema::IsSafeConversion(QualType DestType, Expr *E) { // check function pointer Type in 'IsSafeFunctionPointerTypeCast' if (DestType->isFunctionPointerType()) { return true; @@ -240,20 +268,20 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { // Init a owned or borrow pointer by nullptr is allowed in the safe zone if ((DestType.isOwnedQualified() && DestType->isPointerType()) || DestType.isBorrowQualified()) { - if (isa(Expr)) + if (isa(E)) return true; } bool IsSafeBehavior = true; - QualType SrcType = Expr->getType(); - if (IsTraitExpr(Expr)) { + QualType SrcType = E->getType(); + if (IsTraitExpr(E)) { SrcType = CompleteTraitType(SrcType); } // conversion from non trait pointer type to trait pointer type is allowed if (DestType->isTraitPointerType() && !SrcType->isTraitPointerType()) { return true; } - if (const ConditionalOperator *Exp = dyn_cast(Expr)) { + if (const ConditionalOperator *Exp = dyn_cast(E)) { if (IsSafeConversion(DestType, Exp->getTrueExpr())) { return IsSafeConversion(DestType, Exp->getFalseExpr()); } else { @@ -290,7 +318,7 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { } // conversion const value is allowed, if the destination type can embrace it if (!DestType->isEnumeralType() && !SrcType->isEnumeralType() && - IsSafeConstantValueConversion(DestType, Expr)) { + IsSafeConstantValueConversion(DestType, E)) { IsSafeBehavior = true; } @@ -300,7 +328,8 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { if (SrcType.getCanonicalType() == DestType.getCanonicalType()) { IsSafeBehavior = true; // conversion enum value type to enum variable type is allowed - } else if (DeclRefExpr *DRE = dyn_cast(Expr)) { + } + if (auto *DRE = getDeclRefExprForEnumCoversion(E)) { if (EnumConstantDecl *ECD = dyn_cast(DRE->getDecl())) { EnumDecl *Enum = cast(ECD->getDeclContext()); @@ -314,7 +343,7 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { } if (!IsSafeBehavior) { - Diag(Expr->getExprLoc(), diag::err_unsafe_cast) << SrcType << DestType; + Diag(E->getExprLoc(), diag::err_unsafe_cast) << SrcType << DestType; } return IsSafeBehavior; } diff --git a/clang/test/BSC/Negative/SafeZone/unsafe_cast_expr/unsafe_cast_expr.cbs b/clang/test/BSC/Negative/SafeZone/unsafe_cast_expr/unsafe_cast_expr.cbs index 0668d52f2694..319ffca2599f 100644 --- a/clang/test/BSC/Negative/SafeZone/unsafe_cast_expr/unsafe_cast_expr.cbs +++ b/clang/test/BSC/Negative/SafeZone/unsafe_cast_expr/unsafe_cast_expr.cbs @@ -110,12 +110,20 @@ void baseTypeCastConver() { // char -128~127 char b = (char)a; // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} b = (char)1; // ok + b = unsafe((char)1); // ok + b = safe((char)1); // ok b = (char)128; // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} + b = unsafe((char)128); // ok + b = safe((char)128); // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} + constexpr int c = 10; b = (int)c; a = (int)b; // char->int ok a = (int)'b'; // char->int ok - + a = unsafe((int)b); // ok + a = safe((int)b); // ok + a = (int)(unsafe(b)); // ok + a = (int)(safe(b)); // ok unsigned int d = 0; d = (unsigned int)b; // expected-error{{conversion from type 'char' to 'unsigned int' is forbidden in the safe zone}} } @@ -126,6 +134,10 @@ void baseTypeCastConver() { _Bool c = (_Bool)b; // expected-error{{conversion from type 'int' to '_Bool' is forbidden in the safe zone}} c = (_Bool)1; c = (_Bool)2; // expected-error{{conversion from type 'int' to '_Bool' is forbidden in the safe zone}} + c = unsafe((_Bool)1); + c = safe((_Bool)1); + c = (_Bool)(safe(1)); + c = (_Bool)(unsafe(1)); } // int <-> enum enum E { @@ -143,9 +155,15 @@ void baseTypeCastConver() { enum E num = (enum E)a; // expected-error{{conversion from type 'int' to 'enum E' is forbidden in the safe zone}} num = (enum E)0; // expected-error{{conversion from type 'int' to 'enum E' is forbidden in the safe zone}} num = (enum E)ZERO; // ok + num = safe((enum E)ZERO); + num = unsafe((enum E)ZERO); enum F day = SUN; num = (enum E)day; // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} num = (enum E)SUN; // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = safe((enum E)day); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = safe((enum E)SUN); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = unsafe((enum E)day); // ok + num = unsafe((enum E)SUN); // ok a = (int)num; // expected-error{{conversion from type 'enum E' to 'int' is forbidden in the safe zone}} a = (int)ZERO; } diff --git a/clang/test/BSC/Negative/SafeZone/unsafe_implicit_conversion/unsafe_implicit_conversion.cbs b/clang/test/BSC/Negative/SafeZone/unsafe_implicit_conversion/unsafe_implicit_conversion.cbs index 604a1b2ba199..d95f1eb4eb63 100644 --- a/clang/test/BSC/Negative/SafeZone/unsafe_implicit_conversion/unsafe_implicit_conversion.cbs +++ b/clang/test/BSC/Negative/SafeZone/unsafe_implicit_conversion/unsafe_implicit_conversion.cbs @@ -111,11 +111,17 @@ void baseTypeCastConver() { char b = a; // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} b = 1; // ok b = 128; // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} + b = unsafe(1); // ok + b = safe(1); // ok + b = unsafe(128); // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} + b = safe(128); // expected-error{{conversion from type 'int' to 'char' is forbidden in the safe zone}} + constexpr int c = 10; b = c; a = b; // char->int ok a = 'b'; // char->int ok - + a = unsafe(b); // ok + a = safe(b); // ok unsigned int d = 0; d = b; // expected-error{{conversion from type 'char' to 'unsigned int' is forbidden in the safe zone}} } @@ -126,6 +132,8 @@ void baseTypeCastConver() { _Bool c = b; // expected-error{{conversion from type 'int' to '_Bool' is forbidden in the safe zone}} c = 1; c = 2; // expected-error{{conversion from type 'int' to '_Bool' is forbidden in the safe zone}} + c = unsafe(1); + c = safe(1); } // char <-> long double safe { @@ -149,11 +157,17 @@ void baseTypeCastConver() { enum E num = a; // expected-error{{conversion from type 'int' to 'enum E' is forbidden in the safe zone}} num = 0; // expected-error{{conversion from type 'int' to 'enum E' is forbidden in the safe zone}} num = ZERO; // ok + num = safe(ZERO); // ok + num = unsafe(ZERO); // ok enum F day = SUN; num = day; // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} num = SUN; // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} a = num; // expected-error{{conversion from type 'enum E' to 'int' is forbidden in the safe zone}} a = ZERO; + num = safe(day); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = safe(SUN); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = unsafe(day); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} + num = unsafe(SUN); // expected-error{{conversion from type 'enum F' to 'enum E' is forbidden in the safe zone}} enum F f1 = a > 0 ? SUN : MON; // ok enum F f2 = a == 0 ? SUN : MON; // ok enum F f3 = a > 0 ? SUN : ZERO; // expected-error{{conversion from type 'enum E' to 'enum F' is forbidden in the safe zone}} diff --git a/clang/test/BSC/Positive/NullabilityCheck/owned_ptr_nullptr.cbs b/clang/test/BSC/Positive/NullabilityCheck/owned_ptr_nullptr.cbs index 89d2c57f8ee8..819bf8f2123b 100644 --- a/clang/test/BSC/Positive/NullabilityCheck/owned_ptr_nullptr.cbs +++ b/clang/test/BSC/Positive/NullabilityCheck/owned_ptr_nullptr.cbs @@ -119,6 +119,29 @@ safe void test7(void) { } } +struct S2 { + int *owned _Nullable p; + int *borrow _Nullable q; +}; + +safe void test8(void) { + int a = 1; + struct S2 s = { + .p = unsafe(safe_malloc(a)), + .q = unsafe(&mut a) + }; + safe_free((void *owned)s.p); +} + +safe void test9(void) { + int a = 1; + struct S2 s = { + .p = (safe_malloc(a)), + .q = (&mut a) + }; + safe_free((void *owned)s.p); +} + int main() { test1(); test2(); -- Gitee