aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorflovent <flbven@protonmail.com>2025-07-07 19:46:30 +0800
committerGitHub <noreply@github.com>2025-07-07 13:46:30 +0200
commit22357fe33a8a8cc221632e32cb443676f1feeda9 (patch)
tree95c5d9388d3ff83fe0de33c071fbfeae9c11291a /clang/lib/StaticAnalyzer
parent1113224f9444f5c2cf69784cd3c110b8dd560897 (diff)
downloadllvm-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.cpp48
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).