Ooops in proc_readdir [2.0.30-pre2-werner22jul]

Zlatko Calusic (Zlatko.Calusic@CARNet.hr)
25 Jul 1997 04:20:34 +0200


Oops: 0000
CPU: 0
EIP: 0010:[proc_readdir+125/280]
EFLAGS: 00010246
eax: 00000000 ebx: 028247fc ecx: 00000000 edx: bffff638
esi: 00000001 edi: 0192eb00 ebp: 00000107 esp: 00748f78
ds: 0018 es: 0018 fs: 002b gs: 002b ss: 0018
Process ls (pid: 16800, process nr: 50, stackpage=00748000)
Stack: 0192eb00 000003d8 bffff62c bffffa2c 0012dec3 0031d0fc 0192eb00 00748fa8
0012dd88 0099b810 bffff62c 08052dc0 bffff638 bffff62c 000003cc ffffffea
0010a655 00000003 bffff62c 000003d8 bffff62c 08052dc0 bffffa2c ffffffda
Call Trace: [sys_getdents+151/200] [filldir+0/164] [system_call+85/124]
Code: 8b 53 24 0f b7 12 52 56 6a 02 68 3a 87 19 00 8b 4c 24 2c 51

Oops can be easily reproduced:

I have all my scsi drivers compiled as modules. So when I start
cdrecord -eject, cat /dev/sga or whatever command that accesses SCSI
devices, kerneld loads modules (scsi_mod, aci7xxx, sg) and
/proc/scsi/aic7xxx directory is created. When I enter that directory I
can get some information about attached devices. If I stay in that
directory and wait for modules to unload and than do ls -al, ls gets
SIGSEGV and kernel gets an oops! Machine works after that without any
problems and I can repeat procedure as many time as I want, always
with same results.

I tried to fix problem myself (it doesn't look _that_ complicated),
just to find that my knowledge of Linux kernel just isn't good enough.
So I'll leave fixing it to some more clever heads. :)

As far as I can understand it, directory dissapears after kerneld
unloads modules, and some check is missing, to avoid accessing illegal
memory address (or null pointer dereference). It looks like the same
bug exists in 2.1.x kernels, too.

Here comes additional info:

0x15c82c <proc_readdir>: pushl %ebp
0x15c82d <proc_readdir+1>: pushl %edi
0x15c82e <proc_readdir+2>: pushl %esi
0x15c82f <proc_readdir+3>: pushl %ebx
0x15c830 <proc_readdir+4>: movl 0x14(%esp,1),%eax
0x15c834 <proc_readdir+8>: movl 0x18(%esp,1),%edi
0x15c838 <proc_readdir+12>: testl %eax,%eax
0x15c83a <proc_readdir+14>: je 0x15c84c <proc_readdir+32>
0x15c83c <proc_readdir+16>: movw 0x8(%eax),%dx
0x15c840 <proc_readdir+20>: andw $0xf000,%dx
0x15c845 <proc_readdir+25>: cmpw $0x4000,%dx
0x15c84a <proc_readdir+30>: je 0x15c858 <proc_readdir+44>
0x15c84c <proc_readdir+32>: movl $0xffffffec,%eax
0x15c851 <proc_readdir+37>: popl %ebx
0x15c852 <proc_readdir+38>: popl %esi
0x15c853 <proc_readdir+39>: popl %edi
0x15c854 <proc_readdir+40>: popl %ebp
0x15c855 <proc_readdir+41>: ret
0x15c856 <proc_readdir+42>: movl %esi,%esi
0x15c858 <proc_readdir+44>: movl 0x4(%eax),%ebp
0x15c85b <proc_readdir+47>: movl 0x8c(%eax),%ebx
0x15c861 <proc_readdir+53>: testl %ebx,%ebx
0x15c863 <proc_readdir+55>: jne 0x15c870 <proc_readdir+68>
0x15c865 <proc_readdir+57>: movl $0xffffffea,%eax
0x15c86a <proc_readdir+62>: popl %ebx
0x15c86b <proc_readdir+63>: popl %esi
0x15c86c <proc_readdir+64>: popl %edi
0x15c86d <proc_readdir+65>: popl %ebp
0x15c86e <proc_readdir+66>: ret
0x15c86f <proc_readdir+67>: nop
0x15c870 <proc_readdir+68>: movl 0x4(%edi),%esi
0x15c873 <proc_readdir+71>: testl %esi,%esi
0x15c875 <proc_readdir+73>: je 0x15c880 <proc_readdir+84>
0x15c877 <proc_readdir+75>: cmpl $0x1,%esi
0x15c87a <proc_readdir+78>: je 0x15c8a9 <proc_readdir+125>
0x15c87c <proc_readdir+80>: jmp 0x15c8dd <proc_readdir+177>
0x15c87e <proc_readdir+82>: movl %esi,%esi
0x15c880 <proc_readdir+84>: pushl %ebp
0x15c881 <proc_readdir+85>: pushl $0x0
0x15c883 <proc_readdir+87>: pushl $0x1
0x15c885 <proc_readdir+89>: pushl $0x198738
0x15c88a <proc_readdir+94>: movl 0x2c(%esp,1),%ecx
0x15c88e <proc_readdir+98>: pushl %ecx
0x15c88f <proc_readdir+99>: movl 0x34(%esp,1),%ecx
0x15c893 <proc_readdir+103>: call *%ecx
0x15c895 <proc_readdir+105>: addl $0x14,%esp
0x15c898 <proc_readdir+108>: testl %eax,%eax
0x15c89a <proc_readdir+110>: jl 0x15c8ca <proc_readdir+158>
0x15c89c <proc_readdir+112>: movl $0x1,%esi
0x15c8a1 <proc_readdir+117>: addl $0x1,0x4(%edi)
0x15c8a5 <proc_readdir+121>: adcl $0x0,0x8(%edi) oops
0x15c8a9 <proc_readdir+125>: movl 0x24(%ebx),%edx <<<-----
0x15c8ac <proc_readdir+128>: movzwl (%edx),%edx
0x15c8af <proc_readdir+131>: pushl %edx
0x15c8b0 <proc_readdir+132>: pushl %esi
0x15c8b1 <proc_readdir+133>: pushl $0x2
0x15c8b3 <proc_readdir+135>: pushl $0x19873a
0x15c8b8 <proc_readdir+140>: movl 0x2c(%esp,1),%ecx
0x15c8bc <proc_readdir+144>: pushl %ecx
0x15c8bd <proc_readdir+145>: movl 0x34(%esp,1),%ecx
0x15c8c1 <proc_readdir+149>: call *%ecx
0x15c8c3 <proc_readdir+151>: addl $0x14,%esp
0x15c8c6 <proc_readdir+154>: testl %eax,%eax
0x15c8c8 <proc_readdir+156>: jnl 0x15c8d4 <proc_readdir+168>
0x15c8ca <proc_readdir+158>: xorl %eax,%eax
0x15c8cc <proc_readdir+160>: popl %ebx
0x15c8cd <proc_readdir+161>: popl %esi
0x15c8ce <proc_readdir+162>: popl %edi
0x15c8cf <proc_readdir+163>: popl %ebp
0x15c8d0 <proc_readdir+164>: ret
...

fs/proc/root.c:

int proc_readdir(struct inode * inode, struct file * filp,
void * dirent, filldir_t filldir)
{
...

filp->f_pos++;
/* fall through */
oops?? case 1:
---->>> if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0)
return 0;
i++;
filp->f_pos++;
/* fall through */
default:
...

Configuration:
Pentium 133, CDR400tx CD-R, AHA-2940UW...

Special thanks goes to Linus Torvalds, David S. Miller and Dr. Werner
Fink for their great work and spirit!

Greetings!

-- 
Posted by Zlatko Calusic           E-mail: <Zlatko.Calusic@CARNet.hr>
---------------------------------------------------------------------
	There is an exception to every rule, except this one.