[PATCH] D135682: Fix false positive related to handling of [[noreturn]] function pointers

2022-10-11 Thread Arseniy Zaostrovnykh via Phabricator via cfe-commits
arseniy-sonar created this revision.
arseniy-sonar added reviewers: xazax.hun, martong, NoQ, Szelethus, steakhal.
arseniy-sonar added a project: clang.
Herald added a subscriber: rnkovacs.
Herald added a project: All.
arseniy-sonar requested review of this revision.
Herald added a subscriber: cfe-commits.

Before this change, the NoReturnFunctionChecker was missing function pointers 
with a [[noreturn]] attribute, while CFG was constructed taking that into 
account, which lead CSA to take impossible paths. The reason was that the 
NoReturnFunctionChecker was looking for the attribute in the type of the entire 
call expression rather than the type of the function being called.

This change makes the [[noreturn]] attribute of a function pointer visible to 
NoReturnFunctionChecker. This leads to a more coherent behavior of the CSA on 
the AST involving


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D135682

Files:
  clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
  clang/test/Analysis/no-return.c


Index: clang/test/Analysis/no-return.c
===
--- /dev/null
+++ clang/test/Analysis/no-return.c
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+typedef void(fatal_fun)() __attribute__((__noreturn__));
+fatal_fun* fatal_fptr;
+void fatal_decl() __attribute__((__noreturn__));
+
+int rng();
+
+/// This code calls a [[noreturn]] function pointer, which used to be handled
+/// inconsistently between AST builder and CSA.
+/// In the result, CSA produces a path where this function returns non-0.
+int return_zero_or_abort_by_fnptr() {
+  if (rng()) fatal_fptr();
+  return 0;
+}
+
+/// This function calls a [[noreturn]] function.
+/// If it does return, it always returns 0.
+int return_zero_or_abort_by_direct_fun() {
+  if (rng()) fatal_decl();
+  return 0;
+}
+
+/// Trigger a division by zero issue depending on the return value
+/// of the called functions.
+int caller() {
+  int x = 0;
+  // The following if branches must never be taken.
+  if (return_zero_or_abort_by_fnptr())
+return 1 / x; // no-warning: Dead code.
+  if (return_zero_or_abort_by_direct_fun())
+return 1 / x; // no-warning: Dead code.
+
+  // Make sure the warning is still reported when viable.
+  return 1 / x; // expected-warning {{Division by zero}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -44,9 +44,11 @@
   if (const FunctionDecl *FD = dyn_cast_or_null(CE.getDecl()))
 BuildSinks = FD->hasAttr() || FD->isNoReturn();
 
-  const Expr *Callee = CE.getOriginExpr();
-  if (!BuildSinks && Callee)
-BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
+  if (const CallExpr* CExpr = dyn_cast_or_null(CE.getOriginExpr());
+  CExpr && !BuildSinks) {
+if (const Expr *C = CExpr->getCallee())
+  BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn();
+  }
 
   if (!BuildSinks && CE.isGlobalCFunction()) {
 if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {


Index: clang/test/Analysis/no-return.c
===
--- /dev/null
+++ clang/test/Analysis/no-return.c
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+typedef void(fatal_fun)() __attribute__((__noreturn__));
+fatal_fun* fatal_fptr;
+void fatal_decl() __attribute__((__noreturn__));
+
+int rng();
+
+/// This code calls a [[noreturn]] function pointer, which used to be handled
+/// inconsistently between AST builder and CSA.
+/// In the result, CSA produces a path where this function returns non-0.
+int return_zero_or_abort_by_fnptr() {
+  if (rng()) fatal_fptr();
+  return 0;
+}
+
+/// This function calls a [[noreturn]] function.
+/// If it does return, it always returns 0.
+int return_zero_or_abort_by_direct_fun() {
+  if (rng()) fatal_decl();
+  return 0;
+}
+
+/// Trigger a division by zero issue depending on the return value
+/// of the called functions.
+int caller() {
+  int x = 0;
+  // The following if branches must never be taken.
+  if (return_zero_or_abort_by_fnptr())
+return 1 / x; // no-warning: Dead code.
+  if (return_zero_or_abort_by_direct_fun())
+return 1 / x; // no-warning: Dead code.
+
+  // Make sure the warning is still reported when viable.
+  return 1 / x; // expected-warning {{Division by zero}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -44,9 +44,11 @@
   if (const FunctionDecl *FD = dyn_cast_or_null(CE.getDecl()))
 BuildSinks = FD->hasAttr() || FD->isNo

[PATCH] D138713: Fix assertion failure "PathDiagnosticSpotPiece's must have a valid location." in ReturnPtrRange checker on builtin functions

2022-11-25 Thread Arseniy Zaostrovnykh via Phabricator via cfe-commits
arseniy-sonar created this revision.
arseniy-sonar added reviewers: xazax.hun, martong, NoQ, Szelethus, steakhal.
arseniy-sonar added a project: clang.
Herald added a subscriber: rnkovacs.
Herald added a project: All.
arseniy-sonar requested review of this revision.
Herald added a subscriber: cfe-commits.

This fixes https://github.com/llvm/llvm-project/issues/55347
Builtin functions (such ast `std::move`, `std::forward`, `std::as_const`) have 
a body generated during the analysis not related to any source file so their 
statements have no valid source locations.
ReturnPtrRange checker should not report issues for these builtin functions 
because they only forward its parameter and do not create any new pointers.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138713

Files:
  clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
  clang/test/Analysis/return-ptr-range.cpp


Index: clang/test/Analysis/return-ptr-range.cpp
===
--- clang/test/Analysis/return-ptr-range.cpp
+++ clang/test/Analysis/return-ptr-range.cpp
@@ -115,3 +115,14 @@
 
 }
 
+namespace std {
+// A builtin function with the body generated on the fly.
+template  T&& move(T &&) noexcept;
+} // namespace std
+
+char buf[2];
+
+void top() {
+  // see https://github.com/llvm/llvm-project/issues/55347
+  (void)std::move(*(buf + 3)); // no-crash
+}
Index: clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -41,6 +41,13 @@
   if (!RetE)
 return;
 
+  // Skip calls to built-in functions because such functions might feature
+  // a return statement with no valid source location.
+  if (const CallExpr *CE =
+  dyn_cast_or_null(C.getStackFrame()->getCallSite());
+  CE && CE->getBuiltinCallee() != 0)
+return;
+
   SVal V = C.getSVal(RetE);
   const MemRegion *R = V.getAsRegion();
 


Index: clang/test/Analysis/return-ptr-range.cpp
===
--- clang/test/Analysis/return-ptr-range.cpp
+++ clang/test/Analysis/return-ptr-range.cpp
@@ -115,3 +115,14 @@
 
 }
 
+namespace std {
+// A builtin function with the body generated on the fly.
+template  T&& move(T &&) noexcept;
+} // namespace std
+
+char buf[2];
+
+void top() {
+  // see https://github.com/llvm/llvm-project/issues/55347
+  (void)std::move(*(buf + 3)); // no-crash
+}
Index: clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -41,6 +41,13 @@
   if (!RetE)
 return;
 
+  // Skip calls to built-in functions because such functions might feature
+  // a return statement with no valid source location.
+  if (const CallExpr *CE =
+  dyn_cast_or_null(C.getStackFrame()->getCallSite());
+  CE && CE->getBuiltinCallee() != 0)
+return;
+
   SVal V = C.getSVal(RetE);
   const MemRegion *R = V.getAsRegion();
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits