SUPERCALL_KSTORAGE_LIST_IDS writes to an unvalidated userspace pointer with a raw memcpy → kernel KE
Maintainer thường phản hồi trong vòng 3 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 72/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- android, c, linux
- Lĩnh vực
- operating-systems, security
Hướng nghiên cứu
Bắt đầu trong kernel/patch/common/supercall.c và kernel/patch/common/kstorage.c, sau đó kiểm tra đường dẫn tương ứng lkm/supercall/kstorage.c. Truy vết SUPERCALL_KSTORAGE_LIST_IDS từ arg2 đến list_kstorage_ids() và chạy reproducer được cung cấp. Hoàn tất khi đầu ra của userspace được xử lý an toàn trong cả hai đường dẫn bị ảnh hưởng và ngoại lệ kernel do con trỏ không hợp lệ không còn xảy ra.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
SUPERCALL_KSTORAGE_LIST_IDS (0x1043) forwards a userspace-supplied pointer into list_kstorage_ids() with data_is_user = false, so the kernel takes the raw memcpy() path instead of copy_to_user(). There is no access_ok() check anywhere in kstorage.c (verified: grep -c access_ok = 0). A bad or stale ids argument therefore makes the kernel write hashtable data to an arbitrary address in kernel context → data abort / KE.
Hit on a Redmi K50 Pro (MT6983, Android 14, KernelPatch 0.13.8 / 0xd08), reproducible kernel exception:
ZZ_INTERNAL: Kernel (KE),0,0,99,/data/vendor/core/,0,,KE at __memcpy+0x8c/0x180,Sat Sep 12 21:59:23 CST 2026
Root cause
kernel/patch/common/kstorage.c:
int list_kstorage_ids(int gid, long *ids, int idslen, bool data_is_user)
{
if (gid < 0 || gid >= KSTRORAGE_MAX_GROUP_NUM) return -ENOENT;
...
for (int b = 0; b < KSTORAGE_NBUCKETS; b++) {
hlist_for_each_entry_rcu(pos, &kstorage_groups[gid][b], hnode)
{
if (cnt >= idslen) goto out;
if (data_is_user) {
int cplen = compat_copy_to_user(ids + cnt, &pos->did, sizeof(pos->did));
if (cplen <= 0) { ...; goto out; }
} else {
memcpy(ids + cnt, &pos->did, sizeof(pos->did)); // <-- raw kernel write
}
cnt++;
}
}
...
}
kernel/patch/common/supercall.c — the userspace-reachable path passes false:
static long call_list_kstorage_ids(int gid, long *ids, int ids_len)
{
return list_kstorage_ids(gid, ids, ids_len, false); // ids is a __user pointer!
}
...
case SUPERCALL_KSTORAGE_LIST_IDS:
return call_list_kstorage_ids((int)arg1, (long *)arg2, (int)arg3);
arg2 comes straight from the syscall registers, i.e. it is an untrusted userspace value. It is then dereferenced with a plain memcpy() in kernel context, with no access_ok() and no data_is_user flag.
The same pattern exists in the kp_kstorage_list_ids port used by the LKM (lkm/supercall/kstorage.c), so current main is affected too, not just the kernel/patch/common tree.
Reproduction
Minimal reproducer (aarch64, run as a uid in the su allowlist, e.g. root):
/* supercall channel: __NR_supercall == __NR3264_truncate == 45 */
#define SUPERCALL_KSTORAGE_LIST_IDS 0x1043
#define KSTORAGE_EXCLUDE_LIST_GROUP 1
long ver_and_cmd(long cmd) {
/* MAJOR 0, MINOR 13, PATCH 8 -> 0x0d08 ; magic 0x1158 ; cmd in low 16 bits */
return ((long)0x0d08 << 32) | (0x1158 << 16) | (cmd & 0xFFFF);
}
long *ids = malloc(sizeof(long) * 512); /* or an intentionally bad pointer */
syscall(45, "su", ver_and_cmd(SUPERCALL_KSTORAGE_LIST_IDS),
KSTORAGE_EXCLUDE_LIST_GROUP, ids, 512);
Accidentally passing a non-pointer in the ids slot (e.g. two integers that were meant to be (ids, len)) reproduces the crash deterministically: the kernel memcpys into an address like 0x200 = 512 and faults.
Impact
- Availability: kernel data abort / KE → device hangs → forced long-press reboot (observed
ro.boot.bootreason: reboot_longkey), MTK rebuild + fsck on the next boot. - Security: this is effectively a kernel arbitrary-write primitive: an authenticated supercall caller (any uid in the su allowlist / manager uid) controls both the destination address and the written value (
pos->did, the stored kstorage key, i.e. attacker-chosen uid values). Writing 8 bytes at a controlled kernel address is directly usable for privilege escalation. Theidslenbound only limits the number of writes, not where they land. - Any KPM (including third-party ones) that calls the exported
list_kstorage_ids("...", ptr, len, false)with a userspace pointer inherits the same fault.
Suggested fix
In call_list_kstorage_ids() the data really is userspace, so either of these works:
- Pass
data_is_user = true(one-line fix):
static long call_list_kstorage_ids(int gid, long *ids, int ids_len)
{
- return list_kstorage_ids(gid, ids, ids_len, false);
+ return list_kstorage_ids(gid, ids, ids_len, true);
}
- Additionally validate in
list_kstorage_ids()before taking thememcpypath, so future callers cannot regress:
if (!data_is_user && !access_ok(ids, (size_t)idslen * sizeof(long)))
return -EFAULT;
and consider clamping idslen to the group size to also protect the VLA in list_ap_mod_exclude():
int list_ap_mod_exclude(uid_t *uids, int len)
{
long ids[len]; /* VLA sized by an unvalidated caller argument */
...
for (int i = 0; i < len; i++) uids[i] = (uid_t)ids[i]; /* writes len entries */
}
Environment
| Device | Redmi K50 Pro (22011211C, MT6983), Android 14 |
| Kernel | 5.10.209-android12-9-00019-g4ea09a298bb4-ab12292661 |
| Root | APatch, /data/adb/apd version 115032 |
| KernelPatch | 0xd08 (0.13.8), compile time 08:03:59 Aug 31 2026, config=android,release |
| Supercall | SUPERCALL_KSTORAGE_LIST_IDS = 0x1043, KSTORAGE_EXCLUDE_LIST_GROUP = 1 |
| Crash | KE at __memcpy+0x8c/0x180, 2026-09-12 21:59:23 CST |
Notes
- Not a duplicate of #227 (that one is
__sock_createon a Pixel). kernel/patch/common/sucompat.c:481(list_ap_mod_exclude) passesfalsetoo, but with a stack array, so that call site itself is fine — the bug is that the supercall path reuses the samefalsewith an untrusted pointer.- I can produce the AEE dump and a disassembly of the faulting
list_kstorage_idsframe if useful.
- Ngôn ngữ chính
- C
- Star
- 1.6k
- Fork
- 393
- Merge trung bình
- 3 ngày 23 giờ
- Pull request đã merge (30 ngày)
- 7
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của bmax121/KernelPatch
-
official LineageOS on nicepool: APpatch patching fails but only if done via recovery sideloadCó thể đã có người làm @bmax121 đã nhận 9 ngày trước. Đang mở
bmax121/KernelPatch#332 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 3 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
bmax121/KernelPatch#329 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 3 ngày
-
kernel stack overflow: handle_supercmd 与 supercmd_echo 各持 4KB 栈缓冲区嵌套,event 上报改写 argv 时栈耗尽 panicĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
bmax121/KernelPatch#323 ·
Maintainer thường phản hồi trong vòng 3 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
bmax121/KernelPatch#319 · 9 bình luận ·
Maintainer thường phản hồi trong vòng 3 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
bmax121/KernelPatch#306 ·
Maintainer thường phản hồi trong vòng 3 ngày
Tất cả issue của bmax121/KernelPatch
Issue tương tự
-
[mimalloc] update to 3.6.0Đang mởcategory:port-update
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
Maintainer thường phản hồi trong vòng 2 ngày
-
emerge failure MARK mark-unstable media-kit
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
macaroni-os/mark-issues#834 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 93/100
webarkit/WebARKitLib#107 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày