https://github.com/venk-ks updated https://github.com/llvm/llvm-project/pull/230695
>From a151873c7c840653a9a8da700a6f11b4aef9e493 Mon Sep 17 00:00:00 2001 From: Venkatesh Srinivasan <[email protected]> Date: Fri, 9 Oct 2026 22:39:08 +0000 Subject: [PATCH 1/2] [Clang][Sema] Add fortify warnings for realpath Teach -Wfortify-source to diagnose when realpath is called with a non-null destination buffer whose statically known size is smaller than PATH_MAX bytes. Part of #142230 Assisted-by: Gemini --- clang/docs/ReleaseNotes.md | 3 ++ clang/include/clang/Basic/Builtins.td | 6 +++ .../clang/Basic/DiagnosticSemaKinds.td | 5 ++ clang/lib/Sema/SemaChecking.cpp | 53 +++++++++++++++++++ clang/test/Sema/warn-fortify-source.c | 19 +++++++ 5 files changed, 86 insertions(+) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 96d5e630e80dac..3a8d6712cbc605 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -359,6 +359,9 @@ features cannot lower the translation-unit ABI level; - `-Wfortify-source` now diagnoses when `poll`, `ppoll`, or `ppoll64` is called with a descriptor count whose total size exceeds the `fds` array size. +- `-Wfortify-source` now diagnoses when `realpath` is called with a destination + buffer smaller than `PATH_MAX` bytes. + - The `cannot overload a member function` diagnostic now describes the previous declaration first, matching the order in which the declarations appear in the source. (#GH219803) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index cd92a36049e32d..cc42c155d4b9db 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -3297,6 +3297,12 @@ def AlignedAlloc : LibBuiltin<"stdlib.h"> { let Prototype = "void*(size_t, size_t)"; } +// POSIX stdlib.h +def Realpath : LibBuiltin<"stdlib.h"> { + let Spellings = ["realpath"]; + let Prototype = "char*(char const* restrict, char* restrict)"; +} + // C99 string.h def MemCpy : LibBuiltin<"string.h"> { let Spellings = ["memcpy"]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 70f25c78a6f4a9..dea096c64395fc 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1059,6 +1059,11 @@ def warn_fortify_source_overread : Warning< "size %1, but the size is %2">, InGroup<FortifySource>; +def warn_fortify_source_buffer_too_small : Warning< + "'%0' may overflow; destination buffer has size %1, but at least %2 bytes " + "are required">, + InGroup<FortifySource>; + def err_function_start_invalid_type: Error< "argument must be a function">; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 0531dfa877fbdf..5569401c025b41 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -56,6 +56,7 @@ #include "clang/Basic/SyncScope.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering. +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Ownership.h" @@ -1319,6 +1320,49 @@ class FortifiedBufferChecker { const DiagnoseAsBuiltinAttr *DABAttr; unsigned SizeTypeWidth; }; + +static std::optional<uint64_t> tryExpandMacroAsUInt(StringRef Macro, + const Preprocessor &PP) { + const IdentifierInfo *MacroII = PP.getIdentifierInfo(Macro); + if (!MacroII) + return std::nullopt; + const MacroInfo *MI = PP.getMacroInfo(MacroII); + if (!MI || MI->tokens_empty()) + return std::nullopt; + + ArrayRef<Token> Tokens = MI->tokens(); + while (Tokens.size() >= 2 && Tokens.front().is(tok::l_paren) && + Tokens.back().is(tok::r_paren)) + Tokens = Tokens.drop_front().drop_back(); + if (Tokens.size() != 1 || !Tokens.front().is(tok::numeric_constant)) + return std::nullopt; + + bool InvalidSpelling = false; + SmallString<32> Buffer; + StringRef ValueStr = PP.getSpelling(Tokens.front(), Buffer, &InvalidSpelling); + if (InvalidSpelling) + return std::nullopt; + + ValueStr = ValueStr.rtrim("uUlL"); + uint64_t Val; + if (ValueStr.getAsInteger(/*Radix=*/0, Val)) + return std::nullopt; + return Val; +} + +static std::optional<uint64_t> getPathMaxValue(const ASTContext &Ctx, + const Preprocessor &PP) { + if (std::optional<uint64_t> MacroVal = tryExpandMacroAsUInt("PATH_MAX", PP)) + return MacroVal; + + const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); + if (T.isOSLinux() || T.isOSFuchsia() || T.isOSAIX() || T.isOSHaiku()) + return 4096; + if (T.isOSDarwin() || T.isOSFreeBSD() || T.isOSNetBSD() || T.isOSOpenBSD() || + T.isOSSolaris()) + return 1024; + return std::nullopt; +} } // anonymous namespace void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, @@ -1601,6 +1645,15 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, BufferSize = Checker.ComputeSizeArgument(0); break; } + case Builtin::BIrealpath: { + std::optional<uint64_t> PathMax = getPathMaxValue(Context, PP); + if (!PathMax) + return; + DiagID = diag::warn_fortify_source_buffer_too_small; + AccessSize = llvm::APSInt::getUnsigned(*PathMax).extOrTrunc(SizeTypeWidth); + BufferSize = Checker.ComputeSizeArgument(1); + break; + } // memchr(buf, val, size) case Builtin::BImemchr: case Builtin::BI__builtin_memchr: { diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c index 73a10070008f44..0873432e8df581 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -49,6 +49,7 @@ void bzero(void *dst, size_t n); size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); char *fgets(char *s, int size, FILE *stream); +char *realpath(const char *path, char *resolved_path); #ifdef __cplusplus } @@ -173,6 +174,24 @@ void call_fread_fwrite_fgets(FILE *fp) { fgets(src, 0, fp); } +void call_realpath(const char *path, char *unknown_buf) { + char ok_buf[1024]; + char small_buf[1023]; + realpath(path, (char *)0); + realpath(path, unknown_buf); + realpath(path, ok_buf); + realpath(path, small_buf); // expected-warning {{'realpath' may overflow; destination buffer has size 1023, but at least 1024 bytes are required}} +} + +#define PATH_MAX (4096) +void call_realpath_macro(const char *path) { + char ok_buf[4096]; + char small_buf[4095]; + realpath(path, ok_buf); + realpath(path, small_buf); // expected-warning {{'realpath' may overflow; destination buffer has size 4095, but at least 4096 bytes are required}} +} +#undef PATH_MAX + void call_snprintf(double d, int n) { char buf[10]; __builtin_snprintf(buf, 10, "merp"); >From d15c0ee9143158d615890c5638b325dfd4d80000 Mon Sep 17 00:00:00 2001 From: Venkatesh Srinivasan <[email protected]> Date: Fri, 9 Oct 2026 22:46:21 +0000 Subject: [PATCH 2/2] Simplify realpath PATH_MAX lookup to use target triple directly --- clang/lib/Sema/SemaChecking.cpp | 60 ++++++--------------------- clang/test/Sema/warn-fortify-source.c | 9 ---- 2 files changed, 12 insertions(+), 57 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 5569401c025b41..ad84b5e84581e1 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -56,7 +56,6 @@ #include "clang/Basic/SyncScope.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering. -#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Ownership.h" @@ -1320,49 +1319,6 @@ class FortifiedBufferChecker { const DiagnoseAsBuiltinAttr *DABAttr; unsigned SizeTypeWidth; }; - -static std::optional<uint64_t> tryExpandMacroAsUInt(StringRef Macro, - const Preprocessor &PP) { - const IdentifierInfo *MacroII = PP.getIdentifierInfo(Macro); - if (!MacroII) - return std::nullopt; - const MacroInfo *MI = PP.getMacroInfo(MacroII); - if (!MI || MI->tokens_empty()) - return std::nullopt; - - ArrayRef<Token> Tokens = MI->tokens(); - while (Tokens.size() >= 2 && Tokens.front().is(tok::l_paren) && - Tokens.back().is(tok::r_paren)) - Tokens = Tokens.drop_front().drop_back(); - if (Tokens.size() != 1 || !Tokens.front().is(tok::numeric_constant)) - return std::nullopt; - - bool InvalidSpelling = false; - SmallString<32> Buffer; - StringRef ValueStr = PP.getSpelling(Tokens.front(), Buffer, &InvalidSpelling); - if (InvalidSpelling) - return std::nullopt; - - ValueStr = ValueStr.rtrim("uUlL"); - uint64_t Val; - if (ValueStr.getAsInteger(/*Radix=*/0, Val)) - return std::nullopt; - return Val; -} - -static std::optional<uint64_t> getPathMaxValue(const ASTContext &Ctx, - const Preprocessor &PP) { - if (std::optional<uint64_t> MacroVal = tryExpandMacroAsUInt("PATH_MAX", PP)) - return MacroVal; - - const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); - if (T.isOSLinux() || T.isOSFuchsia() || T.isOSAIX() || T.isOSHaiku()) - return 4096; - if (T.isOSDarwin() || T.isOSFreeBSD() || T.isOSNetBSD() || T.isOSOpenBSD() || - T.isOSSolaris()) - return 1024; - return std::nullopt; -} } // anonymous namespace void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, @@ -1646,12 +1602,20 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, break; } case Builtin::BIrealpath: { - std::optional<uint64_t> PathMax = getPathMaxValue(Context, PP); - if (!PathMax) + BufferSize = Checker.ComputeSizeArgument(1); + if (!BufferSize) + return; + const llvm::Triple &T = Context.getTargetInfo().getTriple(); + uint64_t PathMax = 0; + if (T.isOSLinux() || T.isOSFuchsia() || T.isOSAIX() || T.isOSHaiku()) + PathMax = 4096; + else if (T.isOSDarwin() || T.isOSFreeBSD() || T.isOSNetBSD() || + T.isOSOpenBSD() || T.isOSSolaris()) + PathMax = 1024; + else return; DiagID = diag::warn_fortify_source_buffer_too_small; - AccessSize = llvm::APSInt::getUnsigned(*PathMax).extOrTrunc(SizeTypeWidth); - BufferSize = Checker.ComputeSizeArgument(1); + AccessSize = llvm::APSInt::getUnsigned(PathMax).extOrTrunc(SizeTypeWidth); break; } // memchr(buf, val, size) diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c index 0873432e8df581..1d8fb7f7d80b9f 100644 --- a/clang/test/Sema/warn-fortify-source.c +++ b/clang/test/Sema/warn-fortify-source.c @@ -183,15 +183,6 @@ void call_realpath(const char *path, char *unknown_buf) { realpath(path, small_buf); // expected-warning {{'realpath' may overflow; destination buffer has size 1023, but at least 1024 bytes are required}} } -#define PATH_MAX (4096) -void call_realpath_macro(const char *path) { - char ok_buf[4096]; - char small_buf[4095]; - realpath(path, ok_buf); - realpath(path, small_buf); // expected-warning {{'realpath' may overflow; destination buffer has size 4095, but at least 4096 bytes are required}} -} -#undef PATH_MAX - void call_snprintf(double d, int n) { char buf[10]; __builtin_snprintf(buf, 10, "merp"); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
