[libcxx-commits] [clang] [compiler-rt] [libcxx] [clang][Sema] Add fortify warnings for `unistd.h` (PR #161737)
Colin Kinloch via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Dec 18 12:41:54 PST 2025
https://github.com/ColinKinloch updated https://github.com/llvm/llvm-project/pull/161737
>From bc2db14b5aed7a943f2184495c15b8adb7eabfbe Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 2 Oct 2025 22:01:40 +0100
Subject: [PATCH 1/8] [clang][Sema] Add fortify warnings for `unistd.h`
Define as builtin and check for overflows and over-reads in:
* `read`
* `pread`/`pread64`
* `write`
* `pwrite`/`pwrite64`
* `getcwd`
* `readlink`
* `readlinkat`
It also enables `off_t`, `off64_t` and `ssize_t` for use in builtin
signatures.
Signed-off-by: Colin Kinloch <colin.kinloch at collabora.com>
---
clang/include/clang/Basic/Builtins.td | 65 +++++++++++++++++
.../clang/Basic/DiagnosticSemaKinds.td | 4 ++
clang/lib/AST/ASTContext.cpp | 9 ++-
clang/lib/Sema/SemaChecking.cpp | 70 ++++++++++++++++++-
clang/test/Sema/warn-fortify-source.c | 55 +++++++++++++++
clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 3 +
6 files changed, 201 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index aab2418511399..a1e4e2a203fae 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3515,6 +3515,8 @@ def StrnCaseCmp : GNULibBuiltin<"strings.h"> {
let RequiresUndef = 1;
}
+// POSIX unistd.h
+
def GNU_Exit : GNULibBuiltin<"unistd.h"> {
let Spellings = ["_exit"];
let Attributes = [NoReturn];
@@ -3527,6 +3529,69 @@ 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 PRead : LibBuiltin<"unistd.h"> {
+ let Spellings = ["pread"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void*, size_t, off_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+def PRead64 : LibBuiltin<"unistd.h"> {
+ let Spellings = ["pread64"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void*, size_t, off64_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;
+}
+
+def PWrite : LibBuiltin<"unistd.h"> {
+ let Spellings = ["pwrite"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void const*, size_t, off_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+def PWrite64 : LibBuiltin<"unistd.h"> {
+ let Spellings = ["pwrite64"];
+ let Attributes = [NoThrow];
+ let Prototype = "ssize_t(int, void const*, size_t, off64_t)";
+ let AddBuiltinPrefixedAlias = 1;
+}
+
+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 a8fd17ecd2bdd..9b3cbbcf6fcb9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -953,6 +953,10 @@ def warn_fortify_source_overflow
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 %2,"
+ " but size argument is %1">,
+ 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 f52470a4d7458..afe30adefe555 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12484,9 +12484,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 2ba5dc37eb8a4..ff0827e757bef 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1244,6 +1244,14 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
std::optional<llvm::APSInt> DestinationSize;
unsigned DiagID = 0;
bool IsChkVariant = false;
+ bool IsTriggered = false;
+
+ auto CompareSizeSourceToDest = [&]() {
+ return SourceSize && DestinationSize
+ ? std::optional<int>{llvm::APSInt::compareValues(
+ *SourceSize, *DestinationSize)}
+ : std::nullopt;
+ };
auto GetFunctionName = [&]() {
std::string FunctionNameStr =
@@ -1273,6 +1281,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_strlen_overflow;
SourceSize = ComputeStrLenArgument(1);
DestinationSize = ComputeSizeArgument(0);
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1283,6 +1292,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
SourceSize = ComputeStrLenArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1353,11 +1363,13 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
} else {
DestinationSize = ComputeSizeArgument(0);
}
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
}
return;
}
+
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BI__builtin___memset_chk:
@@ -1373,6 +1385,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
IsChkVariant = true;
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1382,6 +1395,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
SourceSize = ComputeExplicitObjectSizeArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(3);
IsChkVariant = true;
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1399,6 +1413,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
+ IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1413,8 +1428,58 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_source_overflow;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
+ IsTriggered = CompareSizeSourceToDest() > 0;
+ 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: {
+ DiagID = diag::warn_fortify_source_size_mismatch;
+ SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
+ IsTriggered = CompareSizeSourceToDest() > 0;
+ break;
+ }
+
+ case Builtin::BIpread:
+ case Builtin::BI__builtin_pread:
+ case Builtin::BIpread64:
+ case Builtin::BI__builtin_pread64: {
+ DiagID = diag::warn_fortify_source_size_mismatch;
+ SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
+ DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
+ IsTriggered = CompareSizeSourceToDest() > 0;
+ break;
+ }
+
+ case Builtin::BIwrite:
+ case Builtin::BI__builtin_write: {
+ DiagID = diag::warn_fortify_destination_size_mismatch;
+ SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
+ DestinationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ IsTriggered = CompareSizeSourceToDest() < 0;
break;
}
+
+ case Builtin::BIpwrite:
+ case Builtin::BI__builtin_pwrite:
+ case Builtin::BIpwrite64:
+ case Builtin::BI__builtin_pwrite64: {
+ DiagID = diag::warn_fortify_destination_size_mismatch;
+ SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
+ DestinationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
+ IsTriggered = CompareSizeSourceToDest() < 0;
+ break;
+ }
+
case Builtin::BIsnprintf:
case Builtin::BI__builtin_snprintf:
case Builtin::BIvsnprintf:
@@ -1450,11 +1515,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
+ IsTriggered = CompareSizeSourceToDest() > 0;
+ break;
}
}
- if (!SourceSize || !DestinationSize ||
- llvm::APSInt::compareValues(*SourceSize, *DestinationSize) <= 0)
+ if (!IsTriggered)
return;
std::string FunctionName = GetFunctionName();
diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c
index 750bd5361ade9..63aec62600302 100644
--- a/clang/test/Sema/warn-fortify-source.c
+++ b/clang/test/Sema/warn-fortify-source.c
@@ -104,6 +104,61 @@ void call_memset(void) {
__builtin_memset(buf, 0xff, 11); // expected-warning {{'memset' will always overflow; destination buffer has size 10, but size argument is 11}}
}
+void call_read(void) {
+ char buf[10];
+ __builtin_read(0, buf, 10);
+ __builtin_read(0, buf, 20); // expected-warning {{'read' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+void call_pread(void) {
+ char buf[10];
+ __builtin_pread(0, buf, 10, 0);
+ __builtin_pread(0, buf, 20, 0); // expected-warning {{'pread' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+void call_pread64(void) {
+ char buf[10];
+ __builtin_pread64(0, buf, 10, 0);
+ __builtin_pread64(0, buf, 20, 0); // expected-warning {{'pread64' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+void call_write(void) {
+ char buf[10];
+ __builtin_write(0, buf, 10);
+ __builtin_write(0, buf, 20); // expected-warning {{'write' size argument is too large; source buffer has size 10, but size argument is 20}}
+}
+
+void call_pwrite(void) {
+ char buf[10];
+ __builtin_pwrite(0, buf, 10, 0);
+ __builtin_pwrite(0, buf, 20, 0); // expected-warning {{'pwrite' size argument is too large; source buffer has size 10, but size argument is 20}}
+}
+
+void call_pwrite64(void) {
+ char buf[10];
+ __builtin_pwrite64(0, buf, 10, 0);
+ __builtin_pwrite64(0, buf, 20, 0); // expected-warning {{'pwrite64' size argument is too large; source buffer has size 10, but size argument is 20}}
+}
+
+void call_getcwd(void) {
+ char buf[10];
+ __builtin_getcwd(buf, 10);
+ __builtin_getcwd(buf, 20); // expected-warning {{'getcwd' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+void call_readlink(void) {
+ char buf[10];
+ __builtin_readlink("path", buf, 10);
+ __builtin_readlink("path", buf, 20); // expected-warning {{'readlink' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+void call_readlinkat(void) {
+ char buf[10];
+ __builtin_readlinkat(0, "path", buf, 10);
+ __builtin_readlinkat(0, "path", buf, 20); // expected-warning {{'readlinkat' size argument is too large; destination buffer has size 10, but size argument is 20}}
+}
+
+
void call_snprintf(double d, int n) {
char buf[10];
__builtin_snprintf(buf, 10, "merp");
diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
index 9b6dd37c7a4a9..a33c746da82ef 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -348,12 +348,15 @@ class PrototypeParser {
.Case("msint32_t", "Ni")
.Case("msuint32_t", "UNi")
.Case("objc_super", "M")
+ .Case("off_t", "Li")
+ .Case("off64_t", "Wi")
.Case("pid_t", "p")
.Case("ptrdiff_t", "Y")
.Case("SEL", "H")
.Case("short", "s")
.Case("sigjmp_buf", "SJ")
.Case("size_t", "z")
+ .Case("ssize_t", "Sz")
.Case("ucontext_t", "K")
.Case("uint32_t", "UZi")
.Case("uint64_t", "UWi")
>From 9a9759d8b6cebd961511f1329672d084fead6325 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Mon, 3 Nov 2025 02:53:09 +0000
Subject: [PATCH 2/8] [clang][Sema] Add min/max operation size for fortify
checks
---
.../clang/Basic/DiagnosticSemaKinds.td | 8 +-
clang/lib/Sema/SemaChecking.cpp | 162 +++++++++++-------
clang/test/Sema/builtin-memcpy.c | 3 +-
clang/test/Sema/warn-fortify-source.c | 39 ++++-
4 files changed, 140 insertions(+), 72 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9b3cbbcf6fcb9..339bf95358f4a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -950,12 +950,16 @@ 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 %2,"
- " but size argument is %1">,
+ : 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<
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index ff0827e757bef..096f4d971592c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1240,18 +1240,20 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
return llvm::APSInt::getUnsigned(Result + 1).extOrTrunc(SizeTypeWidth);
};
+ // 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 definate 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;
- bool IsTriggered = false;
-
- auto CompareSizeSourceToDest = [&]() {
- return SourceSize && DestinationSize
- ? std::optional<int>{llvm::APSInt::compareValues(
- *SourceSize, *DestinationSize)}
- : std::nullopt;
- };
auto GetFunctionName = [&]() {
std::string FunctionNameStr =
@@ -1278,21 +1280,19 @@ 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);
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
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;
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1316,12 +1316,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) {
@@ -1352,18 +1352,17 @@ 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;
} else {
DestinationSize = ComputeSizeArgument(0);
}
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
}
@@ -1380,22 +1379,21 @@ 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;
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
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;
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1410,10 +1408,17 @@ 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: {
+ MinOperationSize = MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
@@ -1421,14 +1426,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
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);
- IsTriggered = CompareSizeSourceToDest() > 0;
+ SourceSize = ComputeSizeArgument(1);
break;
}
@@ -1440,10 +1443,9 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin_readlinkat:
case Builtin::BIgetcwd:
case Builtin::BI__builtin_getcwd: {
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
- IsTriggered = CompareSizeSourceToDest() > 0;
+ MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
break;
}
@@ -1451,20 +1453,17 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin_pread:
case Builtin::BIpread64:
case Builtin::BI__builtin_pread64: {
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
- IsTriggered = CompareSizeSourceToDest() > 0;
+ MaxOperationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
break;
}
case Builtin::BIwrite:
case Builtin::BI__builtin_write: {
- DiagID = diag::warn_fortify_destination_size_mismatch;
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
- DestinationSize =
+ MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
- IsTriggered = CompareSizeSourceToDest() < 0;
break;
}
@@ -1472,11 +1471,9 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin_pwrite:
case Builtin::BIpwrite64:
case Builtin::BI__builtin_pwrite64: {
- DiagID = diag::warn_fortify_destination_size_mismatch;
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
- DestinationSize =
+ MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
- IsTriggered = CompareSizeSourceToDest() < 0;
break;
}
@@ -1484,12 +1481,11 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
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();
@@ -1499,13 +1495,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)
@@ -1515,23 +1511,57 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
- IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
}
- if (!IsTriggered)
- 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 definate 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 definate 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/test/Sema/builtin-memcpy.c b/clang/test/Sema/builtin-memcpy.c
index 2a55e78034a02..af7f2034f3c30 100644
--- a/clang/test/Sema/builtin-memcpy.c
+++ b/clang/test/Sema/builtin-memcpy.c
@@ -7,7 +7,8 @@
/// Zero-sized structs should not crash.
int b() {
struct { } a[10];
- __builtin_memcpy(&a[2], a, 2); // c-warning {{buffer has size 0, but size argument is 2}}
+ __builtin_memcpy(&a[2], a, 2); // c-warning {{buffer has size 0, but size argument is 2}} \
+ // c-warning {{buffer has size 0, but size argument is 2}}
return 0;
}
diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c
index 63aec62600302..eaf43627c2a26 100644
--- a/clang/test/Sema/warn-fortify-source.c
+++ b/clang/test/Sema/warn-fortify-source.c
@@ -30,6 +30,8 @@ void call_memcpy(void) {
char dst[10];
char src[20];
memcpy(dst, src, 20); // expected-warning {{memcpy' will always overflow; destination buffer has size 10, but size argument is 20}}
+ memcpy(dst, src, 21); // expected-warning {{memcpy' will always overflow; destination buffer has size 10, but size argument is 21}} \
+ // expected-warning {{memcpy' will always over-read; source buffer has size 20, but size argument is 21}}
if (sizeof(dst) == sizeof(src))
memcpy(dst, src, 20); // no warning, unreachable
@@ -43,18 +45,29 @@ void call_memcpy_type(void) {
struct pair p;
char buf[20];
memcpy(&p.first, buf, 20); // expected-warning {{memcpy' will always overflow; destination buffer has size 8, but size argument is 20}}
+ memcpy(&p.first, buf, 21); // expected-warning {{memcpy' will always overflow; destination buffer has size 8, but size argument is 21}} \
+ // expected-warning {{memcpy' will always over-read; source buffer has size 20, but size argument is 21}}
+}
+
+void call_memcpy_chk(void) {
+ char dst[10];
+ char src[10];
+ __builtin___memcpy_chk(dst, src, 10, 10);
+ __builtin___memcpy_chk(dst, src, 10, 9); // expected-warning {{memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
}
void call_strncat(void) {
char s1[10], s2[20];
__builtin_strncat(s2, s1, 20);
__builtin_strncat(s1, s2, 20); // expected-warning {{'strncat' size argument is too large; destination buffer has size 10, but size argument is 20}}
+ __builtin_strncat(s1, "abcd", 20); // expected-warning {{'strncat' size argument is too large; destination buffer has size 10, but size argument is 20}}
}
void call_strncpy(void) {
char s1[10], s2[20];
__builtin_strncpy(s2, s1, 20);
__builtin_strncpy(s1, s2, 20); // expected-warning {{'strncpy' size argument is too large; destination buffer has size 10, but size argument is 20}}
+ __builtin_strncpy(s1, "abcd", 20); // expected-warning {{'strncpy' size argument is too large; destination buffer has size 10, but size argument is 20}}
}
void call_stpncpy(void) {
@@ -92,9 +105,17 @@ void call_stpcpy(void) {
__builtin_stpcpy(dst2, src); // expected-warning {{'stpcpy' will always overflow; destination buffer has size 4, but the source string has length 5 (including NUL byte)}}
}
+void call_stpcpy_chk(void) {
+ const char *const src = "abcd";
+ char dst1[5];
+ __builtin___stpcpy_chk(dst1, src, 5);
+ __builtin___stpcpy_chk(dst1, src, 4); // expected-warning {{'stpcpy' will always overflow; destination buffer has size 4, but the source string has length 5 (including NUL byte)}}
+}
+
void call_memmove(void) {
char s1[10], s2[20];
- __builtin_memmove(s2, s1, 20);
+ __builtin_memmove(s2, s1, 10);
+ __builtin_memmove(s2, s1, 20); // expected-warning {{'memmove' will always over-read; source buffer has size 10, but size argument is 20}}
__builtin_memmove(s1, s2, 20); // expected-warning {{'memmove' will always overflow; destination buffer has size 10, but size argument is 20}}
}
@@ -298,11 +319,23 @@ template <int A, int B>
void call_memcpy_dep() {
char bufferA[A];
char bufferB[B];
- memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
+ if (sizeof(bufferA) < 10 && sizeof(bufferB) < 10) {
+ memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}} \
+ // expected-warning{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
+ } else if (sizeof(bufferA) < 10) {
+ memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
+ } else if (sizeof(bufferB) < 10) {
+ memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
+ } else {
+ memcpy(bufferA, bufferB, 10);
+ }
+
}
void call_call_memcpy() {
- call_memcpy_dep<10, 9>();
+ call_memcpy_dep<10, 10>();
+ call_memcpy_dep<10, 9>(); // expected-note {{in instantiation of function template specialization 'call_memcpy_dep<10, 9>' requested here}}
call_memcpy_dep<9, 10>(); // expected-note {{in instantiation of function template specialization 'call_memcpy_dep<9, 10>' requested here}}
+ call_memcpy_dep<9, 9>(); // expected-note {{in instantiation of function template specialization 'call_memcpy_dep<9, 9>' requested here}}
}
#endif
>From 1ad571473824f58633ca4b6a9493a367de0feda2 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Mon, 3 Nov 2025 20:46:15 +0000
Subject: [PATCH 3/8] [clang][Sema] Avoid builtin redecleration for asm-label
test
As `readlink` is now defined as bultin it throws an error when
redeclared with a different type: "incompatible redeclaration of library
function function 'readlink'"
As `sync` has the type `void(void)` it
this issue should not happen in the future.
---
clang/test/Sema/asm-label.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/test/Sema/asm-label.c b/clang/test/Sema/asm-label.c
index eb0259863b2b2..a8b6afc252c2d 100644
--- a/clang/test/Sema/asm-label.c
+++ b/clang/test/Sema/asm-label.c
@@ -25,6 +25,6 @@ int z __asm__("zooms"); // expected-error{{conflicting asm label}}
// No diagnostics on the following.
-void __real_readlink(void) __asm("readlink");
-void readlink(void) __asm("__protected_readlink");
-void readlink(void) { __real_readlink(); }
+void __real_sync(void) __asm("sync");
+void sync(void) __asm("__protected_sync");
+void sync(void) { __real_sync(); }
>From 31e5e73d490f2788e0ea7c613229d6a230b88b96 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 6 Nov 2025 00:00:53 +0000
Subject: [PATCH 4/8] [clang][Sema] Use relative line number for template
warnings
---
clang/test/Sema/warn-fortify-source.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c
index eaf43627c2a26..a0aff478474fb 100644
--- a/clang/test/Sema/warn-fortify-source.c
+++ b/clang/test/Sema/warn-fortify-source.c
@@ -319,17 +319,15 @@ template <int A, int B>
void call_memcpy_dep() {
char bufferA[A];
char bufferB[B];
+ memcpy(bufferA, bufferB, 10);
if (sizeof(bufferA) < 10 && sizeof(bufferB) < 10) {
- memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}} \
- // expected-warning{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
+ // expected-warning at -2{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
+ // expected-warning at -3{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
} else if (sizeof(bufferA) < 10) {
- memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
+ // expected-warning at -5{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}}
} else if (sizeof(bufferB) < 10) {
- memcpy(bufferA, bufferB, 10); // expected-warning{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
- } else {
- memcpy(bufferA, bufferB, 10);
+ // expected-warning at -7{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}}
}
-
}
void call_call_memcpy() {
>From fcca3caf07a2256d146c95e93fc3632e4615586c Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 6 Nov 2025 01:58:37 +0000
Subject: [PATCH 5/8] [clang][test] Add over-read warnings to analysis tests
---
clang/test/Analysis/bstring.c | 15 +++++++++++++++
clang/test/Analysis/malloc.c | 2 +-
clang/test/Analysis/pr22954.c | 3 ++-
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/clang/test/Analysis/bstring.c b/clang/test/Analysis/bstring.c
index f015e0b5d9fb7..5ccecf805e5bb 100644
--- a/clang/test/Analysis/bstring.c
+++ b/clang/test/Analysis/bstring.c
@@ -93,6 +93,9 @@ void memcpy1 (void) {
char dst[10];
memcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+#ifndef VARIANT
+ // expected-warning at -2{{memcpy' will always over-read; source buffer has size 4, but size argument is 5}}
+#endif
}
void memcpy2 (void) {
@@ -117,6 +120,9 @@ void memcpy4 (void) {
char dst[10];
memcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+#ifndef VARIANT
+ // expected-warning at -2{{memcpy' will always over-read; source buffer has size 2, but size argument is 3}}
+#endif
}
void memcpy5(void) {
@@ -219,6 +225,9 @@ void mempcpy1 (void) {
char dst[10];
mempcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+#ifndef VARIANT
+ // expected-warning at -2{{mempcpy' will always over-read; source buffer has size 4, but size argument is 5}}
+#endif
}
void mempcpy2 (void) {
@@ -243,6 +252,9 @@ void mempcpy4 (void) {
char dst[10];
mempcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+#ifndef VARIANT
+ // expected-warning at -2{{mempcpy' will always over-read; source buffer has size 2, but size argument is 3}}
+#endif
}
void mempcpy5(void) {
@@ -384,6 +396,9 @@ void memmove1 (void) {
char dst[10];
memmove(dst, src, 5); // expected-warning{{out-of-bound}}
+#ifndef VARIANT
+ // expected-warning at -2{{memmove' will always over-read; source buffer has size 4, but size argument is 5}}
+#endif
}
void memmove2 (void) {
diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c
index 92b47bc3b5e9a..1286320bd4e2d 100644
--- a/clang/test/Analysis/malloc.c
+++ b/clang/test/Analysis/malloc.c
@@ -879,7 +879,7 @@ void doNotInvalidateWhenPassedToSystemCalls(char *s) {
// Treat source buffer contents as escaped.
void escapeSourceContents(char *s) {
char *p = malloc(12);
- memcpy(s, &p, 12); // no warning
+ memcpy(s, &p, 12); // expected-warning{{memcpy' will always over-read; source buffer has size}}
void *p1 = malloc(7);
char *a;
diff --git a/clang/test/Analysis/pr22954.c b/clang/test/Analysis/pr22954.c
index b5f8aeb2a5ca6..bfb4ba63eb4fe 100644
--- a/clang/test/Analysis/pr22954.c
+++ b/clang/test/Analysis/pr22954.c
@@ -536,7 +536,8 @@ int f262(void) {
struct aa a262 = {{1, 2, 3, 4}, 0};
a262.s2 = strdup("hello");
char input[] = {'a', 'b', 'c', 'd'};
- memcpy(a262.s1, input, -1); // expected-warning{{'memcpy' will always overflow; destination buffer has size 16, but size argument is 18446744073709551615}}
+ memcpy(a262.s1, input, -1); // expected-warning{{'memcpy' will always overflow; destination buffer has size 16, but size argument is 18446744073709551615}} \
+ // expected-warning{{'memcpy' will always over-read; source buffer has size 4, but size argument is 18446744073709551615}}
clang_analyzer_eval(a262.s1[0] == 1); // expected-warning{{UNKNOWN}}\
expected-warning{{Potential leak of memory pointed to by 'a262.s2'}}
clang_analyzer_eval(a262.s1[1] == 1); // expected-warning{{UNKNOWN}}
>From 87bf3532986734656dbaf90df29d6b9432d6bf1b Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 6 Nov 2025 01:59:31 +0000
Subject: [PATCH 6/8] [clang][test] Use __signed_size_t for function summaries
---
clang/test/Analysis/std-c-library-functions-POSIX.c | 4 ++--
clang/test/Analysis/std-c-library-functions.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c
index f6d88e6c1502d..462bbf5d8e5de 100644
--- a/clang/test/Analysis/std-c-library-functions-POSIX.c
+++ b/clang/test/Analysis/std-c-library-functions-POSIX.c
@@ -98,8 +98,8 @@
// CHECK: Loaded summary for: void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset)
// CHECK: Loaded summary for: int pipe(int fildes[2])
// CHECK: Loaded summary for: off_t lseek(int fildes, off_t offset, int whence)
-// CHECK: Loaded summary for: ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
-// CHECK: Loaded summary for: ssize_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize)
+// CHECK: Loaded summary for: __signed_size_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
+// CHECK: Loaded summary for: __signed_size_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize)
// CHECK: Loaded summary for: int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
// CHECK: Loaded summary for: char *realpath(const char *restrict file_name, char *restrict resolved_name)
// CHECK: Loaded summary for: int execv(const char *path, char *const argv[])
diff --git a/clang/test/Analysis/std-c-library-functions.c b/clang/test/Analysis/std-c-library-functions.c
index b5f663493a676..45ca1c69f89e3 100644
--- a/clang/test/Analysis/std-c-library-functions.c
+++ b/clang/test/Analysis/std-c-library-functions.c
@@ -61,8 +61,8 @@
// CHECK-NEXT: Loaded summary for: int getchar(void)
// CHECK-NEXT: Loaded summary for: __size_t fread(void *restrict, size_t, size_t, FILE *restrict)
// CHECK-NEXT: Loaded summary for: __size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict)
-// CHECK-NEXT: Loaded summary for: ssize_t read(int, void *, size_t)
-// CHECK-NEXT: Loaded summary for: ssize_t write(int, const void *, size_t)
+// CHECK-NEXT: Loaded summary for: __signed_size_t read(int, void *, size_t)
+// CHECK-NEXT: Loaded summary for: __signed_size_t write(int, const void *, size_t)
// CHECK-NEXT: Loaded summary for: ssize_t getline(char **restrict, size_t *restrict, FILE *restrict)
// CHECK-NEXT: Loaded summary for: ssize_t getdelim(char **restrict, size_t *restrict, int, FILE *restrict)
// CHECK-NEXT: Loaded summary for: char *getenv(const char *)
>From f202bff4eabc92c3421388a7e694abc8f984ecb0 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 6 Nov 2025 02:02:59 +0000
Subject: [PATCH 7/8] [clang][test] Rename function to avoid redeclaration
---
clang/test/Analysis/array-struct.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/Analysis/array-struct.c b/clang/test/Analysis/array-struct.c
index f0eba86fe71bf..691bb1348a44f 100644
--- a/clang/test/Analysis/array-struct.c
+++ b/clang/test/Analysis/array-struct.c
@@ -175,12 +175,12 @@ void f17(void) {
x = 1;
}
-void read(char*);
+void readp(char*);
void f18(void) {
char *q;
char *p = (char *) __builtin_alloca(10);
- read(p);
+ readp(p);
q = p;
q++;
if (*q) { // no-warning
>From e1381ee3e10fd5ddc49c70d4612a3a635d87fee7 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch at collabora.com>
Date: Thu, 18 Dec 2025 20:34:38 +0000
Subject: [PATCH 8/8] [clang][test] Fix tests for new over-read warning
This fixes a char literal passed as a wchar_t to __constexpr_wmemchr
that triggers an over-read check when it's copied using memcpy.
It also disables fortify checks for an unrelated test.
---
compiler-rt/test/asan/TestCases/Windows/issue64990.cpp | 2 +-
.../libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/test/asan/TestCases/Windows/issue64990.cpp b/compiler-rt/test/asan/TestCases/Windows/issue64990.cpp
index 5222ec6e08191..785e027a31be8 100644
--- a/compiler-rt/test/asan/TestCases/Windows/issue64990.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/issue64990.cpp
@@ -1,5 +1,5 @@
// Repro for the issue #64990: Asan with Windows EH generates __asan_xxx runtime calls without required funclet tokens
-// RUN: %clang_cl_asan %Od %if MSVC %{ /Oi %} %s -EHsc %Fe%t
+// RUN: %clang_cl_asan %Od %if MSVC %{ /Oi %} %else %{ -Wno-fortify-source %} %s -EHsc %Fe%t
// RUN: not %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: target={{.*-windows-gnu}}
diff --git a/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp
index 02feed064eacc..7d9548e500da8 100644
--- a/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp
+++ b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp
@@ -21,6 +21,6 @@ static_assert(std::__constexpr_wmemcmp(L"Banane", L"Bananf", 6) == -1, "");
constexpr bool test_constexpr_wmemchr() {
const wchar_t str[] = L"Banane";
- return std::__constexpr_wmemchr(str, 'n', 6) == str + 2;
+ return std::__constexpr_wmemchr(str, L'n', 6) == str + 2;
}
static_assert(test_constexpr_wmemchr(), "");
More information about the libcxx-commits
mailing list