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
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to