Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

SUPERCALL_KSTORAGE_LIST_IDS writes to an unvalidated userspace pointer with a raw memcpy → kernel KE

オープン
#308 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 3 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
72/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
android, c, linux

調査の方向性

kernel/patch/common/supercall.c と kernel/patch/common/kstorage.c から始め、次に対応する lkm/supercall/kstorage.c パスを調べます。SUPERCALL_KSTORAGE_LIST_IDS を arg2 から list_kstorage_ids() まで追跡し、提供された reproducer を実行します。両方の影響を受けるパスで userspace の出力が安全に処理され、無効なポインタによる kernel 例外が発生しなくなれば完了です。

索引モデルが issue の本文から書いたものです。

説明

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. The idslen bound 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:

  1. 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);
}
  1. Additionally validate in list_kstorage_ids() before taking the memcpy path, 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_create on a Pixel).
  • kernel/patch/common/sucompat.c:481 (list_ap_mod_exclude) passes false too, but with a stack array, so that call site itself is fine — the bug is that the supercall path reuses the same false with an untrusted pointer.
  • I can produce the AEE dump and a disassembly of the faulting list_kstorage_ids frame if useful.
主要言語
C
スター
1.6k
フォーク
393
平均マージ
3日 23時間
マージ済み PR(30日)
7

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

bmax121/KernelPatch のほかの issue

bmax121/KernelPatch の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。