[clang] [clang][Sema] Add noinline check for __builtin_frame_address and __builtin_return_address (PR #82966)

Timothy Herchen via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 26 12:22:33 PST 2024


https://github.com/anematode updated https://github.com/llvm/llvm-project/pull/82966

>From cd68484e851040fc84e66933454f6fd910566b81 Mon Sep 17 00:00:00 2001
From: Timothy Herchen <timothy.herchen at gmail.com>
Date: Mon, 26 Feb 2024 12:21:40 -0800
Subject: [PATCH] Add noinline check for __builtin_frame_address and
 __builtin_return_address

Resolves #66059. GCC's behavior in the case of inlining (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) is that a caller's return/frame address may be returned, if the function in question gets inlined. Their docs encourage the function to be marked noinline to prevent this behavior. Therefore, produce a warning if the function containing the call to __builtin_frame_address and __builtin_return_address is not marked noinline.
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 ++
 clang/lib/Sema/SemaChecking.cpp               | 34 ++++++++++----
 clang/test/Sema/builtin-returnaddress.c       | 44 ++++++++++++++++---
 3 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a7f2858477bee6..6afd34d50086ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2029,6 +2029,10 @@ def warn_frame_address : Warning<
   "calling '%0' with a nonzero argument is unsafe">,
   InGroup<FrameAddress>, DefaultIgnore;
 
+def warn_frame_address_missing_noinline: Warning<
+  "calling '%0' in function not marked __attribute__((noinline)) may return a caller's %1 address">,
+  InGroup<FrameAddress>, DefaultIgnore;
+
 def warn_cxx98_compat_nontrivial_union_or_anon_struct_member : Warning<
   "%select{anonymous struct|union}0 member %1 with a non-trivial "
   "%sub{select_special_member_kind}2 is incompatible with C++98">,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 7fa295ebd94044..7fd34816dbcda9 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2729,17 +2729,33 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
     if (SemaBuiltinConstantArgRange(TheCall, 0, 0, 0xFFFF))
       return ExprError();
 
-    // -Wframe-address warning if non-zero passed to builtin
-    // return/frame address.
     Expr::EvalResult Result;
     if (!TheCall->getArg(0)->isValueDependent() &&
-        TheCall->getArg(0)->EvaluateAsInt(Result, getASTContext()) &&
-        Result.Val.getInt() != 0)
-      Diag(TheCall->getBeginLoc(), diag::warn_frame_address)
-          << ((BuiltinID == Builtin::BI__builtin_return_address)
-                  ? "__builtin_return_address"
-                  : "__builtin_frame_address")
-          << TheCall->getSourceRange();
+        TheCall->getArg(0)->EvaluateAsInt(Result, getASTContext())) {
+      const char *BuiltinName =
+          (BuiltinID == Builtin::BI__builtin_return_address)
+              ? "__builtin_return_address"
+              : "__builtin_frame_address";
+
+      // -Wframe-address warning if non-zero passed to builtin
+      // return/frame address.
+      if (Result.Val.getInt() != 0) {
+        Diag(TheCall->getBeginLoc(), diag::warn_frame_address)
+            << BuiltinName << TheCall->getSourceRange();
+      }
+
+      // -Wframe-address warning if enclosing function is not marked noinline.
+      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CurContext)) {
+        if (!FD->hasAttr<NoInlineAttr>() && !FD->isMain()) {
+          const char *ShortName =
+              (BuiltinID == Builtin::BI__builtin_return_address) ? "return"
+                                                                 : "frame";
+          Diag(TheCall->getBeginLoc(),
+               diag::warn_frame_address_missing_noinline)
+              << BuiltinName << ShortName << TheCall->getSourceRange();
+        }
+      }
+    }
     break;
   }
 
diff --git a/clang/test/Sema/builtin-returnaddress.c b/clang/test/Sema/builtin-returnaddress.c
index 16d2a517ac12f2..0b6733f9381c9f 100644
--- a/clang/test/Sema/builtin-returnaddress.c
+++ b/clang/test/Sema/builtin-returnaddress.c
@@ -2,24 +2,32 @@
 // RUN: %clang_cc1 -fsyntax-only -Wmost -verify %s
 // RUN: %clang_cc1 -x c++ -fsyntax-only -Wframe-address -verify %s
 
-void* a(unsigned x) {
+__attribute__((noinline)) void* a(unsigned x) {
 return __builtin_return_address(0);
 }
 
-void* b(unsigned x) {
+__attribute__((noinline)) void* b(unsigned x) {
 return __builtin_return_address(1); // expected-warning{{calling '__builtin_return_address' with a nonzero argument is unsafe}}
 }
 
-void* c(unsigned x) {
+__attribute__((noinline)) void* c(unsigned x) {
 return __builtin_frame_address(0);
 }
 
-void* d(unsigned x) {
+__attribute__((noinline)) void* d(unsigned x) {
 return __builtin_frame_address(1); // expected-warning{{calling '__builtin_frame_address' with a nonzero argument is unsafe}}
 }
 
+void* e(unsigned x) {
+  return __builtin_frame_address(0); // expected-warning{{calling '__builtin_frame_address' in function not marked __attribute__((noinline)) may return a caller's frame address}}
+}
+
+void* f(unsigned x) {
+  return __builtin_return_address(0); // expected-warning{{calling '__builtin_return_address' in function not marked __attribute__((noinline)) may return a caller's return address}}
+}
+
 #ifdef __cplusplus
-template<int N> void *RA()
+template<int N> __attribute__((noinline)) void *RA()
 {
   return __builtin_return_address(N); // expected-warning{{calling '__builtin_return_address' with a nonzero argument is unsafe}}
 }
@@ -28,4 +36,28 @@ void *foo()
 {
  return RA<2>(); // expected-note{{in instantiation of function template specialization 'RA<2>' requested here}}
 }
-#endif
+
+void* f() {
+  return ([&] () {
+    return __builtin_frame_address(0); // expected-warning{{calling '__builtin_frame_address' in function not marked __attribute__((noinline)) may return a caller's frame address}}
+  })();
+}
+
+void* g() {
+  return ([&] () __attribute__((noinline)) {
+    return __builtin_frame_address(0);
+  })();
+}
+
+void* h() {
+  return ([&] () {
+    return __builtin_return_address(0); // expected-warning{{calling '__builtin_return_address' in function not marked __attribute__((noinline)) may return a caller's return address}}
+  })();
+}
+
+void* i() {
+  return ([&] () __attribute__((noinline)) {
+    return __builtin_return_address(0);
+  })();
+}
+#endif
\ No newline at end of file



More information about the cfe-commits mailing list