【问题标题】:Why kernel get panic here after accessing system call table in a kernel module for 32 bit systems?为什么内核在访问 32 位系统的内核模块中的系统调用表后会出现恐慌?
【发布时间】:2015-11-29 04:39:26
【问题描述】:

我有一个内核模块,它删除了写保护位来修改系统调用表,以便挂钩任何系统调用——比如sys_rmdir。该模块在 64 位系统上按预期工作,但对于 32 位系统,为什么内核在访问 系统调用表的地址后会出现恐慌并给出 oops

此时模块有问题:

#ifdef __x86_64__
#define CR0_WP 0x00010000   // Write Protect Bit (CR0:16)
#else
#define CR0_WP 0x10000      // Write Protect Bit (CR0:16)
#endif

...

unsigned long cr0 = read_cr0();
write_cr0(cr0 & ~CR0_WP);

...

unsigned long addr = (unsigned long)syscall_table; //Where syscall_table is c059a170, same exists in /proc/kallsyms
if(set_memory_rw(PAGE_ALIGN(addr) - PAGE_SIZE, 3))
{
        ...
        return -1;
}

orig_sys_rmdir = syscall_table[__NR_rmdir]; //where orig_sys_rmdir is declared as : long (*orig_sys_rmdir)(const char *filename);
syscall_table[__NR_rmdir] = my_sys_rmdir; //Kerenl goes panic here, I guess.  my_sys_rmdir is a simple wrapper for sys_rmdir
...

内核版本为 2.6.X-X-generic,Ubuntu 10.04。仅在 32 位系统上出现此故障的原因可能是什么?

感谢您的宝贵时间。

编辑

错误日志:

BUG: unable to handle kernel NULL pointer dereference at 00000005
IP: [<c035834a>] strncmp+0x1a/0x40
*pde = 32090067 *pte = 00000000
Oops: 0000 [#2] SMP
imklog 4.2.0, log source = /proc/kmsg started.
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.32-74-generic (buildd@allspice) (gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) ) #142-Ubuntu SMP Tue Apr 28 10:02:35 UTC 2015 (Ubuntu 2.6.32-74.142-generic 2.6.32.63+drm33.26)

【问题讨论】:

  • 你能把它附加到 gdb 以确定它在哪里崩溃吗?
  • @icbytes,我添加了日志
  • 由于调试信息太少,我猜是加载的模块没有编译到你认为的目标。
  • 这是在 64 位上调试的?在调试期间手动收集所有信息?我们可以在 *pte 中看到什么?我假设,如果为 64 位构建并且它在 32 位上失败并且 *pte 被清零,并且在 32 位中没有,则应该驻留在 pte 中的值在 64 位上的 8 字节 int 值中可能太大,即在 32 位的 4 字节 int 中以某种方式减少到零。
  • 该模块是为加载它的同一台机器编译的。

标签: c linux linux-kernel kernel kernel-module


【解决方案1】:

@NTN 这是您在回答中提出的答案

我不知道相同的代码为何以及如何在 64 位系统上工作,但在 32 位系统上却不行

Asmlinkage 标记告诉编译器查看 cpu 堆栈上的参数。如果您不使用此标记,则编译器将根据调用约定在寄存器中查看。 对于 32 位系统,您可以在寄存器 %eax,%ecx,%edx 中传递 3 个参数,而其他参数则在堆栈中(如果有)。但在 64 位系统中,您可以使用 6 个寄存器 %rdi, %rsi, %rdx, %rcx, %r8, %r9

这意味着您的系统调用使用了 3 个以上的参数。当您在没有 asmlinkage 的情况下传递它们时,编译器无法在 32 位系统上定位所有参数,但在 64 位系统中它可以轻松定位它们。当您使用 asmlinkage 时,所有参数都通过 cpu 堆栈传递,因此您的代码成功。

【讨论】:

  • 谢谢。很高兴知道,asmlinkage 使向函数传递更多参数成为可能。
【解决方案2】:

我找到了这个问题的原因,从而回答了我自己的问题。如果他/她可能面临同样的问题,这将对其他人有所帮助。

我已将orig_sys_rmdir 声明为:

long (*orig_sys_rmdir)(const char *filename); //Wrong!!!

这当然是错的,它缺少asmlinkage关键字,一定是

asmlinkage long (*orig_sys_rmdir)(const char *filename); 

同样的事情发生在

long my_sys_rmdir(const char *filename) {...} //wrong!!!

必须是

asmlinkage long my_sys_rmdir(const char *filename){...}

我不知道为什么以及如何在 64 位系统上运行相同的代码,但在 32 位系统上却不行。我希望有人能更详细地说明这一点;答案将不胜感激。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 2014-05-30
    • 1970-01-01
    • 2020-01-28
    • 2011-03-03
    相关资源
    最近更新 更多