Skip to content

feat(uprobe): implement uprobe breakpoint support (#2150 phase 1) - #2163

Open
sparkzky wants to merge 2 commits into
DragonOS-Community:masterfrom
sparkzky:feat/uprobe-uretprobe
Open

feat(uprobe): implement uprobe breakpoint support (#2150 phase 1)#2163
sparkzky wants to merge 2 commits into
DragonOS-Community:masterfrom
sparkzky:feat/uprobe-uretprobe

Conversation

@sparkzky

Copy link
Copy Markdown
Member

关联 Issue

Refs #2150(阶段一:uprobe 断点探针)

uretprobe(阶段二)不在本 PR 范围,后续单独提交。

概述

实现用户态断点探针(uprobe),使 agentsight 能在用户态函数(如 SSL_read/SSL_write)入口挂探针捕获参数。本 PR 完成阶段一(断点探针),命中路径 #BP → XOL 单步 → #DB → 恢复 端到端打通。

设计决策

经盲区扫描 + 对抗评审纠正后的架构(关键点:不复用 kprobe 的内核缓冲区单步——CPL=3 时内核页不可执行):

  1. XOL 执行原指令:每个 mm 在用户态 slot 页执行原指令副本,RIP-relative 用 yaxpeax-x86 重定位
  2. 独立 per-mm 分发:irqsave SpinLock(非全局 KPROBE_MANAGER 锁 / 非 RwSem,命中路径关中断不可睡眠)
  3. 断点页复刻 do_wp_page 私有 COWcopy_page_as_normal + 单次 set_entry 原子帧替换 + rmap 账簿,每 mm 私有副本(writeback 不回写 0xcc 损坏 .so)
  4. 注册时预填 XOL slot:RIP-relative 位移溢出在注册时 fail-fast 返回 EINVAL(不在命中时 panic)
  5. 异常分发do_int3/do_debugis_from_user() 二分;未消费用户态 #BP 投递 SIGTRAP(TRAP_BRKPT)
  6. perf 接入:复用 PERF_TYPE_MAX(6),按 config1 name 含 / 区分 uprobe/kprobe;复用 BPF_PROG_TYPE_KPROBE

改动文件

模块 文件 说明
新建 crate kernel/crates/uprobe/ 架构无关核心 + x86 指令分析(复用 yaxpeax-x86)
mm 集成 kernel/src/mm/ucontext/uprobe.rs per-mm 表 + XOL 区 + 断点页安装(复刻 do_wp_page COW)
异常分发 kernel/src/exception/uprobe.rs #BP/#DB 用户态分发 + XOL 单步 + SIGTRAP + NEED_UPROBE
perf 接入 kernel/src/perf/uprobe.rs UprobePerfEvent + perf_event_open uprobe 分发臂
改动 interrupt/{trap,mod}.rsexception/mod.rsmm/ucontext/{address_space,inner,mod}.rsperf/mod.rsprocess/state.rs is_from_user 二分、ProbeArgs impl、NEED_UPROBE 位等
测试 user/apps/tests/dunitest/suites/normal/uprobe.cc gtest 套件(3 用例)

验证

  • make kernel0 error / 0 warning
  • cargo test -p uprobe7/7 通过(指令长度、RIP-relative 检测/重定位、位移溢出)
  • 独立 reviewer 核对评审 findings(F1-F10)全部满足,无 kprobe/fork regression;修复了验证发现的 2 个 bug:
    • 重复注册同一 probe_vaddr 读到 0xcc 当原指令 → 改为复用已有指令信息
    • RIP-relative 位移溢出在命中时 panic → 移到注册时 fail-fast
  • dunitest 编译通过;核心触发用例的运行时验证待 QEMU(作者将在其他机器测试)

命中流程

flowchart TD
    A["用户态执行到 0xcc"] -->|"#BP"| B["do_int3: is_from_user?"]
    B -->|是| C["uprobe_breakpoint_handler\nlock uprobe_list(irqsave)"]
    C --> D["跑 pre_handler + BPF\nrip=原探针址"]
    D --> E["取 entries[0] XOL slot\n(slot 注册时已预填)"]
    E --> F["rip→XOL slot + TF + NEED_UPROBE"]
    F --> G["iretq: 用户态执行原指令"]
    G -->|"TF 触发 #DB"| H["uprobe_debug_handler\nNEED_UPROBE?"]
    H -->|是| I["rip 回原址+insn_len\n清 TF → post_handler"]
    I --> J["正常继续"]
Loading

后续

  • 阶段二:uretprobe(栈返回地址改写 + trampoline 页)
  • 运行时集成验证(QEMU + agentsight sslsniff.bpf.c

@github-actions github-actions Bot added the enhancement New feature or request label Jul 31, 2026
@fslongjin

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c8c8a99c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kernel/src/perf/uprobe.rs
Comment thread kernel/crates/uprobe/src/arch/x86/mod.rs
Comment thread kernel/src/mm/ucontext/uprobe.rs
Comment thread kernel/src/mm/ucontext/uprobe.rs
Comment thread kernel/src/exception/uprobe.rs
@sparkzky
sparkzky force-pushed the feat/uprobe-uretprobe branch from 5c8c8a9 to dbeea45 Compare August 1, 2026 06:18
…#2150 phase 1)

Implement userspace breakpoint probes (uprobe), phase 1 of issue DragonOS-Community#2150,
enabling agentsight to instrument SSL_read/SSL_write entry points.

The design is XOL-based rather than reusing kprobe's kernel-buffer
single-step (impossible at CPL=3). Key pieces:

- per-mm uprobe table guarded by an irqsave SpinLock (not the global
  KPROBE_MANAGER lock nor the mm RwSem; the #BP/#DB hit path is IRQ-off)
- breakpoint page install replicates do_wp_page private COW:
  copy_page_as_normal + single atomic set_entry + rmap attach/detach
  + flush_tlb_range. No transient empty PTE; each mm gets a private copy
  so writeback never persists 0xcc into the shared page-cache (.so)
- XOL: a per-mm user slot page executes the saved instruction copy with
  RIP-relative relocation (yaxpeax-x86), validated at registration time
- do_int3/do_debug gain is_from_user() dispatch. The #BP handler runs
  pre_handler + BPF (rip kept as the original probe address), then jumps
  rip to the pre-filled XOL slot, sets TF and NEED_UPROBE. The #DB handler
  recognizes XOL completion via NEED_UPROBE and restores rip; unconsumed
  user #BP is delivered as SIGTRAP(TRAP_BRKPT)
- perf: PERF_TYPE_MAX dispatches to uprobe when the name contains '/';
  UprobePerfEvent mirrors KprobePerfEvent and reuses BPF_PROG_TYPE_KPROBE

Delivered in four batches: uprobe crate (x86 instruction analysis), mm
integration (per-mm table / XOL / breakpoint page), exception dispatch,
and perf attach.

Verified: `make kernel` builds with 0 error / 0 warning; `cargo test -p
uprobe` passes 7/7. An independent reviewer confirmed the F1-F10 review
findings are satisfied with no kprobe/fork regression, and flagged two
bugs that are fixed: re-registering the same probe_vaddr no longer reads
0xcc as the original instruction, and a RIP-relative displacement overflow
now fails fast at registration instead of panicking at hit time.

Out of scope: uretprobe (phase 2) and the QEMU runtime integration test.

Refs: DragonOS-Community#2150

test(uprobe): add dunitest suite for uprobe breakpoint probes

Add suites/normal/uprobe.cc covering the userspace perf_event_open
uprobe path (issue DragonOS-Community#2150 phase 1):
- RegisterAndTriggerSurvivesHit: perf_event_open(type=PERF_TYPE_MAX,
  config1=path, config2=offset) on the current process, then call the
  probed function and assert it survives the #BP -> XOL -> #DB -> resume
  hit path and returns the correct value
- InvalidPathIsRejected / InvalidOffsetIsRejected: error inputs return
  negative errno

Target offset is resolved from /proc/self/maps (executable segment +
file pgoff), so the suite works regardless of PIE layout.

Compiles cleanly via `make build-suites`; the gtest framework runs (the
two negative cases pass on host Linux; the core trigger case is
DragonOS-specific and is validated at runtime under QEMU).

Refs: DragonOS-Community#2150

fix(uprobe): resolve CI failures - format check and cross-arch build

- Apply rustfmt to uprobe integration code (reorder modules, imports,
  line width) to pass format-check on all arches
- Add #[cfg(target_arch = "x86_64")] gates to uprobe integration points
  (exception/perf/mm-ucontext modules, AddressSpace fields, fork path,
  perf dispatch arm) so riscv64/loongarch64 build succeeds
- Non-x86_64 perf dispatch returns ENOSYS for uprobe paths
- Fix unused_mut on phys_addr in fork path for non-x86_64
@sparkzky
sparkzky force-pushed the feat/uprobe-uretprobe branch from dbeea45 to db00ce5 Compare August 1, 2026 06:26
- Thread 1: add ptrace access check (check_process_vm_access) before
  taking a target mm for cross-process uprobe, preventing unprivileged
  users from instrumenting arbitrary processes
- Thread 2: reject control-flow instructions (call/jmp/ret/jcc/loop/int/
  syscall) at registration time — XOL cannot safely single-step them
- Thread 3: read_user_insn_bytes now continues into the next page when
  the probe is near a page boundary, returning real bytes instead of
  zero-padding that could decode to a different instruction
- Thread 5: build_xol_slot fills trailing slot bytes with int3 (0xcc) so
  that a racy unregister during the XOL single-step window re-triggers
  #BP instead of executing zero-filled garbage
@sparkzky
sparkzky force-pushed the feat/uprobe-uretprobe branch from 3a99e3d to 86f1f09 Compare August 1, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants