[libcxx-commits] [clang] [compiler-rt] [libcxx] [clang] Add unistd fortify checks and analyzer size constraints (PR #196499)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat May 9 00:14:02 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: Denys Fedoryshchenko (nuclearcat)
<details>
<summary>Changes</summary>
Based on and partially rewritten from PR#<!-- -->161737 by @<!-- -->ColinKinloch.
This adds Clang fortify diagnostics for several `unistd.h` functions and extends the static analyzer coverage for related POSIX I/O calls.
The Sema changes:
* define builtins for `read`, `write`, `getcwd`, `readlink`, and `readlinkat`
* add fortify checks for destination overflow and source over-read cases
* introduce min/max operation-size handling so existing fortify checks can model both lower and upper bounds
* recognize `pread`/`pread64`/`pwrite`/`pwrite64` in the fortify checker without declaring them as builtins, since their `off_t` argument width depends on platform/header configuration
The analyzer changes:
* add `SSIZE_MAX` size constraints for `read`, `write`, `readlink`, and `readlinkat`
* add summaries for `pread`, `pread64`, `pwrite`, and `pwrite64`
* tighten tests for over-large sizes, including cases such as passing `-1` to `size_t` parameters
This may produce new `-Wfortify-source` diagnostics for code that passes buffer sizes larger than the known object size or larger than `SSIZE_MAX`.
Related to/Part of PR#<!-- -->142230.
Tested:
* `ninja check-clang-sema`
* `ninja check-clang-analysis`
---
Patch is 46.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196499.diff
19 Files Affected:
- (modified) clang/include/clang/Basic/Builtins.td (+43)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+8)
- (modified) clang/lib/AST/ASTContext.cpp (+6-3)
- (modified) clang/lib/Sema/SemaChecking.cpp (+154-44)
- (modified) clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp (+39-7)
- (modified) clang/test/Analysis/Inputs/std-c-library-functions-POSIX.h (+4)
- (modified) clang/test/Analysis/array-struct.c (+2-2)
- (modified) clang/test/Analysis/bstring.c (+19-2)
- (modified) clang/test/Analysis/malloc.c (+1-1)
- (modified) clang/test/Analysis/pr22954.c (+2-1)
- (modified) clang/test/Analysis/std-c-library-functions-POSIX.c (+6-2)
- (modified) clang/test/Analysis/std-c-library-functions-arg-constraints.c (+58)
- (modified) clang/test/Analysis/std-c-library-functions.c (+2-2)
- (modified) clang/test/Sema/asm-label.c (+3-3)
- (modified) clang/test/Sema/builtin-memcpy.c (+2-1)
- (modified) clang/test/Sema/warn-fortify-source.c (+97-3)
- (modified) clang/utils/TableGen/ClangBuiltinsEmitter.cpp (+1)
- (modified) compiler-rt/test/asan/TestCases/Windows/issue64990.cpp (+1-1)
- (modified) libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp (+1-1)
``````````diff
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 4a7eaeb3d353e..c1677cf2f5acc 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3832,6 +3832,8 @@ def StrnCaseCmp : GNULibBuiltin<"strings.h"> {
let RequiresUndef = 1;
}
+// POSIX unistd.h
+
def GNU_Exit : GNULibBuiltin<"unistd.h"> {
let Spellings = ["_exit"];
let Attributes = [NoReturn];
@@ -3844,6 +3846,47 @@ def VFork : LibBuiltin<"unistd.h"> {
let Prototype = "pid_t()";
}
+def Read : LibBuiltin<"unistd.h"> {
+ let Spellings = ["read"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void*, size_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+def Write : LibBuiltin<"unistd.h"> {
+ let Spellings = ["write"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void const*, size_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+// pread/pread64/pwrite/pwrite64 are intentionally not declared as builtins.
+// Their prototypes use off_t, whose width is platform- and macro-dependent
+// (notably _FILE_OFFSET_BITS), so a fixed builtin signature would clash with
+// the system header on some targets. Fortify checks for these functions are
+// dispatched by name in Sema::checkFortifiedBuiltinMemoryFunction instead.
+
+def GetCWD : LibBuiltin<"unistd.h"> {
+ let Spellings = ["getcwd"];
+ let Attributes = [NoThrow];
+ let Prototype = "char*(char*, size_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+def ReadLink : LibBuiltin<"unistd.h"> {
+ let Spellings = ["readlink"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(char const* restrict, char* restrict, size_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+def ReadLinkAt : LibBuiltin<"unistd.h"> {
+ let Spellings = ["readlinkat"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, char const* restrict, char* restrict, size_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
// POSIX pthread.h
def PthreadCreate : GNULibBuiltin<"pthread.h"> {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c69b2ce3648f8..38d328b120f5d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -962,9 +962,17 @@ def warn_builtin_chk_overflow : Warning<
def warn_fortify_source_overflow
: Warning<warn_builtin_chk_overflow.Summary>, InGroup<FortifySource>;
+def warn_fortify_destination_over_read
+ : Warning<"'%0' will always over-read; source buffer has size %1,"
+ " but size argument is %2">,
+ InGroup<FortifySource>;
def warn_fortify_source_size_mismatch : Warning<
"'%0' size argument is too large; destination buffer has size %1,"
" but size argument is %2">, InGroup<FortifySource>;
+def warn_fortify_destination_size_mismatch
+ : Warning<"'%0' size argument is too large; source buffer has size %1,"
+ " but size argument is %2">,
+ InGroup<FortifySource>;
def warn_fortify_strlen_overflow: Warning<
"'%0' will always overflow; destination buffer has size %1,"
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a0894318dbd53..ec7eb82b8e76e 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12696,9 +12696,12 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'b'!");
Type = Context.BoolTy;
break;
- case 'z': // size_t.
- assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'z'!");
- Type = Context.getSizeType();
+ case 'z': // size_t and ssize_t.
+ assert(HowLong == 0 && "Bad modifiers for 'z'!");
+ if (Signed)
+ Type = Context.getSignedSizeType();
+ else
+ Type = Context.getSizeType();
break;
case 'w': // wchar_t.
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'w'!");
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 4706fa5d3cde0..dfb898d94b1fb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1165,7 +1165,26 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
unsigned BuiltinID = UseDecl->getBuiltinID(/*ConsiderWrappers=*/true);
- if (!BuiltinID)
+ // Some libc I/O functions are intentionally not Clang builtins because their
+ // prototypes use off_t, whose width is platform- and macro-dependent
+ // (notably _FILE_OFFSET_BITS). Recognize them by name so fortify checks
+ // work regardless of the platform's off_t encoding.
+ enum class LibCDispatch { None, PRead, PWrite };
+ LibCDispatch LibC = LibCDispatch::None;
+ StringRef LibCName;
+ if (!BuiltinID && FD->isExternC() && FD->getIdentifier() &&
+ TheCall->getNumArgs() == 4) {
+ StringRef Name = FD->getIdentifier()->getName();
+ if (Name == "pread" || Name == "pread64") {
+ LibC = LibCDispatch::PRead;
+ LibCName = Name;
+ } else if (Name == "pwrite" || Name == "pwrite64") {
+ LibC = LibCDispatch::PWrite;
+ LibCName = Name;
+ }
+ }
+
+ if (!BuiltinID && LibC == LibCDispatch::None)
return;
const TargetInfo &TI = getASTContext().getTargetInfo();
@@ -1249,12 +1268,25 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
return std::nullopt;
};
+ // Size of the memory read from
std::optional<llvm::APSInt> SourceSize;
+ // Size of the memory written to
std::optional<llvm::APSInt> DestinationSize;
- unsigned DiagID = 0;
+ // Maximum operation size for detecting possible out of bounds access
+ std::optional<llvm::APSInt> MaxOperationSize;
+ // Minimum operation size for detecting definite out of bounds access
+ std::optional<llvm::APSInt> MinOperationSize;
+
+ unsigned DiagOverflowID = diag::warn_fortify_source_overflow;
+ unsigned DiagMayOverflowID = diag::warn_fortify_source_size_mismatch;
+ unsigned DiagOverReadID = diag::warn_fortify_destination_over_read;
+ unsigned DiagMayOverReadID = diag::warn_fortify_destination_size_mismatch;
bool IsChkVariant = false;
- auto GetFunctionName = [&]() {
+ auto GetFunctionName = [&]() -> std::string {
+ if (LibC != LibCDispatch::None)
+ return LibCName.str();
+
std::string FunctionNameStr =
getASTContext().BuiltinInfo.getName(BuiltinID);
llvm::StringRef FunctionName = FunctionNameStr;
@@ -1270,6 +1302,17 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
return FunctionName.str();
};
+ if (LibC == LibCDispatch::PRead) {
+ // pread/pread64: ssize_t(int fd, void buf[.count], size_t count, off_t);
+ // Up to count(2) bytes are written into buf(1).
+ DestinationSize = ComputeSizeArgument(1);
+ MaxOperationSize = ComputeExplicitObjectSizeArgument(2);
+ } else if (LibC == LibCDispatch::PWrite) {
+ // pwrite/pwrite64: ssize_t(int, const void buf[.count], size_t count, off_t);
+ // Up to count(2) bytes are read from buf(1).
+ SourceSize = ComputeSizeArgument(1);
+ MaxOperationSize = ComputeExplicitObjectSizeArgument(2);
+ } else
switch (BuiltinID) {
default:
return;
@@ -1279,8 +1322,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BIstpcpy:
case Builtin::BI__builtin_strcpy:
case Builtin::BIstrcpy: {
- DiagID = diag::warn_fortify_strlen_overflow;
- SourceSize = ComputeStrLenArgument(1);
+ DiagOverflowID = diag::warn_fortify_strlen_overflow;
+ MinOperationSize = ComputeStrLenArgument(1);
DestinationSize = ComputeSizeArgument(0);
break;
}
@@ -1288,8 +1331,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin___strcat_chk:
case Builtin::BI__builtin___stpcpy_chk:
case Builtin::BI__builtin___strcpy_chk: {
- DiagID = diag::warn_fortify_strlen_overflow;
- SourceSize = ComputeStrLenArgument(1);
+ DiagOverflowID = diag::warn_fortify_strlen_overflow;
+ MinOperationSize = ComputeStrLenArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
break;
@@ -1315,12 +1358,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
auto Diagnose = [&](unsigned ArgIndex, unsigned DestSize,
unsigned SourceSize) {
- DiagID = diag::warn_fortify_scanf_overflow;
unsigned Index = ArgIndex + DataIndex;
std::string FunctionName = GetFunctionName();
DiagRuntimeBehavior(TheCall->getArg(Index)->getBeginLoc(), TheCall,
- PDiag(DiagID) << FunctionName << (Index + 1)
- << DestSize << SourceSize);
+ PDiag(diag::warn_fortify_scanf_overflow)
+ << FunctionName << (Index + 1) << DestSize
+ << SourceSize);
};
auto ShiftedComputeSizeArgument = [&](unsigned Index) {
@@ -1351,11 +1394,11 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
if (!analyze_format_string::ParsePrintfString(
H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
Context.getTargetInfo(), false)) {
- DiagID = H.isKernelCompatible()
- ? diag::warn_format_overflow
- : diag::warn_format_overflow_non_kprintf;
- SourceSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
- .extOrTrunc(SizeTypeWidth);
+ DiagOverflowID = H.isKernelCompatible()
+ ? diag::warn_format_overflow
+ : diag::warn_format_overflow_non_kprintf;
+ MinOperationSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
+ .extOrTrunc(SizeTypeWidth);
if (BuiltinID == Builtin::BI__builtin___sprintf_chk) {
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
@@ -1367,6 +1410,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
return;
}
+
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BI__builtin___memset_chk:
@@ -1377,8 +1421,9 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin___stpncpy_chk:
case Builtin::BI__builtin___memccpy_chk:
case Builtin::BI__builtin___mempcpy_chk: {
- DiagID = diag::warn_builtin_chk_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
+ DiagOverflowID = diag::warn_builtin_chk_overflow;
+ MinOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
IsChkVariant = true;
@@ -1387,8 +1432,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin___snprintf_chk:
case Builtin::BI__builtin___vsnprintf_chk: {
- DiagID = diag::warn_builtin_chk_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(1);
+ DiagOverflowID = diag::warn_builtin_chk_overflow;
+ MinOperationSize = ComputeExplicitObjectSizeArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(3);
IsChkVariant = true;
break;
@@ -1405,44 +1450,74 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
// diagnostic isn't quite right. We should still diagnose passing a buffer
// size larger than the destination buffer though; this is a runtime abort
// in _FORTIFY_SOURCE mode, and is quite suspicious otherwise.
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
break;
}
+ case Builtin::BImemset:
+ case Builtin::BI__builtin_memset:
case Builtin::BIbzero:
- case Builtin::BI__builtin_bzero:
+ case Builtin::BI__builtin_bzero: {
+ MinOperationSize = MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ DestinationSize = ComputeSizeArgument(0);
+ break;
+ }
+
case Builtin::BImemcpy:
case Builtin::BI__builtin_memcpy:
case Builtin::BImemmove:
case Builtin::BI__builtin_memmove:
- case Builtin::BImemset:
- case Builtin::BI__builtin_memset:
case Builtin::BImempcpy:
case Builtin::BI__builtin_mempcpy: {
- DiagID = diag::warn_fortify_source_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ MinOperationSize = MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
+ SourceSize = ComputeSizeArgument(1);
break;
}
case Builtin::BIbcopy:
case Builtin::BI__builtin_bcopy: {
- DiagID = diag::warn_fortify_source_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ MinOperationSize = MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ SourceSize = ComputeSizeArgument(0);
DestinationSize = ComputeSizeArgument(1);
break;
}
+
+ case Builtin::BIread:
+ case Builtin::BI__builtin_read:
+ case Builtin::BIreadlink:
+ case Builtin::BI__builtin_readlink:
+ case Builtin::BIreadlinkat:
+ case Builtin::BI__builtin_readlinkat:
+ case Builtin::BIgetcwd:
+ case Builtin::BI__builtin_getcwd: {
+ DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
+ MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ break;
+ }
+
+ case Builtin::BIwrite:
+ case Builtin::BI__builtin_write: {
+ SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
+ MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ break;
+ }
+
case Builtin::BIsnprintf:
case Builtin::BI__builtin_snprintf:
case Builtin::BIvsnprintf:
case Builtin::BI__builtin_vsnprintf: {
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(1);
+ MaxOperationSize = ComputeExplicitObjectSizeArgument(1);
const auto *FormatExpr = TheCall->getArg(2)->IgnoreParenImpCasts();
StringRef FormatStrRef;
size_t StrLen;
- if (SourceSize &&
+ if (MaxOperationSize &&
ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
EstimateSizeFormatHandler H(FormatStrRef);
const char *FormatBytes = FormatStrRef.data();
@@ -1452,13 +1527,13 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
llvm::APSInt FormatSize =
llvm::APSInt::getUnsigned(H.getSizeLowerBound())
.extOrTrunc(SizeTypeWidth);
- if (FormatSize > *SourceSize && *SourceSize != 0) {
+ if (FormatSize > *MaxOperationSize && *MaxOperationSize != 0) {
unsigned TruncationDiagID =
H.isKernelCompatible() ? diag::warn_format_truncation
: diag::warn_format_truncation_non_kprintf;
SmallString<16> SpecifiedSizeStr;
SmallString<16> FormatSizeStr;
- SourceSize->toString(SpecifiedSizeStr, /*Radix=*/10);
+ MaxOperationSize->toString(SpecifiedSizeStr, /*Radix=*/10);
FormatSize.toString(FormatSizeStr, /*Radix=*/10);
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(TruncationDiagID)
@@ -1472,22 +1547,57 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
const Expr *Dest = TheCall->getArg(0)->IgnoreCasts();
IdentifierInfo *FnInfo = FD->getIdentifier();
CheckSizeofMemaccessArgument(LenArg, Dest, FnInfo);
+ break;
}
}
- if (!SourceSize || !DestinationSize ||
- llvm::APSInt::compareValues(*SourceSize, *DestinationSize) <= 0)
- return;
-
std::string FunctionName = GetFunctionName();
+ SmallString<16> MaxOpStr;
+ SmallString<16> MinOpStr;
+
+ if (MinOperationSize)
+ MinOperationSize->toString(MinOpStr, /*Radix=*/10);
+ if (MaxOperationSize)
+ MaxOperationSize->toString(MaxOpStr, /*Radix=*/10);
+
+ if (DestinationSize) {
+ SmallString<16> DestinationStr;
+ DestinationSize->toString(DestinationStr, /*Radix=*/10);
+ // Check for definite overflow
+ if (MinOperationSize &&
+ llvm::APSInt::compareValues(*MinOperationSize, *DestinationSize) > 0) {
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(DiagOverflowID)
+ << FunctionName << DestinationStr << MinOpStr);
+ }
+ // Check for possible overflow
+ else if (MaxOperationSize && llvm::APSInt::compareValues(
+ *MaxOperationSize, *DestinationSize) > 0) {
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(DiagMayOverflowID)
+ << FunctionName << DestinationStr << MaxOpStr);
+ }
+ }
+
+ if (SourceSize) {
+ SmallString<16> SourceStr;
+ SourceSize->toString(SourceStr, /*Radix=*/10);
+ // Check for definite over-read
+ if (MinOperationSize &&
+ llvm::APSInt::compareValues(*MinOperationSize, *SourceSize) > 0) {
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(DiagOverReadID)
+ << FunctionName << SourceStr << MinOpStr);
- SmallString<16> DestinationStr;
- SmallString<16> SourceStr;
- DestinationSize->toString(DestinationStr, /*Radix=*/10);
- SourceSize->toString(SourceStr, /*Radix=*/10);
- DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
- PDiag(DiagID)
- << FunctionName << DestinationStr << SourceStr);
+ }
+ // Check for possible over-read
+ else if (MaxOperationSize &&
+ llvm::APSInt::compareValues(*MaxOperationSize, *SourceSize) > 0) {
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(DiagMayOverReadID)
+ << FunctionName << SourceStr << MaxOpStr);
+ }
+ }
}
static bool BuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall,
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 8a3ee4443eb16..b579255892084 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -2073,12 +2073,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
std::optional<QualType> Ssize_tTy = lookupTy("ssize_t");
std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
+ auto ValidSsize_tSize = [&](ArgNo ArgN) {
+ return ArgumentCondition(ArgN, WithinRange, Range(0, Ssize_tMax),
+ "a value not greater than SSIZE_MAX");
+ };
auto ReadSummary =
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))},
- ErrnoIrrelevant);
+ ErrnoIrrelevant)
+ .ArgConstraint(ValidSsize_tSize(ArgNo(2)));
// FIXME these are actually defined by POSIX and not by the C standard, we
// should handle them together with the rest of the POSIX functions.
@@ -3004,6 +3009,35 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ // ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
+ addToFunctionSummaryMap(
+ "pread",
+ Signature(ArgTypes{IntTy, VoidPtrTy, SizeTyCanonTy, Off_tTy},
+ RetType{Ssize_tTy}),
+ ReadSummary);
+
+ // ssize_t pread64(int fildes, void *buf, size_t nbyte, off64_t offset);
+ addToFunctionSummaryMap(
+ "pread64",
+ Signature(ArgTypes{IntTy, VoidPtrTy, SizeTyCanonTy, Off64_tTy},
+ RetType{Ssize_tTy}),
+ ReadSummary);
+
+ // ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);
+ addToFunctionSummaryMap(
+ "pwrite"...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/196499
More information about the libcxx-commits
mailing list