Skip to content

Commit 4f33e7c

Browse files
authored
[analyzer] Report violations of the "returns_nonnull" attribute (#106048)
Make sure code respects the GNU-extension `__attribute__((returns_nonnull))`. Extend the NullabilityChecker to check that a function returns_nonnull does not return a nullptr. This commit also reverts an old hack introduced by 49bd58f because it is no longer needed CPP-4741
1 parent 6f62757 commit 4f33e7c

File tree

4 files changed

+85
-8
lines changed

4 files changed

+85
-8
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ if ((y = make_int())) {
571571
nullability
572572
^^^^^^^^^^^
573573
574-
Objective C checkers that warn for null pointer passing and dereferencing errors.
574+
Checkers (mostly Objective C) that warn for null pointer passing and dereferencing errors.
575575
576576
.. _nullability-NullPassedToNonnull:
577577
@@ -588,8 +588,8 @@ Warns when a null pointer is passed to a pointer which has a _Nonnull type.
588588
589589
.. _nullability-NullReturnedFromNonnull:
590590
591-
nullability.NullReturnedFromNonnull (ObjC)
592-
""""""""""""""""""""""""""""""""""""""""""
591+
nullability.NullReturnedFromNonnull (C, C++, ObjC)
592+
""""""""""""""""""""""""""""""""""""""""""""""""""
593593
Warns when a null pointer is returned from a function that has _Nonnull return type.
594594
595595
.. code-block:: objc
@@ -604,6 +604,22 @@ Warns when a null pointer is returned from a function that has _Nonnull return t
604604
return result;
605605
}
606606
607+
Warns when a null pointer is returned from a function annotated with ``__attribute__((returns_nonnull))``
608+
609+
.. code-block:: cpp
610+
611+
int global;
612+
__attribute__((returns_nonnull)) void* getPtr(void* p);
613+
614+
void* getPtr(void* p) {
615+
if (p) { // forgot to negate the condition
616+
return &global;
617+
}
618+
// Warning: nullptr returned from a function that is expected
619+
// to return a non-null value
620+
return p;
621+
}
622+
607623
.. _nullability-NullableDereferenced:
608624
609625
nullability.NullableDereferenced (ObjC)

clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
692692
NullConstraint Nullness = getNullConstraint(*RetSVal, State);
693693

694694
Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
695+
if (const auto *FunDecl = C.getLocationContext()->getDecl();
696+
FunDecl && FunDecl->getAttr<ReturnsNonNullAttr>() &&
697+
(RequiredNullability == Nullability::Unspecified ||
698+
RequiredNullability == Nullability::Nullable)) {
699+
// If a function is marked with the returns_nonnull attribute,
700+
// the return value must be non-null.
701+
RequiredNullability = Nullability::Nonnull;
702+
}
695703

696704
// If the returned value is null but the type of the expression
697705
// generating it is nonnull then we will suppress the diagnostic.
@@ -705,7 +713,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
705713
Nullness == NullConstraint::IsNull);
706714
if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
707715
RetExprTypeLevelNullability != Nullability::Nonnull &&
708-
!InSuppressedMethodFamily && C.getLocationContext()->inTopFrame()) {
716+
!InSuppressedMethodFamily) {
709717
static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
710718
ExplodedNode *N = C.generateErrorNode(State, &Tag);
711719
if (!N)

clang/test/Analysis/nullability.c

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,65 @@
1-
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability -Wno-deprecated-non-prototype -verify %s
1+
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability,debug.ExprInspection -verify %s
2+
3+
void clang_analyzer_warnIfReached(void);
24

35
void it_takes_two(int a, int b);
4-
void function_pointer_arity_mismatch() {
6+
void function_pointer_arity_mismatch(void) {
57
void(*fptr)() = it_takes_two;
68
fptr(1); // no-crash expected-warning {{Function taking 2 arguments is called with fewer (1)}}
9+
// expected-warning@-1 {{passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C23}}
710
}
811

9-
void block_arity_mismatch() {
12+
void block_arity_mismatch(void) {
1013
void(^b)() = ^(int a, int b) { };
1114
b(1); // no-crash expected-warning {{Block taking 2 arguments is called with fewer (1)}}
15+
// expected-warning@-1 {{passing arguments to a function without a prototype is deprecated in all versions of C and is not supported in C23}}
16+
}
17+
18+
int *nonnull_return_annotation_indirect(void) __attribute__((returns_nonnull));
19+
int *nonnull_return_annotation_indirect(void) {
20+
int *x = 0;
21+
return x; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
22+
}
23+
24+
int *nonnull_return_annotation_direct(void) __attribute__((returns_nonnull));
25+
int *nonnull_return_annotation_direct(void) {
26+
return 0; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
27+
} // expected-warning@-1 {{null returned from function that requires a non-null return value}}
28+
29+
int *nonnull_return_annotation_assumed(int* ptr) __attribute__((returns_nonnull));
30+
int *nonnull_return_annotation_assumed(int* ptr) {
31+
if (ptr) {
32+
return ptr;
33+
}
34+
return ptr; // expected-warning {{Null returned from a function that is expected to return a non-null value}}
35+
}
36+
37+
int *produce_nonnull_ptr(void) __attribute__((returns_nonnull));
38+
39+
__attribute__((returns_nonnull))
40+
int *cannot_return_null(void) {
41+
int *x = produce_nonnull_ptr();
42+
if (!x) {
43+
clang_analyzer_warnIfReached();
44+
// expected-warning@-1 {{REACHABLE}}
45+
// TODO: This warning is a false positive, according to the contract of
46+
// produce_nonnull_ptr, x cannot be null.
47+
}
48+
// Regardless of the potential state split above, x cannot be nullptr
49+
// according to the produce_nonnull_ptr annotation.
50+
return x;
51+
// False positive: expected-warning@-1 {{Null returned from a function that is expected to return a non-null value}}
52+
}
53+
54+
__attribute__((returns_nonnull)) int *passthrough(int *p) {
55+
return p; // no-warning: we have no evidence that `p` is null, i.e., violating the contract
56+
}
57+
58+
__attribute__((returns_nonnull)) int *passthrough2(int *p);
59+
int *passthrough2(int *p) {
60+
return p; // expected-warning{{Null returned from a function that is expected to return a non-null value}}
61+
}
62+
63+
void call_with_null(void) {
64+
passthrough2(0);
1265
}

clang/test/Analysis/nullability.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ -(Dummy *)callerWithParam:(Dummy * _Nonnull) p1 {
438438

439439
int * _Nonnull InlinedReturnNullOverSuppressionCallee(int * _Nonnull p2) {
440440
int *result = 0;
441-
return result; // no-warning; but this is an over suppression
441+
return result; // expected-warning{{Null returned from a function that is expected to return a non-null value}}
442442
}
443443

444444
int *InlinedReturnNullOverSuppressionCaller(int * _Nonnull p1) {

0 commit comments

Comments
 (0)