Author: Aaron Ballman
Date: 2026-10-06T19:51:35Z
New Revision: 2186afb50a84dad3c759070aed82fcd992caa748

URL: 
https://github.com/llvm/llvm-project/commit/2186afb50a84dad3c759070aed82fcd992caa748
DIFF: 
https://github.com/llvm/llvm-project/commit/2186afb50a84dad3c759070aed82fcd992caa748.diff

LOG: [clang] Fix handling of invalid nonnull attribute arguments (#229519)

The nonnull attribute accepts a variadic list of indexes to function
pointer parameters, specifying which ones are not allowed to take a null
value. If an index is invalid (e.g. the referenced function parameter is
not a pointer), then the index is dropped from the semantic attribute
retained in the AST.

Previously, if all the specified indicies were invalid, Clang would
associate a semantic nonnull attribute with no arguments on the
FunctionDecl, which would cause all pointer arguments to be treated as
nonnull. Now, if there are no valid indicies specified, the attribute is
dropped entirely and so no pointer arguments are treated as nonnull.

Fixes #228670

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/Sema/nonnull.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f2d8d83670b68..c3ed90f2492aa 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -315,6 +315,10 @@ features cannot lower the translation-unit ABI level;
 
 - Clang now accepts `_single_inheritance` under `-fms-compatibility` as an 
alias for `__single_inheritance`; `_multiple_inheritance` and 
`_virtual_inheritance` were already correctly supported as aliases.
 
+- Fixed a bug with handling a `nonnull` attribute with an invalid argument
+  index such that it would inadvertantly apply the attribute with no arguments,
+  causing all function parameters of pointer type to be considered nonnull. 
(#GH228670)
+
 ### Improvements to Clang's diagnostics
 
 - `-Wfortify-source` now diagnoses when `strlcat`, `__builtin_strlcat`, 
`strlcpy`, or

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eb4a8c2ab9ae0..185a316bcc77c 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1377,6 +1377,16 @@ static void handleNonNullAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
     NonNullArgs.push_back(Idx);
   }
 
+  // If an argument was specified and there was an attribute ignored warning
+  // issued for it, do not apply the nonnull attribute without any arguments as
+  // that has incorrect semantics in a function like:
+  //   __attribute__((nonnull(1))) void f(int val, int *ptr);
+  // because that will signal that 'ptr' is nonnull when it's not intended to
+  // be marked as such. However, continue on if there is at least one valid
+  // parameter index.
+  if (AL.getNumArgs() != 0 && NonNullArgs.empty())
+    return;
+
   // If no arguments were specified to __attribute__((nonnull)) then all 
pointer
   // arguments have a nonnull attribute; warn if there aren't any. Skip this
   // check if the attribute came from a macro expansion or a template

diff  --git a/clang/test/Sema/nonnull.c b/clang/test/Sema/nonnull.c
index 9f3ce2623480f..9098bf0ecb915 100644
--- a/clang/test/Sema/nonnull.c
+++ b/clang/test/Sema/nonnull.c
@@ -23,7 +23,7 @@ int main(void) {
 }
 
 void foo(const char *str) __attribute__((nonnull("foo"))); // 
expected-error{{'nonnull' attribute requires parameter 1 to be an integer 
constant}}
-void bar(int i) __attribute__((nonnull(1))); // expected-warning {{'nonnull' 
attribute only applies to pointer arguments}} expected-warning {{'nonnull' 
attribute applied to function with no pointer arguments}}
+void bar(int i) __attribute__((nonnull(1))); // expected-warning {{'nonnull' 
attribute only applies to pointer arguments}}
 
 void baz(__attribute__((nonnull)) const char *str);
 void baz2(__attribute__((nonnull(1))) const char *str); // expected-warning 
{{'nonnull' attribute when used on parameters takes no arguments}}
@@ -181,3 +181,21 @@ void gh176638_1(int (*g)(const char *h, ...) 
__attribute__((nonnull(2147483648))
 void gh176638_2(int (*g)(const char *h, ...) 
__attribute__((nonnull(1073741825))) __attribute__((nonnull))) {} // 
expected-error {{attribute parameter 1 is out of bounds}}
 void gh176638_3(int (*g)(const char *h, ...) 
__attribute__((nonnull(1073741824))) __attribute__((nonnull))) {} // 
expected-error {{attribute parameter 1 is out of bounds}} 
 void gh176638_4(int (*g)(const char *h, ...) 
__attribute__((nonnull(1073741823))) __attribute__((nonnull))) {} // no-warning
+
+__attribute__((nonnull(1))) int gh228670_1(int p0, char *p1) { // 
expected-warning {{'nonnull' attribute only applies to pointer arguments}}
+  // Previously, the ignored attribute was ending up being applied to the
+  // function without any arguments, meaning all parameters are considered
+  // nonnull. That would in turn generate a warning diagnostic about p1 always
+  // being nonnull. This demonstrates that the attribute is being correctly
+  // ignored.
+  return p1 ? 1 : 0;
+}
+
+__attribute__((nonnull(1, 3))) int gh228670_2(int p0, char *p1, char *p2) { // 
expected-warning {{'nonnull' attribute only applies to pointer arguments}} \
+                                                                               
expected-note {{declared 'nonnull' here}}
+  // Similar to the previous test, this verifies that the attribute is not
+  // applied to every parameter, but that the third parameter is still handled
+  // correctly as being non-null because it is explicitly specified.
+  (void)(p1 ? 1 : 0); // No warning here because p1 isn't marked
+  (void)(p2 ? 1 : 0); // expected-warning {{nonnull parameter 'p2' will 
evaluate to 'true' on first encounter}}
+}


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

Reply via email to