TL;DR Our team found a UAF vulnerability in Samsung's Android kernel. The vulnerability affected Samsung Android devices starting at Galaxy S9 through Galaxy S25, as well as additional devices (we tested S21, S22, S24, A54). Both Qualcomm and Exynos chipset based devices were impacted. The vulnerability could be exploited from any untrusted app, and allowed attackers to obtain multiple memory corruption primitives, potentially leading to complete device takeover. In this post we explain the vulnerability, and exploit primitives that can be derived from it. Note that this post does not describe a full LPE exploit. LucidBit Labs responsibly disclosed the issue to Samsung, and a fix was issued at the 01-2026 Samsung Android update. Background Samsung PROCA (process authenticator) is a proprietary security subsystem built into the kernel of Samsung devices. Its goal is preventing unauthorized processes from executing on Samsung devices. PROCA is a core part of KNOX, a security platform built into the hardware and software of Samsung devices. PROCA validates process authenticity using FIVE, Samsung’s kernel side integrity subsystem for tracking the trust state of files and the processes that use them. FIVE is based on Linux’s integrity-measurement model, and Samsung extends it. Under FIVE, each task has a task_integrity object attached to it, storing values such as the task’s current integrity level, user-visible integrity value, etc. That state is updated during normal process and file lifecycle events, including execve(), fork(), executable mappings, and selected integrity-related file operations. From a vulnerability research perspective, FIVE is an attractive target: widely deployed vendor kernel code, reachable through normal app-accessible interfaces, undocumented behavior, and nontrivial lifetime and state-management logic. The bug For the task_integrity object, the kernel exposes a procfs entry allowing user space to look up various information related to the integrity of a process. The procfs entries are exposed under /proc/pid/integrity/ and trigger handlers that access the provided pid's kernel integrity object. The handlers use this macro to fetch a task's (the kernel object representing a process/thread) integrity object: #define TASK_INTEGRITY(task) ((task)->integrity) The macro fetches a raw pointer, and the handlers use that pointer to look up the pid's integrity state without accounting for the integrity's lifetime. The task_integrity is a refcounted object, but no references are taken by the procfs handlers. During an exec() syscall, the running process is replaced by a newly loaded executable, within the same process (task) container. As part of this, the task_integrity also needs to be replaced, and that happens in __five_bprm_check() : old_tint = TASK_INTEGRITY(task); ... tint = task_integrity_alloc(); task_integrity_assign(task, tint); ... task_integrity_put(old_tint); // frees old object A new task_integrity is allocated and assigned, and the old one is freed. This also explains the likely reason why the bug exists - the above code first allocates and assigns a new integrity to a process, and only after that the old integrity object is freed. However, Android runs a fully preemptive kernel - a process can be scheduled out at nearly any time, so the following sequence of events is possible (in theory, for now): A process forks a child The parent reads the child's /proc/child pid/integrity/* , and triggers a handler that fetches the integrity object, then schedules out. The child calls execve() , triggering the creation of a new integrity, and freeing the old one. The parent resumes execution, and operates on the old (freed) integrity object. If you hit just the right timing (and add some scheduling magic powder), this scenario happens, leading to a various operations done on the freed integrity object's memory. Let's look at one of the handlers to see this more clearly: static int proc_integrity_value_read(struct seq_file *m, ..., struct task_struct *task) { seq_printf(m, "%x\n", task_integrity_user_read(TASK_INTEGRITY(task))); return 0; } The handler fetches the integrity pointer, and then calls task_integrity_user_read() on it. So this can happen: proc_integrity_value_read() takes the pointer via TASK_INTEGRITY(task) . proc_integrity_value_read() is scheduled out before performing task_integrity_user_read() . The target task executes execve() , specifically task_integrity_put(old_tint) , freeing the original struct. proc_integrity_value_read() resumes and calls task_integrity_user_read() with a pointer to freed memory. While relatively straightforward, the race window is tiny - the process running proc_integrity_value_read() needs to be scheduled out just at the right time (within a window consisting of a couple of opcodes), and be scheduled out for long enough to gain control of the memory. Structures Layout & Handlers Code To better understand the implications, let's look at the relevan...
A use-after-free vulnerability (CVE-2026-20971, CVSS 7.8 HIGH) in Samsung's proprietary PROCA/FIVE kernel subsystem allows any untrusted app to trigger memory corruption, potentially leading to full device compromise. The bug occurs because procfs handlers access a task's `task_integrity` object without taking a reference during an `exec()` syscall, where the old object can be freed. The vulnerability affects Samsung Android version 13.0, and a fix was issued in the January 2026 Samsung Android security update.