112507 reachability closed: notification registration refused (-300)
- New kern.ipc.lifecycle module: dead-name registration, dead-name send, notification receive, name reuse — single-threaded, safe on device - Device receipt (iPhone19,7, iOS 27.0.1, ad-hoc signed): request DEAD_NAME returns -300 (MIG_TYPE_ERROR); send -> MACH_SEND_INVALID_DEST; receive times out; 0/12,288 registrations armed in kern.ipc - macOS 27 sweep: 13 notification-type/right combinations, all -300 - kern.ipc now counts armed registrations and retracts the old 'survived' verdict when armed == 0; the request table is never armed - docs/KERNEL-RW-ASSESSMENT.md: 112507 marked closed for this app - app 2.2 build 5; 57 modules
This commit is contained in:
1 parent
c30aadf9bb
commit
6f4ed67c38
9 files changed
+217
-40
No files matched your search
+97
-9
@@ -40,15 +40,20 @@ enum KernelIPC {
|
||||
var deallocations: Int = 0
|
||||
var budgetExhausted = false
|
||||
var notificationsRefused = 0
|
||||
/// Registrations that actually succeeded. Zero means the request-table path was
|
||||
/// never armed, so the run says nothing about 112507.
|
||||
var armed = 0
|
||||
var kernelErrors: [String: Int] = [:]
|
||||
|
||||
var summary: String {
|
||||
var s = "ops=\(operations) alloc=\(allocations) notify=\(notifications) "
|
||||
s += "send=\(sends) dealloc=\(deallocations)"
|
||||
if budgetExhausted { s += tl("\n(达到资源预算,提前收敛 —— 这是刻意的,不是失败)") }
|
||||
if notificationsRefused > 0 {
|
||||
s += tl("\n通知注册被拒 ") + "\(notificationsRefused)" + tl(" 次 —— 该平台不允许对自身端口注册通知,")
|
||||
s += tl("请求表路径无法被武装。真机上应返回 KERN_SUCCESS。")
|
||||
if armed == 0 && notifications > 0 {
|
||||
s += tl("\n通知注册 0 次成功(内核返回 -300)—— 请求表路径从未被武装。")
|
||||
s += tl("这次运行不是 112507 测试:没有请求表项,就没有可被竞态消费的索引。")
|
||||
} else if notificationsRefused > 0 {
|
||||
s += tl("\n通知注册被拒 ") + "\(notificationsRefused)" + tl(" 次(成功 ") + "\(armed)" + tl(" 次)。")
|
||||
}
|
||||
if !kernelErrors.isEmpty {
|
||||
s += "\n" + kernelErrors.sorted { $0.value > $1.value }
|
||||
@@ -82,8 +87,8 @@ enum KernelIPC {
|
||||
/// send into the survivors, then release everything. Returns the counters it produced.
|
||||
private static func ring(task: mach_port_t, depth: Int, budget: inout Int,
|
||||
refused: inout Int,
|
||||
record: (kern_return_t) -> Void) -> (alloc: Int, notify: Int, send: Int, dealloc: Int) {
|
||||
var alloc = 0, notify = 0, send = 0, dealloc = 0
|
||||
record: (kern_return_t) -> Void) -> (alloc: Int, notify: Int, send: Int, dealloc: Int, armed: Int) {
|
||||
var alloc = 0, notify = 0, send = 0, dealloc = 0, armed = 0
|
||||
|
||||
var ports: [mach_port_name_t] = []
|
||||
ports.reserveCapacity(depth)
|
||||
@@ -109,7 +114,7 @@ enum KernelIPC {
|
||||
let kr1 = mach_port_request_notification(
|
||||
task, p, MACH_NOTIFY_SEND_POSSIBLE, 1, p,
|
||||
mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND), &prev)
|
||||
if kr1 != KERN_SUCCESS { record(kr1); if kr1 == KERN_INVALID_CAPABILITY { refused += 1 } }
|
||||
if kr1 == KERN_SUCCESS { armed += 1 } else { record(kr1); refused += 1 }
|
||||
if prev != 0 { _ = mach_port_mod_refs(task, prev, MACH_PORT_RIGHT_SEND, -1) }
|
||||
notify += 1
|
||||
|
||||
@@ -117,7 +122,7 @@ enum KernelIPC {
|
||||
let kr2 = mach_port_request_notification(
|
||||
task, p, MACH_NOTIFY_DEAD_NAME, 0, p,
|
||||
mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND), &prev2)
|
||||
if kr2 != KERN_SUCCESS { record(kr2); if kr2 == KERN_INVALID_CAPABILITY { refused += 1 } }
|
||||
if kr2 == KERN_SUCCESS { armed += 1 } else { record(kr2); refused += 1 }
|
||||
if prev2 != 0 { _ = mach_port_mod_refs(task, prev2, MACH_PORT_RIGHT_SEND, -1) }
|
||||
notify += 1
|
||||
}
|
||||
@@ -164,7 +169,7 @@ enum KernelIPC {
|
||||
}
|
||||
dealloc += 1
|
||||
}
|
||||
return (alloc, notify, send, dealloc)
|
||||
return (alloc, notify, send, dealloc, armed)
|
||||
}
|
||||
|
||||
/// Drive the interleaving for `seconds`, with a bounded number of workers and a hard
|
||||
@@ -184,7 +189,7 @@ enum KernelIPC {
|
||||
}
|
||||
|
||||
DispatchQueue.concurrentPerform(iterations: workers) { _ in
|
||||
var localAlloc = 0, localNotify = 0, localSend = 0, localDealloc = 0, localOps = 0
|
||||
var localAlloc = 0, localNotify = 0, localSend = 0, localDealloc = 0, localOps = 0, localArmed = 0
|
||||
|
||||
while Date() < deadline {
|
||||
lock.lock()
|
||||
@@ -205,6 +210,7 @@ enum KernelIPC {
|
||||
localNotify += result.notify
|
||||
localSend += result.send
|
||||
localDealloc += result.dealloc
|
||||
localArmed += result.armed
|
||||
localOps += 4
|
||||
|
||||
lock.lock()
|
||||
@@ -218,6 +224,7 @@ enum KernelIPC {
|
||||
stats.notifications += localNotify
|
||||
stats.sends += localSend
|
||||
stats.deallocations += localDealloc
|
||||
stats.armed += localArmed
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
@@ -255,4 +262,85 @@ enum KernelIPC {
|
||||
out["name_space"] = "\(nameCount)" + tl(" 已用 / 预算 ") + "\(liveBudget)"
|
||||
return out
|
||||
}
|
||||
|
||||
/// Single-threaded port lifecycle baseline: arm a DEAD_NAME notification, drop the
|
||||
/// receive right while the send right survives, send into the dead name, then receive
|
||||
/// the notification. No race, no frees of live request-table entries — this is the
|
||||
/// sequence the 112507 race has to stay correct through.
|
||||
///
|
||||
/// The second half frees the dead name and measures whether the kernel hands the same
|
||||
/// numeric name back to the next allocation. Name reuse is normal; the point is to
|
||||
/// record the baseline before the racer starts churning the namespace.
|
||||
static func lifecycle() -> (lines: [String], deadName: Bool, reused: Bool, requestKR: kern_return_t) {
|
||||
var lines: [String] = []
|
||||
let task = mach_task_self_
|
||||
var deadName = false
|
||||
var reused = false
|
||||
|
||||
var notify: mach_port_t = 0
|
||||
var kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, ¬ify)
|
||||
lines.append("notify_port alloc: \(name(kr)) name=\(notify)")
|
||||
guard kr == KERN_SUCCESS else { return (lines, false, false, kr) }
|
||||
|
||||
var target: mach_port_t = 0
|
||||
kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &target)
|
||||
lines.append("target receive: \(name(kr)) name=\(target)")
|
||||
guard kr == KERN_SUCCESS else { mach_port_destroy(task, notify); return (lines, false, false, kr) }
|
||||
|
||||
kr = mach_port_insert_right(task, target, target,
|
||||
mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND))
|
||||
lines.append("insert send right: \(name(kr))")
|
||||
|
||||
var prev: mach_port_t = 0
|
||||
let requestKR = mach_port_request_notification(task, target, MACH_NOTIFY_DEAD_NAME, 0, notify,
|
||||
mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND), &prev)
|
||||
kr = requestKR
|
||||
lines.append("request DEAD_NAME -> notify=\(notify): \(name(kr)) prev=\(prev)")
|
||||
if prev != 0 { _ = mach_port_mod_refs(task, prev, MACH_PORT_RIGHT_SEND, -1) }
|
||||
|
||||
kr = mach_port_mod_refs(task, target, MACH_PORT_RIGHT_RECEIVE, -1)
|
||||
lines.append("drop receive right: \(name(kr))")
|
||||
|
||||
var hdr = mach_msg_header_t()
|
||||
hdr.msgh_bits = mach_msg_bits_t(MACH_MSG_TYPE_COPY_SEND)
|
||||
hdr.msgh_size = mach_msg_size_t(MemoryLayout<mach_msg_header_t>.size)
|
||||
hdr.msgh_remote_port = target
|
||||
hdr.msgh_local_port = 0
|
||||
hdr.msgh_id = 0
|
||||
kr = mach_msg_send(&hdr)
|
||||
lines.append("send to dead name: \(name(kr))")
|
||||
|
||||
// DEAD_NAME fires when the last send right is released, not when the receive
|
||||
// right is dropped. Release it before waiting, otherwise the receive always
|
||||
// times out and the baseline records a false negative.
|
||||
kr = mach_port_deallocate(task, target)
|
||||
lines.append("release last send right: \(name(kr))")
|
||||
|
||||
var buffer = [UInt8](repeating: 0, count: 256)
|
||||
kr = buffer.withUnsafeMutableBytes { raw -> kern_return_t in
|
||||
let p = raw.baseAddress!.assumingMemoryBound(to: mach_msg_header_t.self)
|
||||
return mach_msg(p, mach_msg_option_t(MACH_RCV_MSG | MACH_RCV_TIMEOUT),
|
||||
0, mach_msg_size_t(raw.count), notify, 200, 0)
|
||||
}
|
||||
lines.append("receive notification: \(name(kr))")
|
||||
if kr == KERN_SUCCESS {
|
||||
let got = buffer.withUnsafeBytes {
|
||||
$0.baseAddress!.assumingMemoryBound(to: mach_msg_header_t.self).pointee
|
||||
}
|
||||
deadName = got.msgh_id == MACH_NOTIFY_DEAD_NAME
|
||||
lines.append(" id=0x\(String(got.msgh_id, radix: 16)) local=\(got.msgh_local_port) size=\(got.msgh_size) deadName=\(deadName)")
|
||||
}
|
||||
|
||||
for _ in 0..<64 {
|
||||
var p: mach_port_t = 0
|
||||
if mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &p) == KERN_SUCCESS {
|
||||
if p == target { reused = true; mach_port_destroy(task, p); break }
|
||||
mach_port_destroy(task, p)
|
||||
}
|
||||
}
|
||||
lines.append("name reuse of \(target): \(reused ? "yes" : "no")")
|
||||
|
||||
mach_port_destroy(task, notify)
|
||||
return (lines, deadName, reused, requestKR)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user