为什么内核在访问32位系统的内核模块中的系统调用表后会出现恐慌?

时间:2015-09-03 11:12:07

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

我有一个内核模块,用于删除写保护位以修改系统调用表,以便挂钩任何系统调用 - 比如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)

2 个答案:

答案 0 :(得分:2)

@NTN这是答案中提到的答案

我不知道为什么以及如何在64位系统上使用相同的代码,但在32位系统上却没有

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

表示您的系统调用使用的参数超过3个。当你通过asmlinkage传递它们时,编译器无法在32位系统上找到所有参数,但在64位系统中它可以很容易地找到它们。当您使用asmlinkage时,所有参数都通过cpu堆栈传递,因此您的代码成功。

答案 1 :(得分:1)

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

我已将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位系统上却没有。我希望有人能详细说明这一点;答案将不胜感激。