diff options
author | flovent <flbven@protonmail.com> | 2025-07-07 19:46:30 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-07 13:46:30 +0200 |
commit | 22357fe33a8a8cc221632e32cb443676f1feeda9 (patch) | |
tree | 95c5d9388d3ff83fe0de33c071fbfeae9c11291a /clang/lib/StaticAnalyzer | |
parent | 1113224f9444f5c2cf69784cd3c110b8dd560897 (diff) | |
download | llvm-22357fe33a8a8cc221632e32cb443676f1feeda9.zip llvm-22357fe33a8a8cc221632e32cb443676f1feeda9.tar.gz llvm-22357fe33a8a8cc221632e32cb443676f1feeda9.tar.bz2 |
[analyzer] Avoid unnecessary super region invalidation in `CStringChecker` (#146212)
Bounded string functions takes smallest of two values as it's copy size
(`amountCopied` variable in `evalStrcpyCommon`), and it's used to
decided whether this operation will cause out-of-bound access and
invalidate it's super region if it does.
for `strlcat`: `amountCopied = min (size - dstLen - 1 , srcLen)`
for others: `amountCopied = min (srcLen, size)`
Currently when one of two values is unknown or `SValBuilder` can't
decide which one is smaller, `amountCopied` will remain `UnknownVal`,
which will invalidate copy destination's super region unconditionally.
This patch add check to see if one of these two values is definitely
in-bound, if so `amountCopied` has to be in-bound too, because it‘s less
than or equal to them, we can avoid the invalidation of super region and
some related false positives in this situation.
Note: This patch uses `size` as an approximation of `size - dstLen - 1`
in `strlcat` case because currently analyzer doesn't handle complex
expressions like this very well.
Closes #143807.
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 4d12fdc..31cb150 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -2223,6 +2223,44 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call, Result = lastElement; } + // For bounded method, amountCopied take the minimum of two values, + // for ConcatFnKind::strlcat: + // amountCopied = min (size - dstLen - 1 , srcLen) + // for others: + // amountCopied = min (srcLen, size) + // So even if we don't know about amountCopied, as long as one of them will + // not cause an out-of-bound access, the whole function's operation will not + // too, that will avoid invalidating the superRegion of data member in that + // situation. + bool CouldAccessOutOfBound = true; + if (IsBounded && amountCopied.isUnknown()) { + auto CouldAccessOutOfBoundForSVal = + [&](std::optional<NonLoc> Val) -> bool { + if (!Val) + return true; + return !isFirstBufInBound(C, state, C.getSVal(Dst.Expression), + Dst.Expression->getType(), *Val, + C.getASTContext().getSizeType()); + }; + + CouldAccessOutOfBound = CouldAccessOutOfBoundForSVal(strLengthNL); + + if (CouldAccessOutOfBound) { + // Get the max number of characters to copy. + const Expr *LenExpr = Call.getArgExpr(2); + SVal LenVal = state->getSVal(LenExpr, LCtx); + + // Protect against misdeclared strncpy(). + LenVal = svalBuilder.evalCast(LenVal, sizeTy, LenExpr->getType()); + + // Because analyzer doesn't handle expressions like `size - + // dstLen - 1` very well, we roughly use `size` for + // ConcatFnKind::strlcat here, same with other concat kinds. + CouldAccessOutOfBound = + CouldAccessOutOfBoundForSVal(LenVal.getAs<NonLoc>()); + } + } + // Invalidate the destination (regular invalidation without pointer-escaping // the address of the top-level region). This must happen before we set the // C string length because invalidation will clear the length. @@ -2230,9 +2268,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call, // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // string, but that's still an improvement over blank invalidation. - state = invalidateDestinationBufferBySize( - C, state, Dst.Expression, Call.getCFGElementRef(), *dstRegVal, - amountCopied, C.getASTContext().getSizeType()); + if (CouldAccessOutOfBound) + state = invalidateDestinationBufferBySize( + C, state, Dst.Expression, Call.getCFGElementRef(), *dstRegVal, + amountCopied, C.getASTContext().getSizeType()); + else + state = invalidateDestinationBufferNeverOverflows( + C, state, Call.getCFGElementRef(), *dstRegVal); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). |