diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 94c13d4e1cf12b90b749f8bb4ca1ac3d3439f4ef..8c3133e2444ec0056d75c0de9b7a89cc9a3f1fac 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -740,6 +740,51 @@ readability_comparator (const void *p1, const void *p2)
   return 0;
 }
 
+/* Return true is SNODE is the EXIT node of a function, or is one
+   of the final snodes within its function.
+
+   Specifically, handle the final supernodes before the EXIT node,
+   for the case of clobbers that happen immediately before exiting.
+   We need a run of snodes leading to the return_p snode, where all edges are
+   intraprocedural, and every snode has just one successor.
+
+   We use this when suppressing leak reports at the end of "main".  */
+
+static bool
+returning_from_function_p (const supernode *snode)
+{
+  if (!snode)
+    return false;
+
+  unsigned count = 0;
+  const supernode *iter = snode;
+  while (true)
+    {
+      if (iter->return_p ())
+	return true;
+      if (iter->m_succs.length () != 1)
+	return false;
+      const superedge *sedge = iter->m_succs[0];
+      if (sedge->get_kind () != SUPEREDGE_CFG_EDGE)
+	return false;
+      iter = sedge->m_dest;
+
+      /* Impose a limit to ensure we terminate for pathological cases.
+
+	 We only care about the final 3 nodes, due to cases like:
+	   BB:
+	     (clobber causing leak)
+
+	   BB:
+	   <label>:
+	   return _val;
+
+	   EXIT BB.*/
+      if (++count > 3)
+	return false;
+    }
+}
+
 /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
    If on_leak returns a pending_diagnostic, queue it up to be reported,
    so that we potentially complain about a leak of SVAL in the given STATE.  */
@@ -794,8 +839,7 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
   gcc_assert (m_enode_for_diag);
 
   /* Don't complain about leaks when returning from "main".  */
-  if (m_enode_for_diag->get_supernode ()
-      && m_enode_for_diag->get_supernode ()->return_p ())
+  if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
     {
       tree fndecl = m_enode_for_diag->get_function ()->decl;
       if (id_equal (DECL_NAME (fndecl), "main"))
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101983-main.c b/gcc/testsuite/gcc.dg/analyzer/pr101983-main.c
new file mode 100644
index 0000000000000000000000000000000000000000..a84353be35a46220ee9b4615278d3a2ba9fed07a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr101983-main.c
@@ -0,0 +1,38 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex -fno-analyzer-call-summaries" } */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+struct list {
+	struct list* next;
+	void *a;
+};
+
+void func(struct list **res)
+{
+	struct list *cur = NULL;
+	do {
+		struct list *n = malloc(sizeof(struct list));
+		void *a = malloc(1);
+		if (n == NULL || a == NULL) {
+			if (n != NULL) free(n);
+			if (a != NULL) free(a);
+			break;
+		}
+
+		if (cur == NULL) {
+			*res = cur = n;
+		} else {
+			cur->next = n;
+			cur = n;
+		}
+		n->a = a;
+	} while (true);
+}
+
+int main()
+{
+	struct list *res;
+	func(&res);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101983-not-main.c b/gcc/testsuite/gcc.dg/analyzer/pr101983-not-main.c
new file mode 100644
index 0000000000000000000000000000000000000000..fbf3a393ebb6dadb42177987e528960b1b4d13c8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr101983-not-main.c
@@ -0,0 +1,40 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex -fno-analyzer-call-summaries" } */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+struct list {
+	struct list* next;
+	void *a;
+};
+
+void func(struct list **res)
+{
+	struct list *cur = NULL;
+	do {
+		struct list *n = malloc(sizeof(struct list));
+		void *a = malloc(1);
+		if (n == NULL || a == NULL) {
+			if (n != NULL) free(n);
+			if (a != NULL) free(a);
+			break;
+		}
+
+		if (cur == NULL) {
+			*res = cur = n;
+		} else {
+			cur->next = n;
+			cur = n;
+		}
+		n->a = a;
+	} while (true);
+}
+
+int not_main()
+{
+	struct list *res;
+	func(&res);
+} /* { dg-warning "leak of 'res'" "leak of res" } */
+/* { dg-warning "leak of '<unknown>'" "leak of res->a" { target *-*-* } .-1 } */
+/* TODO: we should emit 'res->a' rather than '<unknown>' here.  */