https://github.com/ChenMiaoi updated 
https://github.com/llvm/llvm-project/pull/229010

>From 7a0b2f93ae262b941600ce2e813ca5621a9020d3 Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Mon, 5 Oct 2026 16:41:51 +0800
Subject: [PATCH 1/3] [clang][Sema] Avoid out-of-bounds format_arg access

Clang implicitly adds `format_arg(1)` to `__CFStringMakeConstantString`,
including declarations created during error recovery. A zero-argument
call can therefore make `checkFormatStringExpr` access a nonexistent
argument.

Reproducer:

```c
void a(char *) __attribute__((format(__CFString__, 1, 2)));
void b() { a(__CFStringMakeConstantString()); }
```

Before this change, an assertions-enabled build crashes after reporting
the source errors (diagnostic excerpts):

```text
error: call to undeclared function '__CFStringMakeConstantString';
       ISO C99 and later do not support implicit function declarations
error: incompatible integer to pointer conversion passing 'int' to parameter of 
type 'char *'
Assertion `Arg < getNumArgs() && "Arg access out of range!"' failed.
```

Check the `format_arg` index against `CE->getNumArgs()` before calling
`CE->getArg()`. Return `SLCT_NotALiteral` when the argument is missing so
normal diagnostics can continue.

After this change, the compiler reports diagnostics and exits with
status 1 without crashing (diagnostic excerpts):

```text
error: call to undeclared function '__CFStringMakeConstantString';
       ISO C99 and later do not support implicit function declarations
error: incompatible integer to pointer conversion passing 'int' to parameter of 
type 'char *'
warning: format string is not a string literal (potentially insecure)
```

Fixes #225034
---
 clang/lib/Sema/SemaChecking.cpp           |  7 ++++++-
 clang/test/Sema/format-strings-cfstring.c | 18 ++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Sema/format-strings-cfstring.c

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0531dfa877fbdf..eb5982f10e7630 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7710,7 +7710,12 @@ checkFormatStringExpr(Sema &S, const StringLiteral 
*ReferenceFormatString,
       bool IsFirst = true;
       StringLiteralCheckType CommonResult;
       for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
-        const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
+        // An implicitly added attribute may refer to a missing argument.
+        // https://github.com/llvm/llvm-project/issues/225034
+        unsigned ArgIndex = FA->getFormatIdx().getASTIndex();
+        if (ArgIndex >= CE->getNumArgs())
+          return SLCT_NotALiteral;
+        const Expr *Arg = CE->getArg(ArgIndex);
         StringLiteralCheckType Result = checkFormatStringExpr(
             S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
             Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
diff --git a/clang/test/Sema/format-strings-cfstring.c 
b/clang/test/Sema/format-strings-cfstring.c
new file mode 100644
index 00000000000000..776a82e7c3eb7d
--- /dev/null
+++ b/clang/test/Sema/format-strings-cfstring.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -Wno-gcc-compat 
-verify=expected,implicit %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -Wno-gcc-compat -DDECLARE_CFSTRING 
-verify %s
+
+// An implicitly added format_arg attribute may refer to a missing argument,
+// both during error recovery and with a non-prototype declaration.
+// https://github.com/llvm/llvm-project/issues/225034
+#ifdef DECLARE_CFSTRING
+char *__CFStringMakeConstantString();
+#endif
+
+void a(char *) __attribute__((format(__CFString__, 1, 2))); // implicit-note 
{{passing argument to parameter here}}
+
+void b(void) {
+  a(__CFStringMakeConstantString()); // expected-warning {{format string is 
not a string literal (potentially insecure)}}
+  // expected-note@-1 {{treat the string as an argument to avoid this}}
+  // implicit-error@-2 {{call to undeclared function 
'__CFStringMakeConstantString'}}
+  // implicit-error@-3 {{incompatible integer to pointer conversion passing 
'int' to parameter of type 'char *'}}
+}

>From 72e0d4ff6d7cf9d8ea8fa483fb4999c753832120 Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Thu, 8 Oct 2026 23:55:57 +0800
Subject: [PATCH 2/3] [clang] Fix crash caused by invalid implicit format
 attributes

Clang implicitly adds `format_arg(1)` to the prototypeless declaration
created during error recovery for an undeclared
`__CFStringMakeConstantString`. Format string checking then accesses
the call's nonexistent first argument, triggering an out-of-bounds
assertion.

In `Sema::AddKnownFunctionAttributes()`, check that the function type
is a prototype function type and that the referenced format string
parameter exists before implicitly adding `format` or `format_arg`
attributes.

Fixes #225034
---
 clang/docs/ReleaseNotes.md                    |  3 ++
 clang/lib/Sema/SemaChecking.cpp               |  7 +--
 clang/lib/Sema/SemaDecl.cpp                   | 31 ++++++------
 clang/test/Sema/format-strings-cfstring.c     | 18 -------
 .../Sema/format-strings-implicit-attributes.c | 50 +++++++++++++++++++
 5 files changed, 70 insertions(+), 39 deletions(-)
 delete mode 100644 clang/test/Sema/format-strings-cfstring.c
 create mode 100644 clang/test/Sema/format-strings-implicit-attributes.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c3ec56c5741f29..85b2e9399c0652 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -689,6 +689,9 @@ features cannot lower the translation-unit ABI level;
 
 #### Bug Fixes to Attribute Support
 
+- Fixed a crash when using a zero-argument call to an undeclared
+  `__CFStringMakeConstantString` as a format string. (#GH225034)
+
 - Fixed an assertion failure when parsing malformed GNU `__attribute__`
   syntax followed by a parenthesized expression list in C code. (#GH225045)
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index eb5982f10e7630..0531dfa877fbdf 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7710,12 +7710,7 @@ checkFormatStringExpr(Sema &S, const StringLiteral 
*ReferenceFormatString,
       bool IsFirst = true;
       StringLiteralCheckType CommonResult;
       for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
-        // An implicitly added attribute may refer to a missing argument.
-        // https://github.com/llvm/llvm-project/issues/225034
-        unsigned ArgIndex = FA->getFormatIdx().getASTIndex();
-        if (ArgIndex >= CE->getNumArgs())
-          return SLCT_NotALiteral;
-        const Expr *Arg = CE->getArg(ArgIndex);
+        const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
         StringLiteralCheckType Result = checkFormatStringExpr(
             S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
             Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1459b71326375a..84e7d37ed82ad7 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17630,18 +17630,20 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl 
*FD) {
   if (FD->isInvalidDecl())
     return;
 
+  // Both format and format_arg attributes require a function prototype.
+  const bool HasPrototype = FD->getType()->isFunctionProtoType();
+
   // If this is a built-in function, map its builtin attributes to
   // actual attributes.
   if (unsigned BuiltinID = FD->getBuiltinID()) {
     // Handle printf-formatting attributes.
     unsigned FormatIdx;
     bool HasVAListArg;
-    if (Context.BuiltinInfo.isPrintfLike(BuiltinID, FormatIdx, HasVAListArg)) {
-      if (!FD->hasAttr<FormatAttr>()) {
+    if (HasPrototype &&
+        Context.BuiltinInfo.isPrintfLike(BuiltinID, FormatIdx, HasVAListArg)) {
+      if (!FD->hasAttr<FormatAttr>() && FormatIdx < FD->getNumParams()) {
         const char *fmt = "printf";
-        unsigned int NumParams = FD->getNumParams();
-        if (FormatIdx < NumParams && // NumParams may be 0 (e.g. vfprintf)
-            FD->getParamDecl(FormatIdx)->getType()->isObjCObjectPointerType())
+        if (FD->getParamDecl(FormatIdx)->getType()->isObjCObjectPointerType())
           fmt = "NSString";
         FD->addAttr(FormatAttr::CreateImplicit(Context,
                                                &Context.Idents.get(fmt),
@@ -17650,14 +17652,12 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl 
*FD) {
                                                FD->getLocation()));
       }
     }
-    if (Context.BuiltinInfo.isScanfLike(BuiltinID, FormatIdx,
-                                             HasVAListArg)) {
-     if (!FD->hasAttr<FormatAttr>())
-       FD->addAttr(FormatAttr::CreateImplicit(Context,
-                                              &Context.Idents.get("scanf"),
-                                              FormatIdx+1,
-                                              HasVAListArg ? 0 : FormatIdx+2,
-                                              FD->getLocation()));
+    if (HasPrototype &&
+        Context.BuiltinInfo.isScanfLike(BuiltinID, FormatIdx, HasVAListArg)) {
+      if (!FD->hasAttr<FormatAttr>() && FormatIdx < FD->getNumParams())
+        FD->addAttr(FormatAttr::CreateImplicit(
+            Context, &Context.Idents.get("scanf"), FormatIdx + 1,
+            HasVAListArg ? 0 : FormatIdx + 2, FD->getLocation()));
     }
 
     // Handle automatically recognized callbacks.
@@ -17807,7 +17807,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) 
{
   if (Name->isStr("asprintf") || Name->isStr("vasprintf")) {
     // FIXME: asprintf and vasprintf aren't C99 functions. Should they be
     // target-specific builtins, perhaps?
-    if (!FD->hasAttr<FormatAttr>())
+    if (HasPrototype && FD->getNumParams() >= 2 && !FD->hasAttr<FormatAttr>())
       FD->addAttr(FormatAttr::CreateImplicit(Context,
                                              &Context.Idents.get("printf"), 2,
                                              Name->isStr("vasprintf") ? 0 : 3,
@@ -17817,7 +17817,8 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) 
{
   if (Name->isStr("__CFStringMakeConstantString")) {
     // We already have a __builtin___CFStringMakeConstantString,
     // but builds that use -fno-constant-cfstrings don't go through that.
-    if (!FD->hasAttr<FormatArgAttr>())
+    if (HasPrototype && FD->getNumParams() >= 1 &&
+        !FD->hasAttr<FormatArgAttr>())
       FD->addAttr(FormatArgAttr::CreateImplicit(Context, ParamIdx(1, FD),
                                                 FD->getLocation()));
   }
diff --git a/clang/test/Sema/format-strings-cfstring.c 
b/clang/test/Sema/format-strings-cfstring.c
deleted file mode 100644
index 776a82e7c3eb7d..00000000000000
--- a/clang/test/Sema/format-strings-cfstring.c
+++ /dev/null
@@ -1,18 +0,0 @@
-// RUN: %clang_cc1 -std=c11 -fsyntax-only -Wno-gcc-compat 
-verify=expected,implicit %s
-// RUN: %clang_cc1 -std=c11 -fsyntax-only -Wno-gcc-compat -DDECLARE_CFSTRING 
-verify %s
-
-// An implicitly added format_arg attribute may refer to a missing argument,
-// both during error recovery and with a non-prototype declaration.
-// https://github.com/llvm/llvm-project/issues/225034
-#ifdef DECLARE_CFSTRING
-char *__CFStringMakeConstantString();
-#endif
-
-void a(char *) __attribute__((format(__CFString__, 1, 2))); // implicit-note 
{{passing argument to parameter here}}
-
-void b(void) {
-  a(__CFStringMakeConstantString()); // expected-warning {{format string is 
not a string literal (potentially insecure)}}
-  // expected-note@-1 {{treat the string as an argument to avoid this}}
-  // implicit-error@-2 {{call to undeclared function 
'__CFStringMakeConstantString'}}
-  // implicit-error@-3 {{incompatible integer to pointer conversion passing 
'int' to parameter of type 'char *'}}
-}
diff --git a/clang/test/Sema/format-strings-implicit-attributes.c 
b/clang/test/Sema/format-strings-implicit-attributes.c
new file mode 100644
index 00000000000000..e96ee514f1ee3e
--- /dev/null
+++ b/clang/test/Sema/format-strings-implicit-attributes.c
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -std=c11 -ast-dump -Wno-gcc-compat 
-verify=expected,implicit %s | FileCheck %s --check-prefix=NO-FORMAT-ARG
+// RUN: %clang_cc1 -std=c11 -ast-dump -Wno-gcc-compat -DNO_PROTOTYPE -verify 
%s | FileCheck %s --check-prefix=NO-FORMAT-ARG
+// RUN: %clang_cc1 -std=c11 -ast-dump -Wno-gcc-compat -DMISSING_FORMAT_PARAM 
-verify %s | FileCheck %s --check-prefix=NO-FORMAT-ARG
+// RUN: %clang_cc1 -std=c11 -ast-dump -DCFSTRING_VALID_PROTO -verify %s | 
FileCheck %s --check-prefix=FORMAT-ARG
+
+#ifdef CFSTRING_VALID_PROTO
+// expected-no-diagnostics
+
+// Keep inferring format_arg(1) when the referenced parameter exists.
+char *__CFStringMakeConstantString(const char *);
+
+// FORMAT-ARG-LABEL: FunctionDecl{{.*}} __CFStringMakeConstantString
+// FORMAT-ARG: FormatArgAttr{{.*}}Implicit 1
+#else
+
+// Do not infer format attributes without a prototype or the format parameter.
+// https://github.com/llvm/llvm-project/issues/225034
+#if defined(MISSING_FORMAT_PARAM)
+char *__CFStringMakeConstantString(void);
+int asprintf(char **);
+int vasprintf(char **);
+
+void test_missing_format_param(char **out) {
+  asprintf(out);
+  vasprintf(out);
+}
+#elif defined(NO_PROTOTYPE)
+char *__CFStringMakeConstantString();
+int asprintf();
+int vasprintf();
+
+void test_no_prototype(void) {
+  asprintf();
+  vasprintf();
+}
+#endif
+
+void a(char *) __attribute__((format(__CFString__, 1, 2))); // implicit-note 
{{passing argument to parameter here}}
+
+void b(void) {
+  a(__CFStringMakeConstantString()); // expected-warning {{format string is 
not a string literal (potentially insecure)}}
+  // expected-note@-1 {{treat the string as an argument to avoid this}}
+  // implicit-error@-2 {{call to undeclared function 
'__CFStringMakeConstantString'}}
+  // implicit-error@-3 {{incompatible integer to pointer conversion passing 
'int' to parameter of type 'char *'}}
+}
+
+// NO-FORMAT-ARG-NOT: FormatArgAttr
+// NO-FORMAT-ARG: FunctionDecl{{.*}} b 'void (void)'
+// NO-FORMAT-ARG-NOT: FormatArgAttr
+#endif

>From 88891f5081a5c6a79c25613a8ab5f67f587b5b9e Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Fri, 9 Oct 2026 03:43:59 +0800
Subject: [PATCH 3/3] [clang][Sema] Test CFString declarations with an integer
 parameter

---
 clang/test/Sema/format-strings-implicit-attributes.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/Sema/format-strings-implicit-attributes.c 
b/clang/test/Sema/format-strings-implicit-attributes.c
index e96ee514f1ee3e..52706c504306c7 100644
--- a/clang/test/Sema/format-strings-implicit-attributes.c
+++ b/clang/test/Sema/format-strings-implicit-attributes.c
@@ -2,6 +2,7 @@
 // RUN: %clang_cc1 -std=c11 -ast-dump -Wno-gcc-compat -DNO_PROTOTYPE -verify 
%s | FileCheck %s --check-prefix=NO-FORMAT-ARG
 // RUN: %clang_cc1 -std=c11 -ast-dump -Wno-gcc-compat -DMISSING_FORMAT_PARAM 
-verify %s | FileCheck %s --check-prefix=NO-FORMAT-ARG
 // RUN: %clang_cc1 -std=c11 -ast-dump -DCFSTRING_VALID_PROTO -verify %s | 
FileCheck %s --check-prefix=FORMAT-ARG
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -Wno-gcc-compat -DCFSTRING_INT_PARAM 
-verify %s
 
 #ifdef CFSTRING_VALID_PROTO
 // expected-no-diagnostics
@@ -11,6 +12,17 @@ char *__CFStringMakeConstantString(const char *);
 
 // FORMAT-ARG-LABEL: FunctionDecl{{.*}} __CFStringMakeConstantString
 // FORMAT-ARG: FormatArgAttr{{.*}}Implicit 1
+#elif defined(CFSTRING_INT_PARAM)
+
+// A non-string parameter must not crash format checking.
+char *__CFStringMakeConstantString(int);
+
+void a(char *) __attribute__((format(__CFString__, 1, 2)));
+
+void b(void) {
+  a(__CFStringMakeConstantString(1)); // expected-warning {{format string is 
not a string literal (potentially insecure)}}
+  // expected-note@-1 {{treat the string as an argument to avoid this}}
+}
 #else
 
 // Do not infer format attributes without a prototype or the format parameter.

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to