From 56edd3c0f4e04e41edcbae26a5873a38bee69059 Mon Sep 17 00:00:00 2001 From: Matiq Date: Mon, 24 Aug 2026 02:22:55 +0300 Subject: [PATCH] 23l: DR exec-watch verified working end-to-end by micro-test on this kernel (SIGTRAP+DR6 correct); contradiction isolated to process/thread targeting in tracer, single remaining hypothesis documented with debug protocol --- handoff/NOTES_LEVEL.md | 14 ++++++++++++++ scripts/fnexec.py | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index ee9dd84..1b4fffa 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -2461,3 +2461,17 @@ resweep-CONT чурн); (в) по хиту — дамп rcx(bands)/rdx(scratch) (г) если рендер опять не стартовал — просто перезапустить (флак ~50%). После дампа bands[] в момент param-change: сравнить с R-кривой, восстановить G, декодировать потребителя через backtrace ([rsp]) из того же хита. + +### 23l (короткое): DR-механизм верифицирован микротестом; расхождение локализовано +Микротест /tmp/opencode/dtest.py на fork+exec ребёнке: DR0 exec-watch срабатывает +(SIGTRAP, rip==target, DR6 bit0) — ядро/CPU полностью поддерживают аппаратные +ловушки через POKEUSER 0x350/0x388. При этом fnexec с ранним армированием +(до создания экземпляра, наследование через TRACECLONE + армирование при +рождении каждого треда) даёт 0 попаданий на {529fe0, loop-head, design×2, +dispatcher, bidir} при подтверждённых рендерах и 8–12/8–12 армированных тредов. +Одновременно: disp32-скан показывает, что ВЕСЬ доступ к scratch/FIR идёт только +из декодированного цикла 52bxxx. Прямое противоречие ⇒ остался ровно один +неисключённый вариант: **трассируется не тот процесс/треды умирают до арма**. +Следующий шаг: в fnexec логировать КАЖДЫЙ stop (rip,dr6,pid) без фильтра + +счётчик всех SIGTRAP; если трапов нет вообще на тредах, которые заведомо +выполняют модульный код — сравнивать /proc/pid/task состав до/после. diff --git a/scripts/fnexec.py b/scripts/fnexec.py index 4f82168..bbf1193 100644 --- a/scripts/fnexec.py +++ b/scripts/fnexec.py @@ -89,7 +89,9 @@ def main(): time.sleep(0.001) pt(PTRACE_POKEUSER, host, DR_BASE + 0, 0x1802A24C0) pt(PTRACE_POKEUSER, host, DR_BASE + 8, 0x1802FA420) - pt(PTRACE_POKEUSER, host, DR7_OFF, 0x3) + pt(PTRACE_POKEUSER, host, DR_BASE + 16, 0x180535A70) + pt(PTRACE_POKEUSER, host, DR_BASE + 24, 0x18052D650) + pt(PTRACE_POKEUSER, host, DR7_OFF, 0xF) ok0 = pt(PTRACE_PEEKUSER, host, DR7_OFF) print('early arm dr7=%#x' % (ok0 or 0), flush=True) pt(PTRACE_CONT, host, 0, 0) @@ -168,7 +170,12 @@ def main(): if pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs)) is None: continue rip = regs.rip - (1 if False else 0) - if rip not in (0x180529FE0, 0x18052B550, 0x1802A24C0, 0x1802FA420): + if pid not in armed: + seized.add(pid) + if arm(pid): + armed.add(pid) + # re-read regs after arming? DR change does not touch GPRs + if rip not in (0x180529FE0, 0x18052B550, 0x1802A24C0, 0x1802FA420, 0x180535A70, 0x18052D650): pt(PTRACE_CONT, pid, 0, 0) continue if rip == 0x180529FE0: @@ -176,7 +183,7 @@ def main(): elif rip == 0x18052B550: n1 += 1 rec = dict(which='design' if rip in (0x1802A24C0, 0x1802FA420) - else ('529fe0' if hit529 else 'loop'), + else ('529fe0' if rip == 0x180529FE0 else 'loop'), rip=rip, rcx=regs.rcx, rdx=regs.rdx, cnt=regs.r8 & 0xffffffff, t=round(time.time() - t0, 3), tid=pid) @@ -202,6 +209,10 @@ def main(): pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs)) pt(PTRACE_CONT, pid, 0, 0) elif os.WIFSTOPPED(pid): + if pid not in armed and sig == signal.SIGTRAP: + seized.add(pid) + if arm(pid): + armed.add(pid) pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0) if fresh_t is None and os.path.exists(wav) and os.path.getmtime(wav) > wt0: fresh_t = time.time() - t0