[Unionfs] NULL pointer dereference if copyup_dentry() failed?
Tetsuo Handa
penguin-kernel at I-love.SAKURA.ne.jp
Mon Aug 4 07:11:46 EDT 2008
Hello.
Erez Zadok wrote:
> Was this a vanilla 2.6.24.5 kernel, or did it have other patches applied?
> In particular, are you using any "special" MAC system or the like, ala
> selinux, smack, etc.? I'd like to be able to reproduce this on my end.
It's a vanilla 2.6.24.5 with TOMOYO Linux (one of MAC systems) patch applied.
This problem exists in all system with any kind of MAC enabled
(i.e. this problem can happen in SELinux and SMACK and AppArmor too).
But theoretically, this problem can happen in non MAC systems too
if underlying filesystem returns error for setattr operation.
It seems to me that the problem is that unionfs's internal state is disturbed
by failure of setattr operation.
Here is a list of postings related to this problem.
http://marc.info/?l=linux-security-module&m=121603444810942&w=2
http://marc.info/?l=linux-security-module&m=121609490418118&w=2
http://thread.gmane.org/gmane.linux.file-systems/24989
Below is the procedure for how to reproduce this problem.
But this problem also depends on distributions (gcc?) you are using.
(1) Get and extract kernel source.
# wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.24.7.tar.bz2
# tar -jxf linux-2.6.24.7.tar.bz2
# cd linux-2.6.24.7
(2) Get and extract and apply unionfs patch.
# wget http://download.filesystems.org/unionfs/unionfs-2.x/unionfs-2.4_for_2.6.24.7.diff.gz
# zcat unionfs-2.4_for_2.6.24.7.diff.gz | patch -p1
(3) Get and extract and apply TOMOYO Linux patch.
# wget http://osdn.dl.sourceforge.jp/tomoyo/30297/ccs-patch-1.6.3-20080715.tar.gz
# tar -zxf ccs-patch-1.6.3-20080715.tar.gz
# patch -p1 < patches/ccs-patch-2.6.24.diff
(4) Apply some printk() patch for unionfs. This is optional.
---
fs/unionfs/commonfops.c | 5 +++++
fs/unionfs/copyup.c | 5 +++++
fs/unionfs/inode.c | 4 ++++
3 files changed, 14 insertions(+)
--- linux-2.6.24.7.orig/fs/unionfs/commonfops.c
+++ linux-2.6.24.7/fs/unionfs/commonfops.c
@@ -589,6 +589,8 @@ int unionfs_open(struct inode *inode, st
int size;
int valid = 0;
+ printk(KERN_WARNING "Entering %s()\n", __func__);
+
unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT);
unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
if (dentry != dentry->d_parent)
@@ -666,6 +668,9 @@ out_nofree:
unionfs_unlock_dentry(dentry->d_parent);
unionfs_unlock_dentry(dentry);
unionfs_read_unlock(inode->i_sb);
+
+ printk(KERN_WARNING "Leaving %s() with %d\n", __func__, err);
+
return err;
}
--- linux-2.6.24.7.orig/fs/unionfs/copyup.c
+++ linux-2.6.24.7/fs/unionfs/copyup.c
@@ -388,6 +388,8 @@ int copyup_dentry(struct inode *dir, str
mm_segment_t oldfs;
char *symbuf = NULL;
+ printk(KERN_WARNING "Entering %s()\n", __func__);
+
verify_locked(dentry);
old_bindex = bstart;
@@ -541,6 +543,9 @@ out_free:
unionfs_check_inode(dir);
unionfs_check_dentry(dentry);
out:
+
+ printk(KERN_WARNING "Leaving %s() with %d\n", __func__, err);
+
return err;
}
--- linux-2.6.24.7.orig/fs/unionfs/inode.c
+++ linux-2.6.24.7/fs/unionfs/inode.c
@@ -849,6 +849,8 @@ static int unionfs_setattr(struct dentry
int bstart, bend, bindex;
loff_t size;
+ printk(KERN_WARNING "Entering %s()\n", __func__);
+
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
@@ -949,6 +951,8 @@ out:
unionfs_unlock_dentry(dentry);
unionfs_read_unlock(dentry->d_sb);
+ printk(KERN_WARNING "Leaving %s() with %d\n", __func__, err);
+
return err;
}
(5) Compile the kernel with CONFIG_UNION_FS=m and CONFIG_UNION_FS_DEBUG=y and
CONFIG_TOMOYO=y . The config I used is available at http://I-love.SAKURA.ne.jp/tmp/config-2.6.24.7-unionfs-ccs .
(6) Get and extract and compile and install TOMOYO Linux tools.
# cd /tmp/
# wget http://osdn.dl.sourceforge.jp/tomoyo/30298/ccs-tools-1.6.3-20080715.tar.gz
# tar -zxf ccs-tools-1.6.3-20080715.tar.gz
# make -C ccstools install
(7) Initialize policy configuration.
# /usr/lib/ccs/init_policy.sh
# echo "4-MAC_FOR_CAPABILITY::SYS_CHMOD=enforcing" >> /etc/ccs/profile.conf
(8) Reboot the system with the compiled kernel.
(9) Run the following commands to set up environment.
My environment has only / partition (ext3 filesystem).
# rm -fR /tmp/1 /tmp/2
# mkdir /tmp/1 /tmp/2
# touch /tmp/2/foo
# mount -t unionfs -o dirs=/tmp/1=rw:/tmp/2=ro none /mnt/
(10) Configure /bin/touch executed from current shell to allow everything
except for changing mode (i.e. calling notify_change() with ATTR_MODE).
# ( echo `cat < /proc/ccs/self_domain` /bin/touch ; echo use_profile 4) | /usr/sbin/ccs-loadpolicy -d
(11) Create /mnt/foo using /bin/touch . Unionfs will try to create copy of
/tmp/2/foo as /tmp/1/foo , but notify_change() for changing mode is
rejected by TOMOYO Linux. So, you will see TOMOYO-ERROR: message in dmesg.
# touch /mnt/foo
If you are using Mandriva 2008.1 (installed using mandriva-linux-one-2008-spring-KDE-int-cdrom-i586.iso
and updated to latest packages, gcc version 4.2.3 (4.2.3-6mnb1)),
you will get NULL pointer dereference message at this point. The dmesg is:
Registering unionfs 2.4 (for 2.6.24.7)
Entering unionfs_open()
Leaving unionfs_open() with 0
Entering unionfs_setattr()
Entering copyup_dentry()
TOMOYO-ERROR: sys_chmod() denied for /bin/touch
Leaving copyup_dentry() with -1
Leaving unionfs_setattr() with -1
PC:fs/unionfs/dentry.c:unionfs_d_revalidate:497
CI1: dentry/inode=f1ec9358:f1ecc07c istart=1 dstart=0
CI3: dentry/inode=f1ec9358:f1ecc07c dstart=0 dend=1
Entering unionfs_setattr()
BUG: unable to handle kernel NULL pointer dereference at virtual address 00000118
printing eip: f887ac6e *pde = 00000000
Oops: 0000 [#1] SMP
Modules linked in: unionfs binfmt_misc pcnet32 mptspi mptscsih mptbase scsi_transport_spi uhci_hcd ohci_hcd ehci_hcd
Pid: 3795, comm: touch Not tainted (2.6.24.7-unionfs #3)
EIP: 0060:[<f887ac6e>] EFLAGS: 00010202 CPU: 0
EIP is at unionfs_setattr+0x334/0x36b [unionfs]
EAX: 00000000 EBX: f67e3f38 ECX: f1ec9358 EDX: f1ec9860
ESI: ffffff8c EDI: 00000000 EBP: f1ecc07c ESP: f67e3e68
DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process touch (pid: 3795, ti=f67e3000 task=f67c0270 task.ti=f67e3000)
Stack: f8885b9c f88857d8 c0326c04 00000246 f1ecc140 c013ea03 f67e3f38 f1ec9358
f1ec9860 f1ecc108 f140a180 00000070 f67e3f38 00000070 f1ecc07c 00000000
c017b44f c01849e8 00000000 f1ec9358 00000000 222281a4 22222222 22222222
Call Trace:
[<c0326c04>] mutex_lock_nested+0x27e/0x291
[<c013ea03>] trace_hardirqs_on+0x129/0x167
[<c017b44f>] notify_change+0x311/0x34e
[<c01849e8>] do_utimes+0x1a8/0x1e8
[<c01849f5>] do_utimes+0x1b5/0x1e8
[<c013ea03>] trace_hardirqs_on+0x129/0x167
[<c0327ed0>] _spin_unlock_irq+0x20/0x23
[<c011ef3e>] finish_task_switch+0x50/0x8c
[<c011eeee>] finish_task_switch+0x0/0x8c
[<c0184aa1>] sys_futimesat+0x79/0x88
[<c013ea03>] trace_hardirqs_on+0x129/0x167
[<c0184b4a>] sys_utimensat+0x77/0x86
[<c0184acf>] sys_utimes+0x1f/0x23
[<c0103ec2>] sysenter_past_esp+0x5f/0xa5
=======================
Code: 44 85 c9 74 0e 64 a1 00 80 47 c0 3b 88 b0 01 00 00 74 2d 8d 42 08 e8 7a cb 8b c7 eb 23 be f3 ff ff ff eb c1 8b 54 24 20 8b 42 28 <8b> 80 18 01 00 00 f6 40 30 01 0f 85 a3 fd ff ff e9 60 fe ff ff
EIP: [<f887ac6e>] unionfs_setattr+0x334/0x36b [unionfs] SS:ESP 0068:f67e3e68
---[ end trace 6e9a91d6c565efcc ]---
If you are using Debian Sarge (gcc (GCC) 3.3.5 (Debian 1:3.3.5-13)), you will get NULL pointer dereference message when you run
# touch /mnt/foo
once again. The dmesg is:
Registering unionfs 2.4 (for 2.6.24.7)
Entering unionfs_open()
Leaving unionfs_open() with 0
Entering unionfs_setattr()
Entering copyup_dentry()
TOMOYO-ERROR: sys_chmod() denied for /bin/touch
Leaving copyup_dentry() with -1
Leaving unionfs_setattr() with -1
PC:fs/unionfs/dentry.c:unionfs_d_revalidate:497
CI1: dentry/inode=f79b0bf8:f79b207c istart=1 dstart=0
CI3: dentry/inode=f79b0bf8:f79b207c dstart=0 dend=1
Entering unionfs_open()
BUG: unable to handle kernel NULL pointer dereference at virtual address 00000080
printing eip: c0140852 *pde = 00000000
Oops: 0000 [#1] SMP
Modules linked in: unionfs nfsd lockd sunrpc exportfs pcnet32 uhci_hcd ohci_hcd ehci_hcd
Pid: 2353, comm: touch Not tainted (2.6.24.7-unionfs #1)
EIP: 0060:[<c0140852>] EFLAGS: 00010046 CPU: 0
EIP is at __lock_acquire+0xa5/0x76c
EAX: 00000001 EBX: 00000246 ECX: 0000007c EDX: 00000002
ESI: 00000000 EDI: 00000000 EBP: 0000007c ESP: f7df1ddc
DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process touch (pid: 2353, ti=f7df1000 task=f7c480f0 task.ti=f7df1000)
Stack: c1860000 00000000 f7c487f4 d31a4b4d 00000000 00000001 f7c480f0 f7c487bc
f7c480f0 00000000 f7c480f0 00000002 00000000 00000000 0000007c 00000246
00000000 00000000 0000007c c01414e3 00000000 00000002 00000000 c0173a88
Call Trace:
[<c01414e3>] lock_acquire+0x79/0x93
[<c0173a88>] get_write_access+0xe/0x38
[<c033a227>] _spin_lock+0x2c/0x55
[<c0173a88>] get_write_access+0xe/0x38
[<c0173a88>] get_write_access+0xe/0x38
[<c016ccc3>] __dentry_open+0x36/0x177
[<c016cf31>] dentry_open+0x4a/0x50
[<f889344d>] __open_file+0x188/0x211 [unionfs]
[<c016ac4d>] __kmalloc+0x110/0x134
[<f8893618>] unionfs_open+0x142/0x2fb [unionfs]
[<f8893679>] unionfs_open+0x1a3/0x2fb [unionfs]
[<f88934d6>] unionfs_open+0x0/0x2fb [unionfs]
[<c016cd51>] __dentry_open+0xc4/0x177
[<c016ced8>] nameidata_to_filp+0x23/0x32
[<c016ce36>] do_filp_open+0x32/0x39
[<c033a372>] _spin_unlock+0x14/0x1c
[<c016cfe7>] get_unused_fd_flags+0xb0/0xba
[<c016d0ba>] do_sys_open+0x44/0xc5
[<c016d155>] sys_open+0x1a/0x1c
[<c0103ec2>] syscall_call+0x7/0xb
=======================
Code: 09 00 00 e9 84 04 00 00 83 7c 24 34 07 76 11 e8 51 e6 0b 00 c7 04 24 00 27 3e c0 e9 9f 06 00 00 83 7c 24 34 00 75 0f 8b 4c 24 38 <8b> 59 04 85 db 0f 85 c2 03 00 00 83 3d d4 24 53 c0 00 75 19 e8
EIP: [<c0140852>] __lock_acquire+0xa5/0x76c SS:ESP 0068:f7df1ddc
---[ end trace ddec43033c47e4bb ]---
What is strange is that the stack dump is different.
It is not oopsing at unionfs_setattr but __lock_acquire .
If you can't reproduce this problem, please tell me the distribution you
can prepare. I have Fedora, CentOS, Debian, SUSE, Gentoo etc.
> Since you were able to stick printk's in unionfs_setattr, can you add a few
> more closer to the end (0x324/0x35b) and try to narrow down where's the null
> deref?
It's after notify_change() failed, but I haven't identified the exact location.
> In the steps above: it looks fairly simple (ie. no funky chroot, bind
> mounts, pivot_root, etc.) right?
Nothing special, except for MAC system is enabled.
> Do you know what's the condition which caused copyup_dentry (in
> unionfs_setattr) to fail? Can you print the errno?
In this case, security_inode_setattr returning non-zero (e.g. -EPERM).
> If you don't mind, please open a bugzilla report at
> https://bugzilla.filesystems.org/, give me pointers to any patches you
> applied to the kernel, kernel .config used, etc.
Sorry. I don't have bugzilla account. All pointers are listed above.
> BTW, does the problem still exist in unionfs-2.4?
Same result (both triggers NULL pointer deref) for
linux-2.6.24.5.tar.bz2 + unionfs-2.3.3_for_2.6.24.5.diff.gz
linux-2.6.24.7.tar.bz2 + unionfs-2.4_for_2.6.24.7.diff.gz
Regards.
More information about the unionfs
mailing list