Skip to content

userd: fix userspace buffer overflow when redirecting and restoring rc file - #310

Open
fanuverse wants to merge 1 commit into
bmax121:mainfrom
fanuverse:fix-rc-restore-overflow
Open

fanuverse wants to merge 1 commit into
bmax121:mainfrom
fanuverse:fix-rc-restore-overflow

Conversation

@fanuverse

@fanuverse fanuverse commented Sep 18, 2026

Copy link
Copy Markdown

Summary

Fix out-of-bounds heap write in openat hook caused by using array sizeof instead of string length, which corrupted Scudo chunk headers in Android 14 init.


Problem

In after_openat(), compat_copy_to_user passed sizeof(ORIGIN_RC_FILES[args->local.data3 - 1]) as the copy length. Because ORIGIN_RC_FILES is declared as const char [][64], sizeof evaluates to 64 bytes (0x40) rather than the actual string length.

When userspace (/system/bin/init) opens /system/etc/init/hw/init.rc (28 bytes with null terminator), LLVM libc++ allocates a heap buffer of ~32 bytes (as it exceeds the 23-byte SSO limit). Copying 64 bytes back into userspace causes an out-of-bounds 32-byte heap write into adjacent memory, corrupting Scudo allocator chunk headers and triggering an immediate SIGSEGV during init::Parser::ParseData() on Android 14.

Solution

  1. Restore exact string bounds in after_openat(): Copy strlen(origin_rc) + 1 bytes instead of sizeof(ORIGIN_RC_FILES[...]).
  2. Guard in-place overwrite in before_openat(): Verify strlen(origin_rc) + 1 >= sizeof(REPLACE_RC_FILE) before attempting an in-place compat_copy_to_user, safely falling back to copy_to_user_stack() for shorter targets like /init.rc (9 bytes).

How Has This Been Tested?

  • Boot tested on Android 14 target device.
  • Verified /system/bin/init successfully parses /system/etc/init/hw/init.rc without Scudo crashes.
  • Verified shorter paths (e.g. /init.rc) fall back to stack copy without overflow.

…c file

In after_openat(), compat_copy_to_user previously passed
sizeof(ORIGIN_RC_FILES[args->local.data3 - 1]) as the copy length.
Because ORIGIN_RC_FILES is declared as const char [][64], the sizeof
evaluated to 64 bytes (0x40) rather than the actual string length.

When userspace (/system/bin/init) opens /system/etc/init/hw/init.rc
(28 bytes with null terminator), LLVM libc++ allocates a heap buffer of
~32 bytes. Copying 64 bytes back into userspace caused a 32-byte heap
buffer overflow, corrupting adjacent Scudo allocator chunk headers and
triggering an immediate SIGSEGV during init::Parser::ParseData() on
Android 14.

Fix this by:
1. Copying strlen(origin_rc) + 1 bytes in after_openat() to restore
   only the exact string bounds without overflowing userspace memory.
2. In before_openat(), checking strlen(origin_rc) + 1 >= sizeof(REPLACE_RC_FILE)
   before attempting an in-place compat_copy_to_user, safely falling
   back to copy_to_user_stack() for shorter paths like /init.rc.
@fanuverse fanuverse closed this Sep 18, 2026
@fanuverse fanuverse reopened this Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant