diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64
index 5c2cc7e8c5d0..6fc79727cd14 100644
--- a/tools/testing/selftests/bpf/DENYLIST.aarch64
+++ b/tools/testing/selftests/bpf/DENYLIST.aarch64
@@ -1,6 +1,7 @@
 bpf_cookie/multi_kprobe_attach_api               # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
 bpf_cookie/multi_kprobe_link_api                 # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
 exceptions					 # JIT does not support calling kfunc bpf_throw: -524
+exceptions_unwind				 # JIT does not support calling kfunc bpf_throw: -524
 fexit_sleep                                      # The test never returns. The remaining tests cannot start.
 kprobe_multi_bench_attach                        # needs CONFIG_FPROBE
 kprobe_multi_test                                # needs CONFIG_FPROBE
diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x
index 1a63996c0304..f09a73dee72c 100644
--- a/tools/testing/selftests/bpf/DENYLIST.s390x
+++ b/tools/testing/selftests/bpf/DENYLIST.s390x
@@ -1,5 +1,6 @@
 # TEMPORARY
 # Alphabetical order
 exceptions				 # JIT does not support calling kfunc bpf_throw				       (exceptions)
+exceptions_unwind			 # JIT does not support calling kfunc bpf_throw				       (exceptions)
 get_stack_raw_tp                         # user_stack corrupted user stack                                             (no backchain userspace)
 stacktrace_build_id                      # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2                   (?)
diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c b/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c
new file mode 100644
index 000000000000..78df037b60ea
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/exceptions_cleanup.c
@@ -0,0 +1,65 @@
+#include "bpf/bpf.h"
+#include "exceptions.skel.h"
+#include <test_progs.h>
+#include <network_helpers.h>
+
+#include "exceptions_cleanup.skel.h"
+#include "exceptions_cleanup_fail.skel.h"
+
+static void test_exceptions_cleanup_fail(void)
+{
+	RUN_TESTS(exceptions_cleanup_fail);
+}
+
+void test_exceptions_cleanup(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, ropts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);
+	struct exceptions_cleanup *skel;
+	int ret;
+
+	if (test__start_subtest("exceptions_cleanup_fail"))
+		test_exceptions_cleanup_fail();
+
+	skel = exceptions_cleanup__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "exceptions_cleanup__open_and_load"))
+		return;
+
+	ret = exceptions_cleanup__attach(skel);
+	if (!ASSERT_OK(ret, "exceptions_cleanup__attach"))
+		return;
+
+#define RUN_EXC_CLEANUP_TEST(name)                                      \
+	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.name), \
+				     &ropts);                           \
+	if (!ASSERT_OK(ret, #name ": return value"))                    \
+		return;                                                 \
+	if (!ASSERT_EQ(ropts.retval, 0xeB9F, #name ": opts.retval"))    \
+		return;                                                 \
+	ret = bpf_prog_test_run_opts(                                   \
+		bpf_program__fd(skel->progs.exceptions_cleanup_check),  \
+		&ropts);                                                \
+	if (!ASSERT_OK(ret, #name " CHECK: return value"))              \
+		return;                                                 \
+	if (!ASSERT_EQ(ropts.retval, 0, #name " CHECK: opts.retval"))   \
+		return;													\
+	skel->bss->only_count = 0;
+
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_num_iter);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_num_iter_mult);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_prog_dynptr_iter);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_obj);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_percpu_obj);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_ringbuf);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_reg);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_null_or_ptr_do_ptr);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_null_or_ptr_do_null);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_callee_saved);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_frame);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_loop_iterations);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_dead_code_elim);
+	RUN_EXC_CLEANUP_TEST(exceptions_cleanup_frame_dce);
+}
diff --git a/tools/testing/selftests/bpf/progs/exceptions_cleanup.c b/tools/testing/selftests/bpf/progs/exceptions_cleanup.c
new file mode 100644
index 000000000000..ccf14fe6bd1b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/exceptions_cleanup.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_endian.h>
+#include "bpf_misc.h"
+#include "bpf_kfuncs.h"
+#include "bpf_experimental.h"
+
+struct {
+    __uint(type, BPF_MAP_TYPE_RINGBUF);
+    __uint(max_entries, 8);
+} ringbuf SEC(".maps");
+
+enum {
+    RES_DYNPTR,
+    RES_ITER,
+    RES_REG,
+    RES_SPILL,
+    __RES_MAX,
+};
+
+struct bpf_resource {
+    int type;
+};
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __uint(max_entries, 1024);
+    __type(key, int);
+    __type(value, struct bpf_resource);
+} hashmap SEC(".maps");
+
+const volatile bool always_false = false;
+bool only_count = false;
+int res_count = 0;
+
+#define MARK_RESOURCE(ptr, type) ({ res_count++; bpf_map_update_elem(&hashmap, &(void *){ptr}, &(struct bpf_resource){type}, 0); });
+#define FIND_RESOURCE(ptr) ((struct bpf_resource *)bpf_map_lookup_elem(&hashmap, &(void *){ptr}) ?: &(struct bpf_resource){__RES_MAX})
+#define FREE_RESOURCE(ptr) bpf_map_delete_elem(&hashmap, &(void *){ptr})
+#define VAL 0xeB9F
+
+SEC("fentry/bpf_cleanup_resource")
+int BPF_PROG(exception_cleanup_mark_free, struct bpf_frame_desc_reg_entry *fd, void *ptr)
+{
+    if (fd->spill_type == STACK_INVALID)
+        bpf_probe_read_kernel(&ptr, sizeof(ptr), ptr);
+    if (only_count) {
+        res_count--;
+        return 0;
+    }
+    switch (fd->spill_type) {
+    case STACK_SPILL:
+        if (FIND_RESOURCE(ptr)->type == RES_SPILL)
+            FREE_RESOURCE(ptr);
+        break;
+    case STACK_INVALID:
+        if (FIND_RESOURCE(ptr)->type == RES_REG)
+            FREE_RESOURCE(ptr);
+        break;
+    case STACK_ITER:
+        if (FIND_RESOURCE(ptr)->type == RES_ITER)
+            FREE_RESOURCE(ptr);
+        break;
+    case STACK_DYNPTR:
+        if (FIND_RESOURCE(ptr)->type == RES_DYNPTR)
+            FREE_RESOURCE(ptr);
+        break;
+    }
+    return 0;
+}
+
+static long map_cb(struct bpf_map *map, void *key, void *value, void *ctx)
+{
+    int *cnt = ctx;
+
+    (*cnt)++;
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_check(struct __sk_buff *ctx)
+{
+    int cnt = 0;
+
+    if (only_count)
+        return res_count;
+    bpf_for_each_map_elem(&hashmap, map_cb, &cnt, 0);
+    return cnt;
+}
+
+SEC("tc")
+int exceptions_cleanup_prog_num_iter(struct __sk_buff *ctx)
+{
+    int i;
+
+    bpf_for(i, 0, 10) {
+        MARK_RESOURCE(&___it, RES_ITER);
+        bpf_throw(VAL);
+    }
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_prog_num_iter_mult(struct __sk_buff *ctx)
+{
+    int i, j, k;
+
+    bpf_for(i, 0, 10) {
+        MARK_RESOURCE(&___it, RES_ITER);
+        bpf_for(j, 0, 10) {
+            MARK_RESOURCE(&___it, RES_ITER);
+            bpf_for(k, 0, 10) {
+                MARK_RESOURCE(&___it, RES_ITER);
+                bpf_throw(VAL);
+            }
+        }
+    }
+    return 0;
+}
+
+__noinline
+static int exceptions_cleanup_subprog(struct __sk_buff *ctx)
+{
+    int i;
+
+    bpf_for(i, 0, 10) {
+        MARK_RESOURCE(&___it, RES_ITER);
+        bpf_throw(VAL);
+    }
+    return ctx->len;
+}
+
+SEC("tc")
+int exceptions_cleanup_prog_dynptr_iter(struct __sk_buff *ctx)
+{
+    struct bpf_dynptr rbuf;
+    int ret = 0;
+
+    bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf);
+    MARK_RESOURCE(&rbuf, RES_DYNPTR);
+    if (ctx->protocol)
+        ret = exceptions_cleanup_subprog(ctx);
+    bpf_ringbuf_discard_dynptr(&rbuf, 0);
+    return ret;
+}
+
+SEC("tc")
+int exceptions_cleanup_obj(struct __sk_buff *ctx)
+{
+    struct { int i; } *p;
+
+    p = bpf_obj_new(typeof(*p));
+    MARK_RESOURCE(&p, RES_SPILL);
+    bpf_throw(VAL);
+    return p->i;
+}
+
+SEC("tc")
+int exceptions_cleanup_percpu_obj(struct __sk_buff *ctx)
+{
+    struct { int i; } *p;
+
+    p = bpf_percpu_obj_new(typeof(*p));
+    MARK_RESOURCE(&p, RES_SPILL);
+    bpf_throw(VAL);
+    return !p;
+}
+
+SEC("tc")
+int exceptions_cleanup_ringbuf(struct __sk_buff *ctx)
+{
+    void *p;
+
+    p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    MARK_RESOURCE(&p, RES_SPILL);
+    bpf_throw(VAL);
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_reg(struct __sk_buff *ctx)
+{
+    void *p;
+
+    p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    MARK_RESOURCE(p, RES_REG);
+    bpf_throw(VAL);
+    if (p)
+        bpf_ringbuf_discard(p, 0);
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_null_or_ptr_do_ptr(struct __sk_buff *ctx)
+{
+    union {
+        void *p;
+        char buf[8];
+    } volatile p;
+    u64 z = 0;
+
+    __builtin_memcpy((void *)&p.p, &z, sizeof(z));
+    MARK_RESOURCE((void *)&p.p, RES_SPILL);
+    if (ctx->len)
+        p.p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    bpf_throw(VAL);
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_null_or_ptr_do_null(struct __sk_buff *ctx)
+{
+    union {
+        void *p;
+        char buf[8];
+    } volatile p;
+
+    p.p = 0;
+    MARK_RESOURCE((void *)p.buf, RES_SPILL);
+    if (!ctx->len)
+        p.p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    bpf_throw(VAL);
+    return 0;
+}
+
+__noinline static int mark_resource_subprog(u64 a, u64 b, u64 c, u64 d)
+{
+    MARK_RESOURCE((void *)a, RES_REG);
+    MARK_RESOURCE((void *)b, RES_REG);
+    MARK_RESOURCE((void *)c, RES_REG);
+    MARK_RESOURCE((void *)d, RES_REG);
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_callee_saved(struct __sk_buff *ctx)
+{
+    asm volatile (
+       "r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        r6 = r0;                        \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        r7 = r0;                        \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        r8 = r0;                        \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        r9 = r0;                        \
+        r1 = r6;                        \
+        r2 = r7;                        \
+        r3 = r8;                        \
+        r4 = r9;                        \
+        call mark_resource_subprog;     \
+        r1 = 0xeB9F;                    \
+        call bpf_throw;                 \
+    " : : __imm(bpf_ringbuf_reserve),
+          __imm_addr(ringbuf)
+      : __clobber_all);
+    mark_resource_subprog(0, 0, 0, 0);
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_callee_saved_noopt(struct __sk_buff *ctx)
+{
+    mark_resource_subprog(1, 2, 3, 4);
+    return 0;
+}
+
+__noinline int global_subprog_throw(struct __sk_buff *ctx)
+{
+    u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    bpf_throw(VAL);
+    return p ? *p : 0 + ctx->len;
+}
+
+__noinline int global_subprog(struct __sk_buff *ctx)
+{
+    u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    if (!p)
+        return ctx->len;
+    global_subprog_throw(ctx);
+    bpf_ringbuf_discard(p, 0);
+    return !!p + ctx->len;
+}
+
+__noinline static int static_subprog(struct __sk_buff *ctx)
+{
+    struct bpf_dynptr rbuf;
+    u64 *p, r = 0;
+
+    bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf);
+    p = bpf_dynptr_data(&rbuf, 0, 8);
+    if (!p)
+        goto end;
+    *p = global_subprog(ctx);
+    r += *p;
+end:
+    bpf_ringbuf_discard_dynptr(&rbuf, 0);
+    return r + ctx->len;
+}
+
+SEC("tc")
+int exceptions_cleanup_frame(struct __sk_buff *ctx)
+{
+    struct foo { int i; } *p = bpf_obj_new(typeof(*p));
+    int i;
+    only_count = 1;
+    res_count = 4;
+    if (!p)
+        return 1;
+    p->i = static_subprog(ctx);
+    i = p->i;
+    bpf_obj_drop(p);
+    return i + ctx->len;
+}
+
+SEC("tc")
+__success
+int exceptions_cleanup_loop_iterations(struct __sk_buff *ctx)
+{
+    struct { int i; } *f[50] = {};
+    int i;
+
+    only_count = true;
+
+    for (i = 0; i < 50; i++) {
+        f[i] = bpf_obj_new(typeof(*f[0]));
+        if (!f[i])
+            goto end;
+        res_count++;
+        if (i == 49) {
+            bpf_throw(VAL);
+        }
+    }
+end:
+    for (i = 0; i < 50; i++) {
+        if (!f[i])
+            continue;
+        bpf_obj_drop(f[i]);
+    }
+    return 0;
+}
+
+SEC("tc")
+int exceptions_cleanup_dead_code_elim(struct __sk_buff *ctx)
+{
+    void *p;
+
+    p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    if (!p)
+        return 0;
+    asm volatile (
+        "r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+    " ::: "r0");
+    bpf_throw(VAL);
+    bpf_ringbuf_discard(p, 0);
+    return 0;
+}
+
+__noinline int global_subprog_throw_dce(struct __sk_buff *ctx)
+{
+    u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    bpf_throw(VAL);
+    return p ? *p : 0 + ctx->len;
+}
+
+__noinline int global_subprog_dce(struct __sk_buff *ctx)
+{
+    u64 *p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    if (!p)
+        return ctx->len;
+    asm volatile (
+        "r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+    " ::: "r0");
+    global_subprog_throw_dce(ctx);
+    bpf_ringbuf_discard(p, 0);
+    return !!p + ctx->len;
+}
+
+__noinline static int static_subprog_dce(struct __sk_buff *ctx)
+{
+    struct bpf_dynptr rbuf;
+    u64 *p, r = 0;
+
+    bpf_ringbuf_reserve_dynptr(&ringbuf, 8, 0, &rbuf);
+    p = bpf_dynptr_data(&rbuf, 0, 8);
+    if (!p)
+        goto end;
+    asm volatile (
+        "r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+         r0 = r0;        \
+    " ::: "r0");
+    *p = global_subprog_dce(ctx);
+    r += *p;
+end:
+    bpf_ringbuf_discard_dynptr(&rbuf, 0);
+    return r + ctx->len;
+}
+
+SEC("tc")
+int exceptions_cleanup_frame_dce(struct __sk_buff *ctx)
+{
+    struct foo { int i; } *p = bpf_obj_new(typeof(*p));
+    int i;
+    only_count = 1;
+    res_count = 4;
+    if (!p)
+        return 1;
+    p->i = static_subprog_dce(ctx);
+    i = p->i;
+    bpf_obj_drop(p);
+    return i + ctx->len;
+}
+
+SEC("tc")
+int reject_slot_with_zero_vs_ptr_ok(struct __sk_buff *ctx)
+{
+    asm volatile (
+       "r7 = *(u32 *)(r1 + 0);          \
+        r0 = 0;                         \
+        *(u64 *)(r10 - 8) = r0;         \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        if r7 != 0 goto jump4;          \
+        call %[bpf_ringbuf_reserve];    \
+        *(u64 *)(r10 - 8) = r0;         \
+    jump4:                              \
+        r0 = 0;                         \
+        r1 = 0;                         \
+        call bpf_throw;                 \
+    " : : __imm(bpf_ringbuf_reserve),
+          __imm_addr(ringbuf)
+      : "memory");
+    return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c b/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c
new file mode 100644
index 000000000000..b3c70f92b35f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/exceptions_cleanup_fail.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+struct {
+    __uint(type, BPF_MAP_TYPE_RINGBUF);
+    __uint(max_entries, 8);
+} ringbuf SEC(".maps");
+
+SEC("?tc")
+__failure __msg("Unreleased reference")
+int reject_with_reference(void *ctx)
+{
+	struct { int i; } *f;
+
+	f = bpf_obj_new(typeof(*f));
+	if (!f)
+		return 0;
+	bpf_throw(0);
+	return 0;
+}
+
+SEC("?tc")
+__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry")
+int reject_slot_with_distinct_ptr(struct __sk_buff *ctx)
+{
+    void *p;
+
+    if (ctx->len) {
+        p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    } else {
+        p = bpf_obj_new(typeof(struct { int i; }));
+    }
+    bpf_throw(0);
+    return !p;
+}
+
+SEC("?tc")
+__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry")
+int reject_slot_with_distinct_ptr_old(struct __sk_buff *ctx)
+{
+    void *p;
+
+    if (ctx->len) {
+        p = bpf_obj_new(typeof(struct { int i; }));
+    } else {
+        p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    }
+    bpf_throw(0);
+    return !p;
+}
+
+SEC("?tc")
+__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry")
+int reject_slot_with_misc_vs_ptr(struct __sk_buff *ctx)
+{
+    void *p = (void *)bpf_ktime_get_ns();
+
+    if (ctx->protocol)
+        p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+    bpf_throw(0);
+    return !p;
+}
+
+SEC("?tc")
+__failure __msg("Unreleased reference")
+int reject_slot_with_misc_vs_ptr_old(struct __sk_buff *ctx)
+{
+    void *p = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+
+    if (ctx->protocol)
+        p = (void *)bpf_ktime_get_ns();
+    bpf_throw(0);
+    return !p;
+}
+
+SEC("?tc")
+__failure __msg("frame_desc: merge: failed to merge old and new frame desc entry")
+int reject_slot_with_invalid_vs_ptr(struct __sk_buff *ctx)
+{
+    asm volatile (
+       "r7 = r1;                        \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        r4 = *(u32 *)(r7 + 0);          \
+        r6 = *(u64 *)(r10 - 8);         \
+        if r4 == 0 goto jump;           \
+        call %[bpf_ringbuf_reserve];    \
+        r6 = r0;                        \
+    jump:                               \
+        r0 = 0;                         \
+        r1 = 0;                         \
+        call bpf_throw;                 \
+    " : : __imm(bpf_ringbuf_reserve),
+          __imm_addr(ringbuf)
+      : "memory");
+    return 0;
+}
+
+SEC("?tc")
+__failure __msg("Unreleased reference")
+int reject_slot_with_invalid_vs_ptr_old(struct __sk_buff *ctx)
+{
+    asm volatile (
+       "r7 = r1;                        \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        r6 = r0;                        \
+        r4 = *(u32 *)(r7 + 0);          \
+        if r4 == 0 goto jump2;          \
+        r6 = *(u64 *)(r10 - 8);         \
+    jump2:                              \
+        r0 = 0;                         \
+        r1 = 0;                         \
+        call bpf_throw;                 \
+    " : : __imm(bpf_ringbuf_reserve),
+          __imm_addr(ringbuf)
+      : "memory");
+    return 0;
+}
+
+SEC("?tc")
+__failure __msg("Unreleased reference")
+int reject_slot_with_zero_vs_ptr(struct __sk_buff *ctx)
+{
+    asm volatile (
+       "r7 = *(u32 *)(r1 + 0);          \
+        r1 = %[ringbuf] ll;             \
+        r2 = 8;                         \
+        r3 = 0;                         \
+        call %[bpf_ringbuf_reserve];    \
+        *(u64 *)(r10 - 8) = r0;         \
+        r0 = 0;                         \
+        if r7 != 0 goto jump3;          \
+        *(u64 *)(r10 - 8) = r0;         \
+    jump3:                              \
+        r0 = 0;                         \
+        r1 = 0;                         \
+        call bpf_throw;                 \
+    " : : __imm(bpf_ringbuf_reserve),
+          __imm_addr(ringbuf)
+      : "memory");
+    return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c
index dfd164a7a261..1e73200c6276 100644
--- a/tools/testing/selftests/bpf/progs/exceptions_fail.c
+++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c
@@ -182,19 +182,6 @@ int reject_with_rbtree_add_throw(void *ctx)
 	return 0;
 }
 
-SEC("?tc")
-__failure __msg("Unreleased reference")
-int reject_with_reference(void *ctx)
-{
-	struct foo *f;
-
-	f = bpf_obj_new(typeof(*f));
-	if (!f)
-		return 0;
-	bpf_throw(0);
-	return 0;
-}
-
 __noinline static int subprog_ref(struct __sk_buff *ctx)
 {
 	struct foo *f;
