diff --git a/tools/testing/selftests/bpf/prog_tests/crib.c b/tools/testing/selftests/bpf/prog_tests/crib.c
new file mode 100644
index 000000000000..48c5156504ad
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/crib.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <test_progs.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include "crib_files_failure.skel.h"
+#include "crib_files_success.skel.h"
+
+struct files_test_args {
+	bool *setup_end;
+	bool *cr_end;
+};
+
+static int files_test_process(void *args)
+{
+	struct files_test_args *test_args = (struct files_test_args *)args;
+	int pipefd[2], sockfd, err = 0;
+
+	/* Create a clean file descriptor table for the test process */
+	close_range(0, ~0U, 0);
+
+	if (pipe(pipefd) < 0)
+		return 1;
+
+	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sockfd < 0) {
+		err = 2;
+		goto cleanup_pipe;
+	}
+
+	*test_args->setup_end = true;
+
+	while (!*test_args->cr_end)
+		;
+
+	close(sockfd);
+cleanup_pipe:
+	close(pipefd[0]);
+	close(pipefd[1]);
+	return err;
+}
+
+static void run_files_success_test(const char *prog_name)
+{
+	int prog_fd, child_pid, wstatus, err = 0;
+	const int stack_size = 1024 * 1024;
+	struct crib_files_success *skel;
+	struct files_test_args args;
+	struct bpf_program *prog;
+	bool setup_end, cr_end;
+	char *stack;
+
+	skel = crib_files_success__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	if (!ASSERT_OK(skel->bss->err, "pre_test_err"))
+		goto cleanup_skel;
+
+	prog = bpf_object__find_program_by_name(skel->obj, prog_name);
+	if (!ASSERT_OK_PTR(prog, "find_program_by_name"))
+		goto cleanup_skel;
+
+	prog_fd = bpf_program__fd(prog);
+	if (!ASSERT_GT(prog_fd, -1, "bpf_program__fd"))
+		goto cleanup_skel;
+
+	stack = (char *)malloc(stack_size);
+	if (!ASSERT_OK_PTR(stack, "clone_stack"))
+		return;
+
+	setup_end = false;
+	cr_end = false;
+
+	args.setup_end = &setup_end;
+	args.cr_end = &cr_end;
+
+	/* Note that there is no CLONE_FILES */
+	child_pid = clone(files_test_process, stack + stack_size, CLONE_VM | SIGCHLD, &args);
+	if (!ASSERT_GT(child_pid, -1, "child_pid"))
+		goto cleanup_stack;
+
+	while (!setup_end)
+		;
+
+	skel->bss->pid = child_pid;
+
+	err = bpf_prog_test_run_opts(prog_fd, NULL);
+	if (!ASSERT_OK(err, "prog_test_run"))
+		goto cleanup_stack;
+
+	cr_end = true;
+
+	if (!ASSERT_GT(waitpid(child_pid, &wstatus, 0), -1, "waitpid"))
+		goto cleanup_stack;
+
+	if (!ASSERT_OK(WEXITSTATUS(wstatus), "run_files_test_err"))
+		goto cleanup_stack;
+
+	ASSERT_OK(skel->bss->err, "run_files_test_failure");
+cleanup_stack:
+	free(stack);
+cleanup_skel:
+	crib_files_success__destroy(skel);
+}
+
+static const char * const files_success_tests[] = {
+	"test_bpf_iter_task_file",
+};
+
+void test_crib(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(files_success_tests); i++) {
+		if (!test__start_subtest(files_success_tests[i]))
+			continue;
+
+		run_files_success_test(files_success_tests[i]);
+	}
+
+	RUN_TESTS(crib_files_failure);
+}
diff --git a/tools/testing/selftests/bpf/progs/crib_common.h b/tools/testing/selftests/bpf/progs/crib_common.h
new file mode 100644
index 000000000000..93b8f9b1bdf8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/crib_common.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __CRIB_COMMON_H
+#define __CRIB_COMMON_H
+
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+extern struct task_struct *bpf_task_from_vpid(s32 vpid) __ksym;
+extern void bpf_task_release(struct task_struct *p) __ksym;
+
+struct bpf_iter_task_file;
+extern int bpf_iter_task_file_new(struct bpf_iter_task_file *it,
+		struct task_struct *task) __ksym;
+extern struct file *bpf_iter_task_file_next(struct bpf_iter_task_file *it) __ksym;
+extern int bpf_iter_task_file_get_fd(struct bpf_iter_task_file *it__iter) __ksym;
+extern void bpf_iter_task_file_destroy(struct bpf_iter_task_file *it) __ksym;
+
+#endif /* __CRIB_COMMON_H */
diff --git a/tools/testing/selftests/bpf/progs/crib_files_failure.c b/tools/testing/selftests/bpf/progs/crib_files_failure.c
new file mode 100644
index 000000000000..ebae01d87ff9
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/crib_files_failure.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "crib_common.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("syscall")
+__failure __msg("expected uninitialized iter_task_file as arg #1")
+int bpf_iter_task_file_new_inited_iter(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+	struct task_struct *task;
+
+	task = bpf_get_current_task_btf();
+
+	bpf_iter_task_file_new(&task_file_it, task);
+
+	bpf_iter_task_file_new(&task_file_it, task);
+
+	bpf_iter_task_file_destroy(&task_file_it);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Possibly NULL pointer passed to trusted arg1")
+int bpf_iter_task_file_new_untrusted_task(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+	struct task_struct *task = NULL;
+
+	bpf_iter_task_file_new(&task_file_it, task);
+
+	bpf_iter_task_file_destroy(&task_file_it);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Unreleased reference")
+int bpf_iter_task_file_no_destory(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+	struct task_struct *task;
+
+	task = bpf_get_current_task_btf();
+
+	bpf_iter_task_file_new(&task_file_it, task);
+
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("expected an initialized iter_task_file as arg #1")
+int bpf_iter_task_file_next_uninit_iter(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+
+	bpf_iter_task_file_next(&task_file_it);
+
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("expected an initialized iter_task_file as arg #1")
+int bpf_iter_task_file_get_fd_uninit_iter(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+
+	bpf_iter_task_file_get_fd(&task_file_it);
+
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("expected an initialized iter_task_file as arg #1")
+int bpf_iter_task_file_destroy_uninit_iter(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+
+	bpf_iter_task_file_destroy(&task_file_it);
+
+	return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/crib_files_success.c b/tools/testing/selftests/bpf/progs/crib_files_success.c
new file mode 100644
index 000000000000..92ca7d9d44c3
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/crib_files_success.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "crib_common.h"
+
+char _license[] SEC("license") = "GPL";
+
+int err, pid;
+
+SEC("syscall")
+int test_bpf_iter_task_file(void *ctx)
+{
+	struct bpf_iter_task_file task_file_it;
+	struct task_struct *task;
+	struct file *file;
+	int fd;
+
+	task = bpf_task_from_vpid(pid);
+	if (task == NULL) {
+		err = 1;
+		return 0;
+	}
+
+	bpf_iter_task_file_new(&task_file_it, task);
+
+	file = bpf_iter_task_file_next(&task_file_it);
+	if (file == NULL) {
+		err = 2;
+		goto cleanup;
+	}
+
+	fd = bpf_iter_task_file_get_fd(&task_file_it);
+	if (fd != 0) {
+		err = 3;
+		goto cleanup;
+	}
+
+	file = bpf_iter_task_file_next(&task_file_it);
+	if (file == NULL) {
+		err = 4;
+		goto cleanup;
+	}
+
+	fd = bpf_iter_task_file_get_fd(&task_file_it);
+	if (fd != 1) {
+		err = 5;
+		goto cleanup;
+	}
+
+	file = bpf_iter_task_file_next(&task_file_it);
+	if (file == NULL) {
+		err = 6;
+		goto cleanup;
+	}
+
+	fd = bpf_iter_task_file_get_fd(&task_file_it);
+	if (fd != 2) {
+		err = 7;
+		goto cleanup;
+	}
+
+	file = bpf_iter_task_file_next(&task_file_it);
+	if (file != NULL)
+		err = 7;
+
+cleanup:
+	bpf_iter_task_file_destroy(&task_file_it);
+	bpf_task_release(task);
+	return 0;
+}
