MOKB-14-11-2006

Bug details
Title: Linux 2.6.x SELinux superblock_doinit denial of service
Description: Failure to handle mounting of corrupt filesystem streams may lead to a local denial of service condition when SELinux hooks are enabled. This particular vulnerability is caused by a null pointer dereference in the superblock_doinit function.
Author/Contributor:
References:
Proof of concept or exploit: The following HFS filesystem image can be used to reproduce the bug: MOKB-14-11-2006.img.bz2
Use a loopback device to mount it: bunzip2 MOKB-14-11-2006.img.bz2 && mount -t hfs -o loop MOKB-14-11-2006.img /media/test
Debugging information:

The bug has been found using the Linux version of fsfuzzer on a Fedora Core 6 installation, with up to date packages as of 13-11-2006. No operation except mount itself, is necessary to trigger the bug. The architecture used to conduct the tests is IA32/x86, SMP enabled.

[root@fedora ~]# uname -a
Linux fedora 2.6.18-1.2798.fc6 #1 SMP Mon Oct 16 14:37:32 EDT 2006 i686 i686 i386 GNU/Linux

crash> mach
       MACHINE TYPE: i686
        MEMORY SIZE: 256 MB
               CPUS: 1
    PROCESSOR SPEED: 2799 Mhz
                 HZ: 250
          PAGE SIZE: 4096
      L1 CACHE SIZE: 64
KERNEL VIRTUAL BASE: c0000000
KERNEL VMALLOC BASE: d0800000
  KERNEL STACK SIZE: 4096

loop: loaded (max 8 devices)
hfs: filesystem was not cleanly unmounted, running fsck.hfs is recommended.  mounting read-only.
hfs: get root inode failed.
BUG: unable to handle kernel NULL pointer dereference at virtual address 00000018
 printing eip:
c04c278f
*pde = 0b006067
Oops: 0000 [#1]
SMP
last sysfs file: /block/loop4/range
Modules linked in: hfs loop ipv6 sunrpc ip_conntrack_netbios_ns ipt_REJECT xt_state ip_conntrack nfnetlink
xt_tcpudp iptable_filter ip_tables x_tables video sbs i2c_ec button battery asus_acpi ac parport_pc lp
parport snd_ens1371 gameport snd_rawmidi snd_ac97_codec snd_ac97_bus snd_seq_dummy snd_seq_oss
snd_seq_midi_event floppy snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss sg snd_pcm i2c_piix4
snd_timer snd soundcore pcspkr snd_page_alloc i2c_core vmxnet(U) pcnet32 mii ide_cd cdrom serio_raw
dm_snapshot dm_zero dm_mirror dm_mod ext3 jbd mptspi scsi_transport_spi mptscsih sd_mod scsi_mod mptbase
CPU:    0
EIP:    0060:[]    Tainted: P      VLI
EFLAGS: 00010292   (2.6.18-1.2798.fc6 #1)
EIP is at superblock_doinit+0x21/0x767
eax: c062c349   ebx: cbfd0000   ecx: c068b8a0   edx: 00000000
esi: c9253a40   edi: d0b07cc0   ebp: cf38c400   esp: cb48ed1c
ds: 007b   es: 007b   ss: 0068
Process mount (pid: 1870, ti=cb48e000 task=cfe738b0 task.ti=cb48e000)
Stack: 00000000 00000000 00000001 00000000 00000000 cc5c0c40 00000000 cd43485e
       00000000 c04e5528 00000001 000000d0 c063e046 ced803e0 c063e046 cc5c08c0
       cd434800 ced803e0 c068ccc4 c068cd64 c068cca0 cd434832 c062c2e5 cbcd188c
Call Trace:
 [] selinux_sb_kern_mount+0xc/0x4b
 [] vfs_kern_mount+0x99/0xf6
 [] do_kern_mount+0x2d/0x3e
 [] do_mount+0x5fa/0x66d
 [] sys_mount+0x77/0xae
 [] syscall_call+0x7/0xb
DWARF2 unwinder stuck at syscall_call+0x7/0xb
Leftover inexact backtrace:
 =======================
Code: 9e 9c 01 00 00 5b 5e 5f 5d c3 55 89 c5 57 56 53 89 d3 83 ec 68 8b 80 84 00 00 00 89 44 24
14 8b 55 38 b8 49 c3 62 c0 89 54 24 18 <8b> 4a 18 ba 63 00 00 00 89 4c 24 1c e8 7b c2 f5 ff e8 c1 ff 14
EIP: [] superblock_doinit+0x21/0x767 SS:ESP 0068:cb48ed1c


1996 static int selinux_sb_kern_mount(struct super_block *sb, void *data)
1997 {
1998         struct avc_audit_data ad;
1999         int rc;
2000 
2001         rc = superblock_doinit(sb, data);
2002         if (rc)
2003                 return rc;
2004 
2005         AVC_AUDIT_DATA_INIT(&ad,FS);
2006         ad.u.fs.dentry = sb->s_root;
2007         return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
2008 }

590 static int superblock_doinit(struct super_block *sb, void *data)
591 {
592         struct superblock_security_struct *sbsec = sb->s_security;
593         struct dentry *root = sb->s_root;
594         struct inode *inode = root->d_inode;
595         int rc = 0;
596 
597         down(&sbsec->sem);
598         if (sbsec->initialized)
599                 goto out;
600 
601         if (!ss_initialized) {
602                 /* Defer initialization until selinux_complete_init,
603                    after the initial policy is loaded and the security
604                    server is ready to handle calls. */
605                 spin_lock(&sb_security_lock);
606                 if (list_empty(&sbsec->list))
607                         list_add(&sbsec->list, &superblock_security_head);
608                 spin_unlock(&sb_security_lock);
609                 goto out;
610         }
611 
612      (...)
694 }

crash> disas superblock_doinit+0x21
Dump of assembler code for function superblock_doinit:
0xc04c276e :       push   %ebp
0xc04c276f :       mov    %eax,%ebp
0xc04c2771 :       push   %edi
0xc04c2772 :       push   %esi
0xc04c2773 :       push   %ebx
0xc04c2774 :       mov    %edx,%ebx
0xc04c2776 :       sub    $0x68,%esp
0xc04c2779 :      mov    0x84(%eax),%eax
0xc04c277f :      mov    %eax,0x14(%esp)
0xc04c2783 :      mov    0x38(%ebp),%edx
0xc04c2786 :      mov    $0xc062c349,%eax
0xc04c278b :      mov    %edx,0x18(%esp) <--------
0xc04c278f :      mov    0x18(%edx),%ecx <--------
0xc04c2792 :      mov    $0x63,%edx
0xc04c2797 :      mov    %ecx,0x1c(%esp)
0xc04c279b :      call   0xc041ea1b <__might_sleep>
0xc04c27a0 :      call   0xc0612766 
0xc04c27a5 :      mov    0x14(%esp),%eax
0xc04c27a9 :      mov    0x14(%esp),%edx
0xc04c27ad :      add    $0x20,%eax
0xc04c27b0 :      mov    %eax,0x24(%esp)
0xc04c27b4 :      nop

crash> kmem c068b8a0                   <---- ecx: c068b8a0
c068b8a0 (d) selinux_ops

  PAGE    PHYSICAL   MAPPING    INDEX CNT FLAGS
c900d160    68b000   -------    -----   1 400