Reputation: 1009
I have a kernel module, which removes write protected bit to modify system call table, in order to hook any system call - say sys_rmdir. This module works as expected for 64 bit systems, but for 32 bit systems why kernel get panic and gives oops after accessing address of system call table?
The module has problem at this point:
#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
...
The kernel version is 2.6.X-X-generic, Ubuntu 10.04. What could be the reason of this failure only on 32 bit systems?
Thanks for your time.
EDIT:
Error log:
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)
Upvotes: 1
Views: 713
Reputation: 1822
@NTN this is for the answer asked in in your answer
I don't know why and how the same code worked on 64 bit systems, but not on 32 bit systems.
Asmlinkage tag tell the compiler to see the parametrs on cpu stack. if you do not use this tag then compiler will see in the registers according to calling conventions.
for 32 bit systems you can pass 3 parameters in registers %eax,%ecx,%edx
while others on stack (if any). but in 64 bit systems you can use 6 registers %rdi, %rsi, %rdx, %rcx, %r8, %r9
.
that implies that your syscall is using more than 3 parameters. when you pass them with out asmlinkage, compiler is not able to locate all parameters on 32 bit systems but in 64 bits systems it locate them comfortably. when you use asmlinkage then all parametrs are passed through cpu stack so your code is successful.
Upvotes: 2
Reputation: 1009
I found cause of this problem, thus answering my own question. This would help to someone else, if he/she may face the same problem.
I had declared orig_sys_rmdir
as:
long (*orig_sys_rmdir)(const char *filename); //Wrong!!!
which was wrong of course, it was missing the asmlinkage
keyword, it must be
asmlinkage long (*orig_sys_rmdir)(const char *filename);
The same happened with
long my_sys_rmdir(const char *filename) {...} //wrong!!!
which must be
asmlinkage long my_sys_rmdir(const char *filename){...}
I don't know why and how the same code worked on 64 bit systems, but not on 32 bit systems. I hope someone will elaborate on this in more detail; answers will be appreciated.
Upvotes: 1