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

Reply via email to