硬件断点只能在模块初始化时设置



我想在我的内核模块中使用register_wide_hw_breakpoint来观察我正在使用的内存页面的页面结构的变化(出于调试目的(。然而,我似乎只能在模块init函数中注册断点。如果我从ioctl处理程序中使用该函数,则该函数返回-1(EPERM?(。这篇博文让我觉得这应该是可能的。

我在Intel(R(Xeon(R(Silver 4215上运行5.1.0内核。

示例代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <linux/kallsyms.h>
static uint32_t val = 0;
void inc_val(void)
{
val++;
}
struct perf_event * __percpu *sample_hbp;
static void sample_hbp_handler(struct perf_event *bp, struct perf_sample_data *data, struct pt_regs *regs)
{
pr_info("My module: val changed!");
}
int test_hw_breakpoint(void)
{
struct perf_event_attr attr;
hw_breakpoint_init(&attr);
attr.bp_addr = (unsigned long)&val;
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W;
pr_info("My module: HW breakpoint at 0x%llxn", attr.bp_addr);
sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
if (IS_ERR((void __force *)sample_hbp))
{
int ret = PTR_ERR((void __force *)sample_hbp);
pr_info("My module: Breakpoint registration failed: %dn", ret);
return ret;
}
inc_val();
pr_info("My module: val: %d", val);
inc_val();
pr_info("My module: val: %d", val);
unregister_wide_hw_breakpoint(sample_hbp);
return 0;
}
static int mod_open(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
pr_info("My module: Opening...n");
return 0;
}
static int mod_release(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
pr_info("My module: Releasing...n");
return 0;
}

static long mod_ioctl(struct file *file, unsigned num, uintptr_t param)
{
(void)file;
(void)num;
(void)param;
pr_info("My module: Ioctl...n");
test_hw_breakpoint();
return 0;
}
static struct file_operations fops = {
.open = mod_open,
.release = mod_release,
.unlocked_ioctl = mod_ioctl,
};
static struct miscdevice mod_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mymod!io",
.fops = &fops,
.mode = 0666,
};
int mod_init(void)
{
int ret;
ret = misc_register(&mod_dev);
if (ret != 0)
return ret;
test_hw_breakpoint();
return 0;
}
void mod_exit(void)
{
misc_deregister(&mod_dev);
}
static int __init od_init(void)
{
int ret;
pr_info("My module: Initializing...n");
ret = mod_init();
if (ret != 0)
return -1;
return 0;
}
static void __exit od_exit(void)
{
pr_info("My module: Terminating...n");
mod_exit();
}
module_init(od_init)
module_exit(od_exit)
MODULE_LICENSE("GPL");

在dmesg中生成以下输出:

[  +0.031269] My module: Initializing...
[  +0.000086] My module: HW breakpoint at 0xffffffffc04ae4c8
[  +0.000191] My module: val changed!
[  +0.000002] My module: val: 1
[  +0.000003] My module: val changed!
[  +0.000001] My module: val: 2
[  +0.002405] My module: Opening...
[  +0.000003] My module: Ioctl...
[  +0.000003] My module: HW breakpoint at 0xffffffffc04ae4c8
[  +0.000019] My module: Breakpoint registration failed: -1
[  +0.000003] My module: Releasing...

我不知道在模块初始化时要观察的内存地址。有什么方法可以在运行时添加数据断点吗?

当进程发出系统调用时,它切换到内核模式,但内核代码在"过程上下文";。从理论上讲,这个进程在内核模式下运行时可以做任何事情。然而,典型的内核代码会检查调用进程是否有能力做它试图做的事情。一个进程有一个允许它做的事情的功能列表:请参阅功能(7(。

在OP的特定情况下,发出导致对register_wide_hw_breakpoint的调用的ioctl调用的无特权进程可能在"0"中的hw_breakpoint_parse函数检查失败时失败;kernel/events/hw_breakpoint.c":

if (arch_check_bp_in_kernelspace(hw)) {
if (attr->exclude_kernel)
return -EINVAL;
/*
* Don't let unprivileged users set a breakpoint in the trap
* path to avoid trap recursion attacks.
*/
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
}

最新更新