[PATCH] Kernel panic on IA-64 Linux with SELinux

2004-06-09T04:36:53Z

[-- Attachment #1: Type: text/plain, Size: 4549 bytes --]

Hello, everyone.

I observed kernel panic of linux-2.6.6 for IA-64, with SELinux.
(CONFIG_SECURITY=y and CONFIG_SECURITY_SELINUX=y)
I did a research about the problem, and found that there is an issue
with regard to the init_task initialization of IA-64.
I attached temporary workaround for the issue, but we should
come up with better solution.

[Overview]

While booing up the kernel with LSM/SELinux, init process tries to
execve() and one of the SELinux function is called to access init_task
(the parent of the init process)
Actually the LSM function tries to wake up tasks which is linked from
init_task, normally we expect nothing happens because init_task does
not have any linked tasks.
But because some members of init_task are initialized by wrong value,
due to the fact that IA-64 kernel image is now virtually mapped
from both region 5 and 7, it will lead to kernel panic.

I think this problem always happens when LSM/SELinux is enabled and
is IA-64 specific initialization issue.

[Detail]

The task_struct of cpu_idle (which is the parent of init process)
is named init_task and is defined in arch/ia64/kernel/init_task.c.
Init_task is initialized statically by INIT_TASK() macro at compile time.
The structure contains a wait_queue_head_t and a list_head in it.
Because the task_struct is linked to region 5 (0xa0....), the list_head
is also initialized to have region 5 addresses.

| |
0xa0000000... +=======================+
(region 5) | |
+-----------------------+
|init_task |
|pid=0 |
|wait_chldexit.task_list|
| +-------------------+ |
| |next: 0xa000.... | |<--+ Initialized as empty list
| +-------------------+ | | by referencing itself.
| |prev: 0xa000.... | |<--+
| +-------------------+ |
| |
+-----------------------+
| |
=== ===
| |
0xe0000000... +=======================+
(region 7) | |
+-----------------------+ <------ current of idle process
|init_task |
|pid=0 |
|wait_chldexit.task_list|
| +-------------------+ |
| |next: 0xa000.... | |<--+ This list head is NOT EMPTY
| +-------------------+ | | when accessed from region 7
| |prev: 0xa000.... | |<--+
| +-------------------+ |
| | <---+ +-----------------+
+-----------------------+ | | init process |
| | | | pid=1 |
| | +----- parent=init_task|
+-----------------+

As the current (r13) for the init_task is initialized in head.S to point
region 7 address (0xe0...), its child (init) will have region 7 address
for its parent address.

When the init process(PID=1) call execve(), LSM/SELinux hook function
tries to access the wait_chldexit waiting queue of the cpu_idle.
Since this doubly linked list is broken by above reason,
kernel panic occurred. Oops!

This problem came up to the surface for the first time by enabling SELinux.
Is it right referring to the parent of init?

[Solution]

Please find the attached patch to fix the problem.
The patch re-initializes current->wait_chldexit waitqueue, using
correct region 7 address in the head of setup_arch().

This solution is simple but sounds a bit ad hoc and I am seeking
better solution. I tried other 2 ways to fix the problem below,
but both of them failed.

1) I tried to move the init_task to region 7, by modifying linker
script arch/ia64/kernel/vmlinux.lds.S.
But I understand that the address of loading the kernel image is
now be detemined dynamically (not compile time) and the INIT_TASK()
macro still initializes the link list by wrong value :-<

2) We tried to set the current (r13) of cpu_idle to region 5 address.
But it paniced and I could not grab the cause of the panic.
Is there any reason we must put init_task in region 5?

Does anybody have better idea?

Best Regards,
--
Kaigai Kohei, Linux Promotion Center, NEC
E-mail: kaigai@ak.jp.nec.com

[-- Attachment #2: ia64.selinux.nopanic.040607.patch --]
[-- Type: application/octet-stream, Size: 899 bytes --]

diff -rNU2 linux-2.6.6/arch/ia64/kernel/setup.c linux-2.6.6.selinux/arch/ia64/kernel/setup.c
--- linux-2.6.6/arch/ia64/kernel/setup.c 2004-05-10 11:32:01.000000000 +0900
+++ linux-2.6.6.selinux/arch/ia64/kernel/setup.c 2004-06-08 14:48:45.000000000 +0900
@@ -284,4 +284,14 @@
setup_arch (char **cmdline_p)
{
+ /*-----------------------------------------------------
+ because task_struct of the parent of init(PID=0) was
+ initialized statically,the members of list type
+ in task_struct should be empty by self reference
+ references 0xa000... But,since current is 0xe000...
+ ,list_for_each() macro can not recognise this data
+ as empty list. Thus,these member should be initialized
+ dynamically. This problem was actualized in using SELinux.
+ -----------------------------------------------------*/
+ init_waitqueue_head(&(current->wait_chldexit));
unw_init();