1use alloc::{
21 collections::BTreeMap,
22 sync::{Arc, Weak},
23 vec::Vec,
24};
25use core::{
26 fmt,
27 num::NonZeroI32,
28 sync::atomic::{AtomicI32, Ordering},
29};
30
31use ax_lazyinit::LazyInit;
32use ax_memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr, VirtAddrRange};
33use ax_runtime::hal::{
34 cpu::{KernelTrapFrame, UserRegisters},
35 paging::MappingFlags,
36};
37use kprobe::{
38 KprobeAuxiliaryOps, KretprobeBuilder, ProbeBuilder, ProbePointList,
39 register_kprobe as kprobe_crate_register_kprobe,
40 register_kretprobe as kprobe_crate_register_kretprobe, retprobe::RetprobeInstance,
41 unregister_kprobe as kprobe_crate_unregister_kprobe,
42 unregister_kretprobe as kprobe_crate_unregister_kretprobe,
43};
44
45use crate::{
46 StarryError, StarryResult,
47 sync::{IrqMutex, RawSpinNoIrq},
48 task::PidIdentity,
49};
50
51static NEXT_UPROBE_TARGET_ID: AtomicI32 = AtomicI32::new(1);
52static UPROBE_TARGETS: IrqMutex<BTreeMap<UprobeTargetId, Weak<PidIdentity>>> =
53 IrqMutex::new(BTreeMap::new());
54
55#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
57#[repr(transparent)]
58struct UprobeTargetId(NonZeroI32);
59
60impl UprobeTargetId {
61 fn allocate() -> StarryResult<Self> {
62 let id = NEXT_UPROBE_TARGET_ID.fetch_add(1, Ordering::Relaxed);
63 (id > 0)
64 .then(|| NonZeroI32::new(id).map(Self))
65 .flatten()
66 .ok_or(StarryError::NoMemory)
67 }
68
69 const fn get(self) -> i32 {
70 self.0.get()
71 }
72}
73
74pub(crate) struct UprobeTargetLease {
76 id: UprobeTargetId,
77 identity: Arc<PidIdentity>,
78}
79
80impl UprobeTargetLease {
81 pub(crate) fn register(identity: Arc<PidIdentity>) -> StarryResult<Self> {
82 let id = UprobeTargetId::allocate()?;
83 UPROBE_TARGETS.lock().insert(id, Arc::downgrade(&identity));
84 Ok(Self { id, identity })
85 }
86
87 pub(crate) const fn opaque_id(&self) -> i32 {
88 self.id.get()
89 }
90}
91
92impl fmt::Debug for UprobeTargetLease {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_struct("UprobeTargetLease")
95 .field("id", &self.id)
96 .field("identity_id", &self.identity.id())
97 .finish()
98 }
99}
100
101impl Drop for UprobeTargetLease {
102 fn drop(&mut self) {
103 UPROBE_TARGETS.lock().remove(&self.id);
104 }
105}
106
107fn uprobe_target_task(opaque_id: i32) -> crate::task::UserTaskRef {
108 let id = NonZeroI32::new(opaque_id)
109 .map(UprobeTargetId)
110 .expect("uprobe target handle must be non-zero");
111 let identity = UPROBE_TARGETS
112 .lock()
113 .get(&id)
114 .and_then(Weak::upgrade)
115 .expect("uprobe target generation is no longer registered");
116 identity
117 .live_task()
118 .expect("uprobe target task exited while probe remained armed")
119}
120
121pub type KernelRawMutex = RawSpinNoIrq;
133
134#[derive(Debug)]
135pub struct KernelKprobeOps;
136
137impl KprobeAuxiliaryOps for KernelKprobeOps {
138 fn copy_memory(src: *const u8, dst: *mut u8, len: usize, user_pid: Option<i32>) {
139 if let Some(pid) = user_pid {
140 let task = uprobe_target_task(pid);
149 let Ok(aspace) = task.as_thread().proc_data.pin_aspace() else {
150 warn!("kprobe copy_memory: target address space is retiring");
151 return;
152 };
153 let mm = aspace.lock();
154 let mut copied = 0;
155 while copied < len {
156 let vaddr = VirtAddr::from(src as usize + copied);
157 let Ok(paddr) = mm.translate(vaddr) else {
158 warn!(
159 "kprobe copy_memory: user addr {:#x} not mapped",
160 vaddr.as_usize()
161 );
162 return;
163 };
164 let page_off = vaddr.as_usize() & (PAGE_SIZE_4K - 1);
165 let chunk = core::cmp::min(len - copied, PAGE_SIZE_4K - page_off);
166 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
167 unsafe {
168 core::ptr::copy_nonoverlapping(kvaddr.as_ptr(), dst.add(copied), chunk);
169 }
170 copied += chunk;
171 }
172 } else {
173 unsafe {
174 core::ptr::copy_nonoverlapping(src, dst, len);
175 }
176 }
177 }
178
179 fn set_writeable_for_address<F: FnOnce(*mut u8)>(
180 address: usize,
181 len: usize,
182 user_pid: Option<i32>,
183 action: F,
184 ) {
185 if let Some(pid) = user_pid {
186 let task = uprobe_target_task(pid);
195 let Ok(aspace) = task.as_thread().proc_data.pin_aspace() else {
196 warn!("uprobe patch skipped: target address space is retiring");
197 return;
198 };
199 let mm = aspace.lock();
200 let vaddr = VirtAddr::from(address);
201 let paddr = mm
202 .translate(vaddr)
203 .expect("uprobe: target address not mapped");
204 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
205 action(kvaddr.as_mut_ptr());
206 ax_runtime::hal::cache::sync_kernel_text(vaddr.align_down_4k(), PAGE_SIZE_4K);
207 return;
208 }
209 let addr = VirtAddr::from(address);
210 crate::mm::patch_kernel_text(addr, len, action)
211 .expect("kprobe: set_writeable: patch kernel text failed");
212 }
213
214 fn alloc_kernel_exec_memory() -> *mut u8 {
215 let hint = ax_runtime::hal::mem::virtual_address_space()
216 .expect("kernel virtual address layout is initialized")
217 .kernel()
218 .start;
219 let vaddr = ax_runtime::kernel_mapping::allocate_kernel_range(
220 hint,
221 PAGE_SIZE_4K,
222 MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE,
223 true,
224 )
225 .expect("kprobe: map_alloc for exec memory failed");
226 vaddr.as_mut_ptr()
227 }
228
229 fn free_kernel_exec_memory(ptr: *mut u8) {
230 let vaddr = VirtAddr::from(ptr as usize);
231 ax_runtime::kernel_mapping::unmap_kernel_range(vaddr, PAGE_SIZE_4K)
232 .expect("kprobe: unmap exec memory failed");
233 }
234
235 fn alloc_user_exec_memory<F: FnOnce(*mut u8)>(pid: Option<i32>, action: F) -> *mut u8 {
236 let pid = pid.expect("uprobe: alloc_user_exec_memory needs a pid");
241 let task = uprobe_target_task(pid);
242 let Ok(aspace) = task.as_thread().proc_data.pin_aspace() else {
243 warn!("uprobe exec allocation rejected for a retiring address space");
244 return core::ptr::null_mut();
245 };
246 let mut mm = aspace.lock();
247 let range = VirtAddrRange::new(mm.base(), mm.end());
248 let vaddr = mm
249 .find_free_area(mm.base(), PAGE_SIZE_4K, range, PAGE_SIZE_4K)
250 .expect("uprobe: no free user va for exec memory");
251 let backend = crate::mm::MappingOperation::new_alloc(vaddr, PAGE_SIZE_4K, "uprobe-ols");
252 mm.map(
253 vaddr,
254 PAGE_SIZE_4K,
255 MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER,
256 true,
257 backend,
258 )
259 .expect("uprobe: map user exec memory failed");
260 let paddr = mm
261 .translate(vaddr)
262 .expect("uprobe: exec page not mapped after populate");
263 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
264 action(kvaddr.as_mut_ptr());
265 ax_runtime::hal::cache::sync_kernel_text(vaddr, PAGE_SIZE_4K);
266 vaddr.as_mut_ptr()
267 }
268
269 fn free_user_exec_memory(pid: Option<i32>, ptr: *mut u8) {
270 let pid = pid.expect("uprobe: free_user_exec_memory needs a pid");
271 let task = uprobe_target_task(pid);
272 let Ok(aspace) = task.as_thread().proc_data.pin_aspace() else {
273 warn!("uprobe exec free skipped for a retiring address space");
274 return;
275 };
276 let mut mm = aspace.lock();
277 mm.unmap(VirtAddr::from(ptr as usize), PAGE_SIZE_4K)
278 .expect("uprobe: unmap user exec memory failed");
279 }
280
281 fn insert_kretprobe_instance_to_task(instance: RetprobeInstance) {
282 if let Some(task) = crate::task::try_current_user_irq_view() {
283 task.push_kretprobe(instance);
284 return;
285 }
286 let Some(mut instances) = kernel_kretprobe_stack().try_lock() else {
287 panic!("nested kretprobe tried to re-enter the kernel stack");
288 };
289 if instances.len() == KERNEL_KRETPROBE_STACK_CAPACITY {
290 core::mem::forget(instance);
291 panic!("kernel task exceeded its fixed kretprobe nesting capacity");
292 }
293 instances.push(instance);
294 }
295
296 fn pop_kretprobe_instance_from_task() -> RetprobeInstance {
297 if let Some(task) = crate::task::try_current_user_irq_view() {
298 return task.pop_kretprobe();
299 }
300 let Some(mut instances) = kernel_kretprobe_stack().try_lock() else {
301 panic!("nested kretprobe tried to re-enter the kernel stack");
302 };
303 instances.pop().expect("kernel kretprobe stack underflow")
304 }
305}
306
307pub(crate) type KprobeManager = kprobe::ProbeManager<KernelRawMutex, KernelKprobeOps>;
308pub(crate) type KprobePointList = ProbePointList<KernelKprobeOps>;
309
310pub type KernelKprobe = kprobe::Kprobe<KernelRawMutex, KernelKprobeOps>;
313pub type KernelKretprobe = kprobe::Kretprobe<KernelRawMutex, KernelKprobeOps>;
315pub type KprobeAuxiliary = KernelKprobeOps;
317
318static KPROBE_MANAGER: KprobeManager = KprobeManager::new();
319static KPROBE_POINT_LIST: IrqMutex<KprobePointList> = IrqMutex::new(KprobePointList::new());
320const KERNEL_KRETPROBE_STACK_CAPACITY: usize = 64;
321static INSTANCE: LazyInit<IrqMutex<Vec<RetprobeInstance>>> = LazyInit::new();
322
323fn kernel_kretprobe_stack() -> &'static IrqMutex<Vec<RetprobeInstance>> {
324 INSTANCE
325 .get()
326 .expect("kernel kretprobe stack must be prepared before probes are armed")
327}
328
329fn with_manager<F, R>(f: F) -> R
330where
331 F: FnOnce(&KprobeManager) -> R,
332{
333 f(&KPROBE_MANAGER)
334}
335
336fn with_manager_and_list<F, R>(f: F) -> R
337where
338 F: FnOnce(&KprobeManager, &mut KprobePointList) -> R,
339{
340 let mut list = KPROBE_POINT_LIST.try_lock().unwrap();
341 f(&KPROBE_MANAGER, &mut list)
342}
343
344#[inline(never)]
346pub fn register_kprobe(builder: ProbeBuilder<KernelKprobeOps>) -> Arc<KernelKprobe> {
347 with_manager_and_list(|mgr, list| {
348 kprobe_crate_register_kprobe(mgr, list, builder).expect("Failed to register kprobe")
349 })
350}
351
352#[inline(never)]
354pub fn unregister_kprobe(kprobe: Arc<KernelKprobe>) {
355 with_manager_and_list(|mgr, list| kprobe_crate_unregister_kprobe(mgr, list, kprobe));
356}
357
358#[inline(never)]
360pub fn register_kretprobe(builder: KretprobeBuilder<KernelRawMutex>) -> Arc<KernelKretprobe> {
361 INSTANCE.get_or_init(|| IrqMutex::new(Vec::with_capacity(KERNEL_KRETPROBE_STACK_CAPACITY)));
362 with_manager_and_list(|mgr, list| {
363 kprobe_crate_register_kretprobe(mgr, list, builder).expect("Failed to register kretprobe")
364 })
365}
366
367#[inline(never)]
369pub fn unregister_kretprobe(kretprobe: Arc<KernelKretprobe>) {
370 with_manager_and_list(|mgr, list| kprobe_crate_unregister_kretprobe(mgr, list, kretprobe));
371}
372
373pub(crate) fn trapframe_to_ptregs(tf: &UserRegisters) -> kprobe::PtRegs {
374 #[cfg(target_arch = "x86_64")]
375 {
376 kprobe::PtRegs {
377 r15: tf.r15 as usize,
378 r14: tf.r14 as usize,
379 r13: tf.r13 as usize,
380 r12: tf.r12 as usize,
381 rbp: tf.rbp as usize,
382 rbx: tf.rbx as usize,
383 r11: tf.r11 as usize,
384 r10: tf.r10 as usize,
385 r9: tf.r9 as usize,
386 r8: tf.r8 as usize,
387 rax: tf.rax as usize,
388 rcx: tf.rcx as usize,
389 rdx: tf.rdx as usize,
390 rsi: tf.rsi as usize,
391 rdi: tf.rdi as usize,
392 orig_rax: tf.vector as usize,
393 rip: tf.rip as usize,
394 cs: tf.cs as usize,
395 rflags: tf.rflags as usize,
396 rsp: tf.rsp as usize,
397 ss: tf.ss as usize,
398 }
399 }
400 #[cfg(target_arch = "riscv64")]
401 {
402 kprobe::PtRegs {
403 epc: tf.sepc,
404 ra: tf.regs.ra,
405 sp: tf.regs.sp,
406 gp: tf.regs.gp,
407 tp: tf.regs.tp,
408 t0: tf.regs.t0,
409 t1: tf.regs.t1,
410 t2: tf.regs.t2,
411 s0: tf.regs.s0,
412 s1: tf.regs.s1,
413 a0: tf.regs.a0,
414 a1: tf.regs.a1,
415 a2: tf.regs.a2,
416 a3: tf.regs.a3,
417 a4: tf.regs.a4,
418 a5: tf.regs.a5,
419 a6: tf.regs.a6,
420 a7: tf.regs.a7,
421 s2: tf.regs.s2,
422 s3: tf.regs.s3,
423 s4: tf.regs.s4,
424 s5: tf.regs.s5,
425 s6: tf.regs.s6,
426 s7: tf.regs.s7,
427 s8: tf.regs.s8,
428 s9: tf.regs.s9,
429 s10: tf.regs.s10,
430 s11: tf.regs.s11,
431 t3: tf.regs.t3,
432 t4: tf.regs.t4,
433 t5: tf.regs.t5,
434 t6: tf.regs.t6,
435 status: tf.sstatus.bits(),
436 badaddr: 0,
437 cause: 0,
438 orig_a0: tf.regs.a0,
439 }
440 }
441 #[cfg(target_arch = "aarch64")]
442 {
443 kprobe::PtRegs {
444 regs: tf.x,
445 sp: 0, pc: tf.elr,
447 pstate: tf.spsr,
448 orig_x0: tf.x[0],
449 syscallno: -1,
450 unused2: 0,
451 }
452 }
453 #[cfg(target_arch = "loongarch64")]
454 {
455 kprobe::PtRegs {
456 regs: [
457 tf.regs.zero,
458 tf.regs.ra,
459 tf.regs.tp,
460 tf.regs.sp,
461 tf.regs.a0,
462 tf.regs.a1,
463 tf.regs.a2,
464 tf.regs.a3,
465 tf.regs.a4,
466 tf.regs.a5,
467 tf.regs.a6,
468 tf.regs.a7,
469 tf.regs.t0,
470 tf.regs.t1,
471 tf.regs.t2,
472 tf.regs.t3,
473 tf.regs.t4,
474 tf.regs.t5,
475 tf.regs.t6,
476 tf.regs.t7,
477 tf.regs.t8,
478 tf.regs.u0,
479 tf.regs.fp,
480 tf.regs.s0,
481 tf.regs.s1,
482 tf.regs.s2,
483 tf.regs.s3,
484 tf.regs.s4,
485 tf.regs.s5,
486 tf.regs.s6,
487 tf.regs.s7,
488 tf.regs.s8,
489 ],
490 orig_a0: 0,
491 csr_era: tf.era,
492 csr_badvaddr: 0,
493 csr_crmd: 0,
494 csr_prmd: tf.prmd,
495 csr_euen: 0,
496 csr_ecfg: 0,
497 csr_estat: 0,
498 }
499 }
500}
501
502pub(crate) fn ptregs_write_back(pt: &kprobe::PtRegs, tf: &mut UserRegisters) {
503 #[cfg(target_arch = "x86_64")]
504 {
505 tf.r15 = pt.r15 as u64;
506 tf.r14 = pt.r14 as u64;
507 tf.r13 = pt.r13 as u64;
508 tf.r12 = pt.r12 as u64;
509 tf.rbp = pt.rbp as u64;
510 tf.rbx = pt.rbx as u64;
511 tf.r11 = pt.r11 as u64;
512 tf.r10 = pt.r10 as u64;
513 tf.r9 = pt.r9 as u64;
514 tf.r8 = pt.r8 as u64;
515 tf.rax = pt.rax as u64;
516 tf.rcx = pt.rcx as u64;
517 tf.rdx = pt.rdx as u64;
518 tf.rsi = pt.rsi as u64;
519 tf.rdi = pt.rdi as u64;
520 tf.rip = pt.rip as u64;
521 tf.cs = pt.cs as u64;
522 tf.vector = pt.orig_rax as u64;
523 tf.rflags = pt.rflags as u64;
524 tf.rsp = pt.rsp as u64;
525 tf.ss = pt.ss as u64;
526 }
527 #[cfg(target_arch = "riscv64")]
528 {
529 tf.sepc = pt.epc;
530 tf.regs.ra = pt.ra;
531 tf.regs.sp = pt.sp;
532 tf.regs.gp = pt.gp;
533 tf.regs.tp = pt.tp;
534 tf.regs.t0 = pt.t0;
535 tf.regs.t1 = pt.t1;
536 tf.regs.t2 = pt.t2;
537 tf.regs.s0 = pt.s0;
538 tf.regs.s1 = pt.s1;
539 tf.regs.a0 = pt.a0;
540 tf.regs.a1 = pt.a1;
541 tf.regs.a2 = pt.a2;
542 tf.regs.a3 = pt.a3;
543 tf.regs.a4 = pt.a4;
544 tf.regs.a5 = pt.a5;
545 tf.regs.a6 = pt.a6;
546 tf.regs.a7 = pt.a7;
547 tf.regs.s2 = pt.s2;
548 tf.regs.s3 = pt.s3;
549 tf.regs.s4 = pt.s4;
550 tf.regs.s5 = pt.s5;
551 tf.regs.s6 = pt.s6;
552 tf.regs.s7 = pt.s7;
553 tf.regs.s8 = pt.s8;
554 tf.regs.s9 = pt.s9;
555 tf.regs.s10 = pt.s10;
556 tf.regs.s11 = pt.s11;
557 tf.regs.t3 = pt.t3;
558 tf.regs.t4 = pt.t4;
559 tf.regs.t5 = pt.t5;
560 tf.regs.t6 = pt.t6;
561 }
562 #[cfg(target_arch = "aarch64")]
563 {
564 tf.x = pt.regs;
565 tf.elr = pt.pc;
566 tf.spsr = pt.pstate;
567 }
568 #[cfg(target_arch = "loongarch64")]
569 {
570 tf.regs.zero = pt.regs[0];
571 tf.regs.ra = pt.regs[1];
572 tf.regs.tp = pt.regs[2];
573 tf.regs.sp = pt.regs[3];
574 tf.regs.a0 = pt.regs[4];
575 tf.regs.a1 = pt.regs[5];
576 tf.regs.a2 = pt.regs[6];
577 tf.regs.a3 = pt.regs[7];
578 tf.regs.a4 = pt.regs[8];
579 tf.regs.a5 = pt.regs[9];
580 tf.regs.a6 = pt.regs[10];
581 tf.regs.a7 = pt.regs[11];
582 tf.regs.t0 = pt.regs[12];
583 tf.regs.t1 = pt.regs[13];
584 tf.regs.t2 = pt.regs[14];
585 tf.regs.t3 = pt.regs[15];
586 tf.regs.t4 = pt.regs[16];
587 tf.regs.t5 = pt.regs[17];
588 tf.regs.t6 = pt.regs[18];
589 tf.regs.t7 = pt.regs[19];
590 tf.regs.t8 = pt.regs[20];
591 tf.regs.u0 = pt.regs[21];
592 tf.regs.fp = pt.regs[22];
593 tf.regs.s0 = pt.regs[23];
594 tf.regs.s1 = pt.regs[24];
595 tf.regs.s2 = pt.regs[25];
596 tf.regs.s3 = pt.regs[26];
597 tf.regs.s4 = pt.regs[27];
598 tf.regs.s5 = pt.regs[28];
599 tf.regs.s6 = pt.regs[29];
600 tf.regs.s7 = pt.regs[30];
601 tf.regs.s8 = pt.regs[31];
602 tf.era = pt.csr_era;
603 tf.prmd = pt.csr_prmd;
604 }
605}
606
607pub fn handle_breakpoint(tf: &mut KernelTrapFrame<'_>) -> bool {
608 let mut updated = tf.snapshot();
609 let mut pt_regs = trapframe_to_ptregs(&updated);
610 let handled = with_manager(|manager| kprobe::kprobe_handler_from_break(manager, &mut pt_regs));
611 if handled.is_some() {
612 ptregs_write_back(&pt_regs, &mut updated);
613 tf.apply_registers(&updated);
614 return true;
615 }
616 false
617}
618
619#[cfg(target_arch = "x86_64")]
620pub fn handle_debug(tf: &mut KernelTrapFrame<'_>) -> bool {
621 let mut updated = tf.snapshot();
622 let mut pt_regs = trapframe_to_ptregs(&updated);
623 let handled = with_manager(|manager| kprobe::kprobe_handler_from_debug(manager, &mut pt_regs));
624 if handled.is_some() {
625 ptregs_write_back(&pt_regs, &mut updated);
626 tf.apply_registers(&updated);
627 return true;
628 }
629 false
630}