https://github.com/ColinKinloch updated https://github.com/llvm/llvm-project/pull/161737
>From 98912f07d2b6f3a1d8c79d2e38407dc1d9ba2905 Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Thu, 2 Oct 2025 22:01:40 +0100 Subject: [PATCH 1/7] [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 <[email protected]> --- 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 468121f7d20ab..3c3a5a82a28a6 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -3490,6 +3490,8 @@ def StrnCaseCmp : GNULibBuiltin<"strings.h"> { let RequiresUndef = 1; } +// POSIX unistd.h + def GNU_Exit : GNULibBuiltin<"unistd.h"> { let Spellings = ["_exit"]; let Attributes = [NoReturn]; @@ -3502,6 +3504,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 b157cbb0b8069..f2206ad08414e 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 056bfe36b2a0a..d0432de14dbb3 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12528,9 +12528,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 7b37e0b8d5430..c7d6d0c142eee 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1243,6 +1243,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 = @@ -1270,6 +1278,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, DiagID = diag::warn_fortify_strlen_overflow; SourceSize = ComputeStrLenArgument(1); DestinationSize = ComputeSizeArgument(0); + IsTriggered = CompareSizeSourceToDest() > 0; break; } @@ -1279,6 +1288,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, SourceSize = ComputeStrLenArgument(1); DestinationSize = ComputeExplicitObjectSizeArgument(2); IsChkVariant = true; + IsTriggered = CompareSizeSourceToDest() > 0; break; } @@ -1349,11 +1359,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: @@ -1369,6 +1381,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, DestinationSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1); IsChkVariant = true; + IsTriggered = CompareSizeSourceToDest() > 0; break; } @@ -1378,6 +1391,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, SourceSize = ComputeExplicitObjectSizeArgument(1); DestinationSize = ComputeExplicitObjectSizeArgument(3); IsChkVariant = true; + IsTriggered = CompareSizeSourceToDest() > 0; break; } @@ -1395,6 +1409,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; } @@ -1409,8 +1424,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: @@ -1446,11 +1511,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 216878c0836d8..5d4a8d81b0b3e 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -96,6 +96,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 352765acf5338..30c7afd95a89a 100644 --- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp +++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp @@ -347,12 +347,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 496eec840f8f0903da2b6cbd440631962ebad5e9 Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Mon, 3 Nov 2025 02:53:09 +0000 Subject: [PATCH 2/7] [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 f2206ad08414e..484f161194199 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 c7d6d0c142eee..3889a07d5f962 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1239,18 +1239,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 = @@ -1275,20 +1277,18 @@ 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___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; } @@ -1312,12 +1312,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) { @@ -1348,18 +1348,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; } } @@ -1376,22 +1375,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; } @@ -1406,10 +1404,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; } @@ -1417,14 +1422,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; } @@ -1436,10 +1439,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; } @@ -1447,20 +1449,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; } @@ -1468,11 +1467,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; } @@ -1480,12 +1477,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(); @@ -1495,13 +1491,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) @@ -1511,23 +1507,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 5d4a8d81b0b3e..62068361b92f9 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) { @@ -84,9 +97,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}} } @@ -290,11 +311,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 fcd697de00bb6bd0db9da2645c470b09c67c702f Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Mon, 3 Nov 2025 20:46:15 +0000 Subject: [PATCH 3/7] [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 af4cb4d73a977ab4546de2c4b863802d81c8bd40 Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Thu, 6 Nov 2025 00:00:53 +0000 Subject: [PATCH 4/7] [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 62068361b92f9..f0e9b2fc9c1db 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -311,17 +311,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@-2{{'memcpy' will always overflow; destination buffer has size 9, but size argument is 10}} + // expected-warning@-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@-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@-7{{'memcpy' will always over-read; source buffer has size 9, but size argument is 10}} } - } void call_call_memcpy() { >From 85c6e482ed41bd907f8d481d9263da447510c11b Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Thu, 6 Nov 2025 01:58:37 +0000 Subject: [PATCH 5/7] [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@-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@-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@-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@-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@-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 86f4dba491bbc4c59c87c6c33783257963d6d5e1 Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Thu, 6 Nov 2025 01:59:31 +0000 Subject: [PATCH 6/7] [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 f74be7d3255bbac7b0d5aba61f3f97634327f375 Mon Sep 17 00:00:00 2001 From: Colin Kinloch <[email protected]> Date: Thu, 6 Nov 2025 02:02:59 +0000 Subject: [PATCH 7/7] [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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
