硬件断点Linux内核模块

问题描述 投票:0回答:1

我正在尝试编写内核模块,用于在给定地址设置硬件断点并在读/写访问时触发回调,寄存器看起来还不错,但在访问时没有触发回调,我完全迷失和沮丧,正在寻找任何可能提供建议或帮助。

代码:

#include "linux/hw_breakpoint.h"
#include "linux/printk.h"
#include "linux/sysctl.h"
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <linux/cpumask.h>
#include <linux/hw_breakpoint.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/ptrace.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>

DEFINE_PER_CPU(unsigned long *, cpu_dr0);
DEFINE_PER_CPU(unsigned long *, cpu_dr1);
DEFINE_PER_CPU(unsigned long *, cpu_dr2);
DEFINE_PER_CPU(unsigned long *, cpu_dr3);
DEFINE_PER_CPU(unsigned long *, cpu_dr6);

static unsigned long watch_address = 0;
static unsigned long test_var = 0;
module_param(watch_address, ulong, 0644);
MODULE_PARM_DESC(watch_address, "Memory address to set the watchpoint");

static struct kobject *watch_kobj;
static struct perf_event *__percpu *hw_breakpoint;
static struct task_struct *__other_task;

static void hw_breakpoint_handler(struct perf_event *bp,
                                  struct perf_sample_data *data,
                                  struct pt_regs *regs) {
  pr_info("Watchpoint triggered at address: 0x%lx\n", watch_address);
  dump_stack();
}

static void print_debug_registers(void) {
  unsigned long dr0, dr1, dr2, dr3, dr6, dr7;
  __asm__("mov %%dr0, %0" : "=r"(dr0));
  __asm__("mov %%dr1, %0" : "=r"(dr1));
  __asm__("mov %%dr2, %0" : "=r"(dr2));
  __asm__("mov %%dr3, %0" : "=r"(dr3));
  __asm__("mov %%dr6, %0" : "=r"(dr6));
  __asm__("mov %%dr7, %0" : "=r"(dr7));
  pr_info("Debug registers: \t\t\t\nDR0=0x%lx | DR1=0x%lx | DR2=0x%lx | "
          "\t\t\t\t\nDR3=0x%lx | DR6=0x%lx | DR7=0x%lx\n",
          dr0, dr1, dr2, dr3, dr6, dr7);
}

static int get_test_cpu(int num) {
  int cpu;

  WARN_ON(num < 0);

  for_each_online_cpu(cpu) {
    if (num-- <= 0)
      break;
  }

  return cpu;
}

static int get_hw_bp_slots(void) {
  static int slots;

  if (!slots)
    slots = hw_breakpoint_slots(TYPE_DATA);

  return slots;
}

static struct perf_event *set_watchpoint(int cpu) {
  if (get_hw_bp_slots() < 1) {
    printk("No available hardware breakpoint slots");
    return NULL;
  }
  pr_info("Setting watchpoint\n");
  print_debug_registers(); // Print registers before setting the watchpoint
  struct perf_event_attr attr = {};
  int ret;

  memset(&attr, 0, sizeof(struct perf_event_attr));
  attr.type = PERF_TYPE_BREAKPOINT;
  attr.size = sizeof(struct perf_event_attr);
  attr.bp_addr = (unsigned long)watch_address;
  attr.bp_len = HW_BREAKPOINT_LEN_8;
  attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;

  hw_breakpoint =
      register_wide_hw_breakpoint(&attr, hw_breakpoint_handler, NULL);
  if (IS_ERR(hw_breakpoint)) {
    ret = PTR_ERR(hw_breakpoint);
    hw_breakpoint = NULL;
    pr_err("Failed to set watchpoint: %d\n", ret);
    return NULL;
  }

  /*perf_event_create_kernel_counter(&attr, cpu, NULL, hw_breakpoint_handler,
                                   NULL //context);*/

  pr_info("Watchpoint set at address: 0x%lx\n", watch_address);

  print_debug_registers();
  return NULL;
}

static void clear_watchpoint(struct perf_event **bp) {
  if (WARN_ON(IS_ERR(*bp)))
    return;
  if (WARN_ON(!*bp))
    return;
  unregister_hw_breakpoint(*bp);
  *bp = NULL;
  pr_info("Watchpoint cleared at address: 0x%lx\n", watch_address);
}

static ssize_t watch_address_show(struct kobject *kobj,
                                  struct kobj_attribute *attr, char *buf) {
  return sprintf(buf, "%lx\n", watch_address);
}

static ssize_t watch_address_store(struct kobject *kobj,
                                   struct kobj_attribute *attr, const char *buf,
                                   size_t count) {
  int ret = kstrtoul(buf, 0, &watch_address);
  if (ret)
    return ret;

  clear_watchpoint(hw_breakpoint);
  set_watchpoint(get_test_cpu(0));

  return count;
}

static struct kobj_attribute watch_attr =
    __ATTR(watch_address, 0644, watch_address_show, watch_address_store);

static int __init watchpoint_init(void) {

  int ret;
  printk(KERN_INFO "Watchpoint driver loaded successfully.\n");

  watch_kobj = kobject_create_and_add("watchpoint", kernel_kobj);
  if (!watch_kobj)
    return -ENOMEM;

  ret = sysfs_create_file(watch_kobj, &watch_attr.attr);
  if (ret) {
    kobject_put(watch_kobj);
    return ret;
  }

  watch_address = (unsigned long)&test_var;

  if (watch_address) {
    if (watch_address % HW_BREAKPOINT_LEN_8 != 0) {
      pr_err("Watch address is not aligned correctly.\n");
      return -EINVAL;
    }
    set_watchpoint(get_test_cpu(0));
  }
  printk("size: %zu", sizeof(test_var));
  // Attempt to trigger the watchpoint
  printk("0x%lx\n", watch_address);
  test_var = 42;
  printk("test_var value after write: %lu\n", test_var);
  unsigned long check_read = test_var;
  printk("check_read: %lu\n", check_read);
  return 0;
}

static void __exit watchpoint_exit(void) {
  if (hw_breakpoint)
    clear_watchpoint(hw_breakpoint);
  if (watch_kobj) {
    sysfs_remove_file(watch_kobj, &watch_attr.attr);
    kobject_put(watch_kobj);
  }
  printk(KERN_INFO "Watchpoint driver exited.\n");
}

module_init(watchpoint_init);
module_exit(watchpoint_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Watchpoint Kernel Module");

不要 INSMOD,会导致 RMMOD 导致内核恐慌

所以日志是:

~ took 3s   sudo dmesg -wH
[Aug 5 13:44] Watchpoint driver loaded successfully.
[  +0.000004] Setting watchpoint
[  +0.000001] Debug registers:          
              DR0=0x0 | DR1=0x0 | DR2=0x0 |                 
              DR3=0x0 | DR6=0xfffe0ff0 | DR7=0x400
[  +0.025567] Watchpoint set at address: 0xffffffffc4e8d658
[  +0.000004] Debug registers:          
              DR0=0xffffffffc4e8d658 | DR1=0x0 | DR2=0x0 |              
              DR3=0x0 | DR6=0xfffe0ff0 | DR7=0xb0602
[  +0.000003] test_var value after write: 42
[  +0.000004] check_read: 42

我也不知道为什么它会导致内核恐慌,因为所有检查都是在任何删除/删除之前完成的......

内核恐慌日志(与

watchpoint_exit()
有关:

[  +0.000005] #PF: supervisor read access in kernel mode
[  +0.000002] #PF: error_code(0x0000) - not-present page
[  +0.000002] PGD 0 P4D 0 
[  +0.000003] Oops: Oops: 0000 [#1] PREEMPT SMP PTI
[  +0.000003] CPU: 1 PID: 8696 Comm: rmmod Tainted: P           OE      6.10.2-arch1-2 #1 bb2ff72914e58b5ee7d8bcebe7876e029bbab5bb
[  +0.000003] Hardware name: System manufacturer System Product Name/H110M-CS, BIOS 4210 07/03/2019
[  +0.000002] RIP: 0010:watchpoint_exit+0xc/0xd30 [watchpoint]
[  +0.000005] Code: d2 48 81 c4 90 00 00 00 c3 cc cc cc cc 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 53 48 8b 1d 6c f3 0d 00 <48> 8b 3b 48 81 ff 00 f0 ff ff 77 09 48 85 ff 75 08 0f 0b eb 21 0f
[  +0.000002] RSP: 0018:ffffa28b8b293aa0 EFLAGS: 00010282
[  +0.000003] RAX: ffffffffc4dae2d0 RBX: 00002cf6d9017418 RCX: 0000000000000002
[  +0.000002] RDX: 0000000000000000 RSI: ffffa28b8b293ad0 RDI: ffffffff98ad1060
[  +0.000002] RBP: 0000590b058827b8 R08: 0000000000000074 R09: 0000000000000000
[  +0.000001] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000800
[  +0.000002] R13: 00000000000000b0 R14: 0000000000000000 R15: 0000000000000000
[  +0.000002] FS:  00007f83eb9ce740(0000) GS:ffff9594a6c80000(0000) knlGS:0000000000000000
[  +0.000002] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  +0.000001] CR2: 00002cf6d9017418 CR3: 00000001a0e3e003 CR4: 00000000003706f0
[  +0.000002] DR0: ffffffffc4e8d658 DR1: 0000000000000000 DR2: 0000000000000000
[  +0.000002] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 00000000000b0602
[  +0.000002] Call Trace:
[  +0.000002]  <TASK>
[  +0.000002]  ? __die_body.cold+0x19/0x27
[  +0.000004]  ? page_fault_oops+0x15a/0x2d0
[  +0.000005]  ? exc_page_fault+0x81/0x190
[  +0.000004]  ? asm_exc_page_fault+0x26/0x30
[  +0.000004]  ? __pfx_watchpoint_exit+0x10/0x10 [watchpoint 59dabc72321c6c87b9b36c3c960546c26ce81bf3]
[  +0.000004]  ? watchpoint_exit+0xc/0xd30 [watchpoint 59dabc72321c6c87b9b36c3c960546c26ce81bf3]
[  +0.000004]  __do_sys_delete_module+0x1d1/0x310
[  +0.000005]  do_syscall_64+0x82/0x190
[  +0.000004]  ? xas_load+0xd/0xd0
[  +0.000003]  ? xa_load+0x7b/0xb0
[  +0.000004]  ? __memcg_slab_free_hook+0xf7/0x140
[  +0.000003]  ? __x64_sys_close+0x3c/0x80
[  +0.000004]  ? kmem_cache_free+0x3d5/0x400
[  +0.000003]  ? syscall_exit_to_user_mode+0x72/0x200
[  +0.000004]  ? do_syscall_64+0x8e/0x190
[  +0.000003]  ? syscall_exit_to_user_mode+0x72/0x200
[  +0.000003]  ? do_syscall_64+0x8e/0x190
[  +0.000003]  ? syscall_exit_to_user_mode+0x72/0x200
[  +0.000003]  ? do_syscall_64+0x8e/0x190
[  +0.000002]  ? __mod_memcg_lruvec_state+0xa6/0x150
[  +0.000003]  ? __lruvec_stat_mod_folio+0x68/0xa0
[  +0.000003]  ? set_ptes.isra.0+0x28/0x90
[  +0.000004]  ? do_anonymous_page+0xfa/0x830
[  +0.000002]  ? __pte_offset_map+0x1b/0x180
[  +0.000004]  ? __handle_mm_fault+0xad3/0x1050
[  +0.000004]  ? __count_memcg_events+0x58/0xf0
[  +0.000004]  ? count_memcg_events.constprop.0+0x1a/0x30
[  +0.000003]  ? handle_mm_fault+0x1f0/0x300
[  +0.000003]  ? do_user_addr_fault+0x36c/0x620
[  +0.000003]  ? exc_page_fault+0x81/0x190
[  +0.000003]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  +0.000004] RIP: 0033:0x7f83eb32946b
[  +0.000015] Code: 73 01 c3 48 8b 0d a5 c8 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 75 c8 0c 00 f7 d8 64 89 01 48
[  +0.000002] RSP: 002b:00007ffd975c2638 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
[  +0.000003] RAX: ffffffffffffffda RBX: 0000590b05882750 RCX: 00007f83eb32946b
[  +0.000001] RDX: 0000000000000000 RSI: 0000000000000800 RDI: 0000590b058827b8
[  +0.000002] RBP: 0000000000000000 R08: 1999999999999999 R09: 0000000000000000
[  +0.000001] R10: 00007f83eb3a5fe0 R11: 0000000000000206 R12: 00007ffd975c28a0
[  +0.000002] R13: 0000000000000000 R14: 0000590b058822a0 R15: 00007ffd975c28a8
[  +0.000004]  </TASK>
[  +0.000001] Modules linked in: watchpoint(OE-) cfg80211 snd_seq_dummy snd_hrtimer snd_seq vfat fat intel_rapl_msr intel_rapl_common intel_tcc_cooling x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm snd_soc_avs snd_soc_hda_codec crct10dif_pclmul snd_hda_ext_core crc32_pclmul polyval_clmulni snd_soc_core polyval_generic snd_hda_codec_realtek gf128mul snd_compress snd_hda_codec_generic ghash_clmulni_intel ac97_bus snd_hda_scodec_component snd_hda_codec_hdmi snd_pcm_dmaengine sha512_ssse3 snd_usb_audio sha256_ssse3 nvidia_drm(POE) sha1_ssse3 snd_hda_intel aesni_intel snd_intel_dspcfg nvidia_modeset(POE) iTCO_wdt snd_usbmidi_lib crypto_simd snd_intel_sdw_acpi intel_pmc_bxt snd_ump eeepc_wmi asus_wmi iTCO_vendor_support ee1004 cryptd snd_rawmidi snd_hda_codec platform_profile mei_pxp nvidia_uvm(POE) mei_hdcp ppdev r8169 snd_hda_core i8042 snd_seq_device rapl i2c_i801 sparse_keymap serio realtek snd_hwdep mc mdio_devres intel_cstate i2c_smbus intel_uncore rfkill pcspkr wmi_bmof snd_pcm i2c_mux libphy mei_me mei
[  +0.000053]  snd_timer snd intel_pmc_core soundcore parport_pc intel_vsec pmt_telemetry mousedev parport joydev acpi_pad pmt_class mac_hid nvidia(POE) crypto_user loop dm_mod nfnetlink ip_tables x_tables ext4 crc32c_generic crc16 mbcache jbd2 hid_generic usbhid mxm_wmi crc32c_intel xhci_pci xhci_pci_renesas video wmi
[  +0.000024] CR2: 00002cf6d9017418
[  +0.000002] ---[ end trace 0000000000000000 ]---
[  +0.000001] RIP: 0010:watchpoint_exit+0xc/0xd30 [watchpoint]
[  +0.000003] Code: d2 48 81 c4 90 00 00 00 c3 cc cc cc cc 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 53 48 8b 1d 6c f3 0d 00 <48> 8b 3b 48 81 ff 00 f0 ff ff 77 09 48 85 ff 75 08 0f 0b eb 21 0f
[  +0.000002] RSP: 0018:ffffa28b8b293aa0 EFLAGS: 00010282
[  +0.000002] RAX: ffffffffc4dae2d0 RBX: 00002cf6d9017418 RCX: 0000000000000002
[  +0.000001] RDX: 0000000000000000 RSI: ffffa28b8b293ad0 RDI: ffffffff98ad1060
[  +0.000002] RBP: 0000590b058827b8 R08: 0000000000000074 R09: 0000000000000000
[  +0.000001] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000800
[  +0.000002] R13: 00000000000000b0 R14: 0000000000000000 R15: 0000000000000000
[  +0.000001] FS:  00007f83eb9ce740(0000) GS:ffff9594a6c80000(0000) knlGS:0000000000000000
[  +0.000002] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  +0.000002] CR2: 00002cf6d9017418 CR3: 00000001a0e3e003 CR4: 00000000003706f0
[  +0.000001] DR0: ffffffffc4e8d658 DR1: 0000000000000000 DR2: 0000000000000000
[  +0.000002] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 00000000000b0602
[  +0.000001] note: rmmod[8696] exited with irqs disabled```

我已经尝试过,我已经用谷歌搜索过,但由于只剩下很少的时间来完成(这是面试任务),我真的很紧张,并且 100% 确定我错过了一些非常明显和愚蠢的东西。

c linux-kernel kernel-module systems-programming
1个回答
0
投票

正如我所建议的那样,这是非常愚蠢和明显的缺陷,我必须定义

attr.sample_period = 1;
© www.soinside.com 2019 - 2024. All rights reserved.