火花andlongjin 05a568fd39 feat(uprobe): implement uprobe breakpoint support (#2150 phase 1) (#2163)
* feat(uprobe): implement uprobe breakpoint support (#2150 phase 1)

Implement userspace breakpoint probes (uprobe), phase 1 of issue #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: #2150

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

Add suites/normal/uprobe.cc covering the userspace perf_event_open
uprobe path (issue #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: #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

* fix(uprobe): address P1 review findings (threads 1/2/3/5)

- 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

* fix(uprobe): address maintainer review (12 findings, R1-R12)

Security & correctness:
- R1: pid==-1 (system-wide) now requires CAP_SYS_PTRACE; pid<-1
  returns EINVAL. Per-pid path keeps check_process_vm_access.
- R4: user #DB not consumed by uprobe now falls through to the normal
  DebugException path (restores pre-PR master behavior for
  ptrace/hardware breakpoints/single-step).
- R5: original RFLAGS.TF is saved per-thread before XOL redirect and
  restored on completion (previously cleared unconditionally, silently
  disabling a program's own single-step mode).
- R6: old_instruction copy now covers the full decoded instruction
  across page boundaries (was limited to first-page remainder).
- R7: the breakpoint byte is restored only when the LAST consumer at
  that address unregisters (previously restored on every unregister,
  silently disabling remaining same-address consumers).
- R8: unregister writes the original byte on the CURRENT mapped page
  instead of remapping the registration-time page, preserving the
  program's own writes to other bytes of that page.
- R10: reject MOV SS (suppresses #DB) and POPF (overwrites RFLAGS/TF)
  at registration, in addition to control-flow instructions.
- R11a: honor perf_event_attr.disabled (event starts disabled).

Architecture (per-thread state, R2/R3/R12):
- ActiveXol per-thread state on the PCB: probe_vaddr, return_addr,
  orig_tf, xol_page_base. Saved at #BP before rip redirect, consumed
  at #DB: O(1) completion independent of uprobe_list (racy unregister
  between #BP and #DB no longer corrupts resume), abort path when rip
  is outside the XOL page (signal/fault diversion), callbacks run
  outside the per-mm spinlock.

Durable probe identity (R9):
- Global registry keyed by inode+offset with consumer ids. New file
  mappings (dlopen/mmap via file_mapping_with_file_ext) and fork get
  late-applied probes; exec starts with an empty table. Consumer close
  drops registry entries plus late handles (per-mm unregister via the
  existing UprobeHandle::Drop with R7/R8 semantics). fork inherits
  instances with privatized child breakpoint pages.

Verified: make kernel 0 error/0 warning; make fmt (clippy) clean;
cargo test -p uprobe 9/9; QEMU dunitest uprobe 6/6, exec_abi 6/6,
process_signal_fork 10/10.

* fix(uprobe): align perf lifecycle with Linux semantics

Match the uprobe PMU ABI with Linux for event type discovery, perf_event_attr validation, PID and CPU selection, path resolution, unsupported options, BPF attachment, and file descriptor behavior.

Refactor definitions, consumers, sites, and XOL state so canonical file identity, enable and disable transitions, concurrent teardown, callback snapshots, and instruction restoration have explicit ownership and locking rules.

Reconcile probes across mmap, mremap, mprotect, madvise, fork, exec, signal, fault, and exit paths while revalidating VMA identity after page faults and preserving strict error reporting for eligible mappings.

Add dunitest coverage for lifecycle control, invalid attributes, aliasing, relative paths, tmpfs-backed probes, multiple consumers, and unsupported operations.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): close teardown and task scope races

Filter task-scoped callbacks by exact task identity and make the breakpoint entry atomic with teardown rendezvous. Keep the kernel-owned XOL mapping immutable across VMA operations and reject unsafe repeated string instructions during registration.

Preserve MREMAP_FIXED failure atomicity and add guest coverage for repeated instructions and concurrent final-consumer teardown.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): serialize runtime snapshots and reject pushf

Serialize participant snapshot collection and publication, and release retired callbacks after leaving the IRQ-disabled registry lock.

Reject PUSHF/PUSHFQ in phase-1 XOL so the temporary trap flag cannot leak into user-visible flags. Preserve Linux MADV_DONTNEED per-VMA side-effect ordering while using range-indexed probe lookup.

Apply rustfmt to the touched perf, BPF, and ucontext sources.

Tests:

- make fmt FMT_CHECK=--check

- cargo test --manifest-path kernel/Cargo.toml -p uprobe

- make kernel

- DragonOS QEMU uprobe suite (20/20)

- DragonOS QEMU Madvise suite (1/1)

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): reject xbegin and deduplicate installs

Reject XBEGIN from phase-1 XOL execution because its relative abort target cannot be preserved by the current exact-end single-step state machine.

Move same-consumer idempotence checking into the existing mm write-locked registration path so concurrent enable and VMA apply operations cannot publish duplicate callbacks.

Keep LSS supported: Intel documents that it does not suppress events like MOV SS or POP SS, matching Linux 6.6.139.

Tests:

- make fmt FMT_CHECK=--check

- cargo test --manifest-path kernel/Cargo.toml -p uprobe

- make kernel

- DragonOS QEMU uprobe suite (21/21)

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): preserve XOL fault addresses

Report the original probed instruction in SIGFPE siginfo when a divide fault occurs during XOL execution.

Pass the registry snapshot consumer directly into installation so matching probes do not rescan the global registry for every site.

Add a DragonOS regression test for the fault address and use a lock-free atomic result channel in its signal handler.

Tests:

- make fmt

- make fmt FMT_CHECK=--check

- cargo test --manifest-path kernel/Cargo.toml -p uprobe

- make kernel

- DragonOS QEMU uprobe suite (22/22)

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): harden BPF attachment

Reject duplicate PERF_EVENT_IOC_SET_BPF attachments atomically with EEXIST, matching Linux semantics. Allocate size-aware JIT memory fallibly and release it on every failure path. Add regression coverage for duplicate attachment and large JIT programs.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): serialize fork breakpoint restoration

Restore inherited breakpoint bytes while the child address space is still write-locked, before file-rmap registration scans can enter it. Replay enabled consumers after publication and add bounded concurrent fork coverage for system-wide registration.

Signed-off-by: longjin <longjin@dragonos.org>

* perf(uprobe): scope task mapping scans

Enumerate only the target address space for task-scoped open and enable, while retaining inode file-rmap traversal for system-wide consumers.

Reuse the common enable lifecycle for initial activation and remove the duplicate open scan.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): preserve instruction mapping semantics

Synchronize same-page breakpoint publication across active CPUs, validate complete instruction mappings and bytes, and track instruction-tail VMA changes.

Drain and deduplicate consumer site references so repeated enable and disable does not grow stale attachment state.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): reject unsupported loop branches

Classify the complete x86 LOOP-family as unsupported control flow so phase-one XOL cannot retry a taken branch after mutating the counter.

Cover the decoder variants and perf event error contract with regression tests.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): expose perf hit counts

Provide the raw singleton perf read ABI for uprobe events, keep advanced read formats explicitly unsupported, and serialize counter shutdown with in-flight callbacks. Also remove empty address keys when breakpoint installation rolls back.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): validate relocation and perf attributes

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): preserve identity across VMA splits

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): close perf event lifecycle gaps

Signed-off-by: longjin <longjin@dragonos.org>

* refactor(uprobe): consolidate probe lifecycle architecture

Split the MM uprobe implementation into definition, consumer, site, reconciliation, and XOL ownership domains. Replace per-consumer site instances with one installed site per address and publish immutable RCU hit snapshots for the exception path.

Model enable, disable, close, task exit, and exec through one-shot consumer epochs with explicit install and delivery admission. Move the authoritative XOL phase into task-owned state, keep BPF/JIT ownership in the perf adapter, and prevent heavyweight teardown from escaping into interrupt or RCU contexts.

Restructure live VMA operations around fallible planning, precise probe withdrawal, infallible mutation, and best-effort reconciliation. Install probes for new executable mappings while holding the MM write guard and use a fixed-capacity mremap execution-publication transaction to close normal source and destination execution windows without post-commit allocation.

Extend dunitest coverage for lifecycle transitions, fork and exec interactions, mapping splits, MAP_FIXED, mprotect, madvise, mremap move and DONTUNMAP behavior, locked mappings, in-place growth, BPF delivery, counter reset, and concurrent execution.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): grow the per-mm XOL slot pool

Replace the fixed single-page XOL area with a lazily growing per-mm pool so registered probe sites are no longer capped at 256 addresses. Keep slot allocation and page creation on the registration path, preserving the allocation-free exception hot path and lifetime-pinned slots.

Reserve pool capacity before mapping each additional XOL page, protect every page from user VMA changes, and prefer the newest page while still reusing released slots. Add a guest regression that registers 257 distinct sites and executes the first site backed by the second XOL page.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): synchronize VMA publication and hit lookup

Keep uprobe state consistent across executable VMA mutations and high-volume site updates.

- split VMA validation, withdrawal, commit, locked reconciliation, and permission publication into explicit phases

- hold temporary execute/write barriers across mprotect, madvise, mmap, and mremap publication, including installed-site teardown windows

- revalidate stale user protection faults against stable leaf permissions without masking genuine protection faults

- replace full BTreeMap hit snapshots with a persistent Patricia index and serialize RCU publication with control mutations

- accept CAP_PERFMON alongside the legacy CAP_SYS_ADMIN capability

- add DragonOS regressions for permission transitions, VMA concurrency, DONTNEED, alias mappings, and capability checks

Signed-off-by: longjin <longjin@dragonos.org>

* fix(perf): harden probe inputs and remote writes

Bound dynamic probe names according to the Linux perf ABI and return E2BIG when uprobe paths or kprobe symbols are not terminated within their type-specific limits.

Rework process_vm_writev around remote writable-page acquisition: validate VM_WRITE, resolve remote write faults and COW, preserve private-page fork isolation, pin shared page-cache entries, publish dirty state before copying, and retain exact partial-write accounting. Reject writes to active read-only uprobe mappings instead of bypassing their protection.

Restore an original uprobe opcode only while the software breakpoint is still present, so teardown cannot overwrite a newer byte. Add dunitest coverage for bounded probe names, active uprobe write rejection, anonymous and private COW writes, shared-file persistence, and partial-copy semantics.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): stabilize instruction installation

Allocate XOL slots within the exact disp32 relocation range and keep the pool ordered so distant aliases neither reuse unreachable slots nor degrade into full-pool scans. Roll back unpublished XOL mappings on failed installation.

Build breakpoint COW pages from a stable source snapshot, validate the bytes that will actually be published, and defer RCU hit metadata until all fallible preparation has completed. Propagate Page contention so strict registration waits outside mm.write and then fully revalidates.

Reject SYSENTER and SYSEXIT probes, and return ENODEV only for offline per-CPU events while preserving Linux task-event semantics.

Add regression coverage for distant RIP-relative aliases and fast system-call instructions.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): keep fork replay best-effort

Keep inherited-breakpoint sanitization inside the fallible fork transaction, but treat consumer replay as observational work that cannot change fork's result.

Mapping and fork reconciliation now attempt contended pages once, while explicit consumer activation retains strict wait and rollback semantics.

Add a regression covering a privately modified MAP_PRIVATE instruction page under a system-wide uprobe.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): preserve rseq restart semantics across XOL

Expose the original probe address as rseq's logical instruction pointer while XOL is active. When rseq commits an abort redirect, consume the XOL state first so single-step completion cannot overwrite the abort target and the original trap flag and slot lease are restored correctly.

Add an x86 regression that forces preemption between the uprobe callback and XOL execution and distinguishes active-XOL aborts from legitimate pre-probe rseq aborts.

Signed-off-by: longjin <longjin@dragonos.org>

* perf(uprobe): bound reconciliation overhead

Scope executable publication barriers to consumers that permit the target address space and skip fork replay when no system-wide consumer is active, while retaining inherited breakpoint sanitization.

Replace per-mutation participant vector rebuilds with an RCU-published structurally shared chain, per-site membership liveness, and proportional compaction so registration and teardown no longer accumulate quadratic copying.

Add task-scoped fork coverage to verify that child execution is sanitized without inheriting event counts or disarming the parent site.

Signed-off-by: longjin <longjin@dragonos.org>

* perf(uprobe): scope exec reconciliation

Avoid full address-space replay when active uprobes cannot target the exec task. Reuse the task consumer index and system-wide activity counter to gate reconciliation without allocation.

Move replay out of the ELF loader and into stable exec success and recoverable rollback points, preserving task-scoped events across exec while closing concurrent enable publication gaps. Add a task-scoped exec regression test.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): retain sites across writable mprotect

Keep Linux's distinction between new uprobe installation eligibility and the lifetime of an existing site. A writable private VMA still rejects a new installation, while adding WRITE through mprotect no longer withdraws an already armed breakpoint. Removing WRITE continues to reconcile a persistent consumer.

Add dunitest coverage for both the retained-site transition and delayed installation after WRITE is removed, and remove the obsolete installed-site intersection helper.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): bound teardown and classify XOL completion

Index task-scoped consumers by their stable consumer IDs and remove only the known empty inode-offset bucket during teardown. This avoids quadratic scans while preserving weak ownership and existing lifecycle ordering.

Treat a Running XOL at its exact slot endpoint as completed even when a virtualized debug exception omits DR6.BS. Continue forwarding hardware breakpoint causes and reject ICEBP from XOL analysis.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(uprobe): quiesce events on final close

Close install and delivery admission synchronously when the final perf file reference is released, while leaving sleepable epoch draining, site teardown, and BPF retirement to the existing release worker. Split release admission from worker ownership so late ioctls and callbacks cannot outlive close.

Fix the admission guard's eager construction bug, which decremented a closed gate without a matching acquire and could leave teardown waiting forever. Index same-offset consumers by stable ID to reduce repeated close from quadratic irq-disabled scans to O(N log N), while preserving contiguous reconciliation snapshots.

Validated with make fmt, a full x86_64 kernel build, the 60-case uprobe dunitest suite, and a three-persona adversarial review.

Signed-off-by: longjin <longjin@dragonos.org>

* docs(uprobe): document architecture and execution model

Replace temporary implementation notes and planning material with reader-facing bilingual design documentation. Explain the file-offset identity model, per-mm sites, private breakpoint pages, XOL execution, VMA transactions, lifecycle ordering, performance characteristics, and Linux-compatible boundaries, and link both versions from the tracing indexes.

Signed-off-by: longjin <longjin@dragonos.org>

* fix(mm): serialize mremap mapping validation

Signed-off-by: longjin <longjin@dragonos.org>

---------

Signed-off-by: sparkzky <sparkhhhhhhhhhh@outlook.com>
Signed-off-by: longjin <longjin@dragonos.org>
Co-authored-by: longjin <longjin@dragonos.org>
2026-08-26 01:03:20 +08:00
2022-01-16 22:58:21 +08:00

dragonos-logo

Lightweight Cloud-Native Kernel

官网 bbs

DragonOS

Languages 中文|English  

DragonOS is a 64-bit operating system with a completely independent kernel, designed for lightweight cloud computing scenarios, offering Linux binary compatibility. It aims to provide lightweight, high-performance solutions for containerized workloads. Developed using Rust for enhanced reliability.

The DragonOS open-source community was established in July 2022 and is entirely commercially neutral. We warmly welcome interested developers and enthusiasts to join us!

DragonOS features excellent and comprehensive architectural design. Compared to other systems of similar scale, DragonOS supports eBPF and virtualization. Currently, we are actively advancing container support, cloud platform compatibility, RISC-V support, as well as porting compilers and application software. Our goal is to achieve large-scale production environment deployment within five years.

DragonOS is rapidly evolving under community-driven development. Currently, DragonOS has implemented approximately 1/4 of Linux interfaces. In the future, we will provide 100% Linux compatibility along with new features.

🌟 Want to quickly experience DragonOS? Visit DragonOS Playground to launch DragonOS in the cloud with zero configuration and experience the latest nightly build!

📰 Community News

  • 2025-12-17: ☁️ DragonOS Playground is now live! Experience DragonOS with zero configuration on CNB platform, with daily nightly build updates! Try it now →
  • 2025-11-20: 🚀 DragonOS 0.3.0 has been released! Now supports running Go programs directly!
  • 2025-11-18: 📊 DragonOS CI Dashboard is now live! Check out DragonOS Linux compatibility data now!

How to Run?

Zero configuration, one-click launch! Experience DragonOS on the CNB cloud-native development platform - the simplest and fastest way, no local dependencies required!

Launch on CNB

Method 2: Local Build

If you prefer to build and run DragonOS locally, you can refer to the following documentation:

Want to Contribute?

Read the DragonOS Community Introduction Document carefully to understand how the community operates and how you can contribute!

If you'd like to join us, check out the issues and participate in discussions or share your ideas. You can also visit the DragonOS forum to stay updated on development progress and tasks: https://bbs.dragonos.org.cn

You can also bring your creativity and ideas to discuss with the community and contribute new features to DragonOS.

Sites

How to Connect with the Community?

Please read the Contributor Guide~

  • You can find contact details for members of various committees in the Community Management Team section.
  • You can also locate the contact information for leaders of specific community groups via the SIGs and WGs pages.

Contributor List

Contributors to DragonOS-Community/DragonOS · GitHub

Sponsorship

Sponsor this project

DragonOS is a non-profit open-source project, and its development relies on financial support. All sponsors will be publicly acknowledged. Every contribution you make will help advance DragonOS!

Where Will Sponsorship Funds Be Used?

We guarantee that all sponsorship funds and items will be used for:

  • Event organization, cloud service expenses, and any other purposes beneficial to the development and growth of the DragonOS community.

🌟 Sponsor List

Special thanks to these generous financial supporters (in reverse chronological order):

CDN Sponsor

This project's CDN acceleration and security protection are sponsored by Tencent EdgeOne.

腾讯EdgeOne CDN

Individual Sponsors List

See Supporters.md

Open Source License Notice

This project is open-sourced under the GPLv2 license. You are welcome to use the code in compliance with the open-source license!

If you encounter any violations of the open-source license, we encourage you to email pmc@dragonos.org to report them. Let's work together to build a trustworthy open-source community.


👩💻 Contributors

"Open source shines because of you!"

Thanks to all developers who submitted code, fixed issues, or reviewed PRs:

S
Description
使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。DragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios.
Readme GPL-2.0
196 MiB
Languages
Rust 75.4%
C++ 16.1%
C 4.1%
Python 2%
Shell 1.6%
Other 0.7%