diff options
| author | Earl Chew <earl_chew@agilent.com> | 2009-10-19 18:55:41 -0400 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-21 19:11:44 -0400 | 
| commit | ad3960243e55320d74195fb85c975e0a8cc4466c (patch) | |
| tree | e17b79f2bc7a2fd76d8598fb0075f8c78cd5714a | |
| parent | 2fdc246aaf9a7fa088451ad2a72e9119b5f7f029 (diff) | |
fs: pipe.c null pointer dereference
This patch fixes a null pointer exception in pipe_rdwr_open() which
generates the stack trace:
> Unable to handle kernel NULL pointer dereference at 0000000000000028 RIP:
>  [<ffffffff802899a5>] pipe_rdwr_open+0x35/0x70
>  [<ffffffff8028125c>] __dentry_open+0x13c/0x230
>  [<ffffffff8028143d>] do_filp_open+0x2d/0x40
>  [<ffffffff802814aa>] do_sys_open+0x5a/0x100
>  [<ffffffff8021faf3>] sysenter_do_call+0x1b/0x67
The failure mode is triggered by an attempt to open an anonymous
pipe via /proc/pid/fd/* as exemplified by this script:
=============================================================
while : ; do
   { echo y ; sleep 1 ; } | { while read ; do echo z$REPLY; done ; } &
   PID=$!
   OUT=$(ps -efl | grep 'sleep 1' | grep -v grep |
        { read PID REST ; echo $PID; } )
   OUT="${OUT%% *}"
   DELAY=$((RANDOM * 1000 / 32768))
   usleep $((DELAY * 1000 + RANDOM % 1000 ))
   echo n > /proc/$OUT/fd/1                 # Trigger defect
done
=============================================================
Note that the failure window is quite small and I could only
reliably reproduce the defect by inserting a small delay
in pipe_rdwr_open(). For example:
 static int
 pipe_rdwr_open(struct inode *inode, struct file *filp)
 {
       msleep(100);
       mutex_lock(&inode->i_mutex);
Although the defect was observed in pipe_rdwr_open(), I think it
makes sense to replicate the change through all the pipe_*_open()
functions.
The core of the change is to verify that inode->i_pipe has not
been released before attempting to manipulate it. If inode->i_pipe
is no longer present, return ENOENT to indicate so.
The comment about potentially using atomic_t for i_pipe->readers
and i_pipe->writers has also been removed because it is no longer
relevant in this context. The inode->i_mutex lock must be used so
that inode->i_pipe can be dealt with correctly.
Signed-off-by: Earl Chew <earl_chew@agilent.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/pipe.c | 41 | 
1 files changed, 30 insertions, 11 deletions
| @@ -777,36 +777,55 @@ pipe_rdwr_release(struct inode *inode, struct file *filp) | |||
| 777 | static int | 777 | static int | 
| 778 | pipe_read_open(struct inode *inode, struct file *filp) | 778 | pipe_read_open(struct inode *inode, struct file *filp) | 
| 779 | { | 779 | { | 
| 780 | /* We could have perhaps used atomic_t, but this and friends | 780 | int ret = -ENOENT; | 
| 781 | below are the only places. So it doesn't seem worthwhile. */ | 781 | |
| 782 | mutex_lock(&inode->i_mutex); | 782 | mutex_lock(&inode->i_mutex); | 
| 783 | inode->i_pipe->readers++; | 783 | |
| 784 | if (inode->i_pipe) { | ||
| 785 | ret = 0; | ||
| 786 | inode->i_pipe->readers++; | ||
| 787 | } | ||
| 788 | |||
| 784 | mutex_unlock(&inode->i_mutex); | 789 | mutex_unlock(&inode->i_mutex); | 
| 785 | 790 | ||
| 786 | return 0; | 791 | return ret; | 
| 787 | } | 792 | } | 
| 788 | 793 | ||
| 789 | static int | 794 | static int | 
| 790 | pipe_write_open(struct inode *inode, struct file *filp) | 795 | pipe_write_open(struct inode *inode, struct file *filp) | 
| 791 | { | 796 | { | 
| 797 | int ret = -ENOENT; | ||
| 798 | |||
| 792 | mutex_lock(&inode->i_mutex); | 799 | mutex_lock(&inode->i_mutex); | 
| 793 | inode->i_pipe->writers++; | 800 | |
| 801 | if (inode->i_pipe) { | ||
| 802 | ret = 0; | ||
| 803 | inode->i_pipe->writers++; | ||
| 804 | } | ||
| 805 | |||
| 794 | mutex_unlock(&inode->i_mutex); | 806 | mutex_unlock(&inode->i_mutex); | 
| 795 | 807 | ||
| 796 | return 0; | 808 | return ret; | 
| 797 | } | 809 | } | 
| 798 | 810 | ||
| 799 | static int | 811 | static int | 
| 800 | pipe_rdwr_open(struct inode *inode, struct file *filp) | 812 | pipe_rdwr_open(struct inode *inode, struct file *filp) | 
| 801 | { | 813 | { | 
| 814 | int ret = -ENOENT; | ||
| 815 | |||
| 802 | mutex_lock(&inode->i_mutex); | 816 | mutex_lock(&inode->i_mutex); | 
| 803 | if (filp->f_mode & FMODE_READ) | 817 | |
| 804 | inode->i_pipe->readers++; | 818 | if (inode->i_pipe) { | 
| 805 | if (filp->f_mode & FMODE_WRITE) | 819 | ret = 0; | 
| 806 | inode->i_pipe->writers++; | 820 | if (filp->f_mode & FMODE_READ) | 
| 821 | inode->i_pipe->readers++; | ||
| 822 | if (filp->f_mode & FMODE_WRITE) | ||
| 823 | inode->i_pipe->writers++; | ||
| 824 | } | ||
| 825 | |||
| 807 | mutex_unlock(&inode->i_mutex); | 826 | mutex_unlock(&inode->i_mutex); | 
| 808 | 827 | ||
| 809 | return 0; | 828 | return ret; | 
| 810 | } | 829 | } | 
| 811 | 830 | ||
| 812 | /* | 831 | /* | 
