Re: [Vserver] sendfile crash? (2.6.13.4 with vserver 2.1.0-rc4)

From: Herbert Poetzl <herbert_at_13thfloor.at>
Date: Fri 04 Nov 2005 - 01:07:15 GMT
Message-ID: <20051104010715.GC22020@MAIL.13thfloor.at>

On Thu, Nov 03, 2005 at 09:01:14PM +0100, Grzegorz Nosek wrote:
> 2005/11/3, Herbert Poetzl <herbert@13thfloor.at>:
> > On Thu, Nov 03, 2005 at 05:38:43PM +0100, Grzegorz Nosek wrote:
> > > Hello all
> > >
> > > I needed to apply the patch below in order to keep the kernel from
> > > oopsing (in some older revisions) or freezing solid (in the newest,
> > > listed in the subject.
> >
> > as follow up, please try the following patch instead:
> >
> > http://lkml.org/lkml/diff/2005/11/3/161/1
> >
> > rationale: http://lkml.org/lkml/2005/11/3/161
> >
> > HTH,
> > Herbert
> >
> >
>
> Hello,
>
> I think I disagree that my proposed patch is hiding the real bug
> because the ppos and max variables aren't ever used in do_sendfile.
> They're blindly passed to vfs_sendfile, which does the checks and
> returns -EOVERFLOW if needed.

hmm, good that you disagree ...

> IMHO my patch is more of The Right Way (tm) because it doesn't involve
> modifications in another function (only do_sendfile is affected, which
> is modified heavily anyway).

hmm, well, maybe a mix of both approaches is what we
really want because the following looks 'suspicious'
to me:

asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd,
                                off_t __user *offset, size_t count)
{
        ...
        if (offset) {
                ...
                pos = off;
                ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS);
                ...
                return ret;
        }
        return do_sendfile(out_fd, in_fd, NULL, count, 0);
}

because in the case we _have_ an offset specified, we
pass a pos and limit (MAX_NON_LFS) and if not, we just
set it to zero?

> Of course, you're free to either include it or ignore it.
>
> (my definitions of vfs_sendfile and do_sendfile with some random
> annotations below)

well, I agree with you that the check in do_sendfile()
is superfluous ... and that the checks in vfs_sendfile()
should be sufficient ... haven't checked all cases here

best,
Herbert

PS: please follow up on lkml, if not done so already ...

> ssize_t vfs_sendfile(struct file *out_file, struct file *in_file, loff_t *ppos,
> size_t count, loff_t max)
> {
> struct inode * in_inode, * out_inode;
> loff_t pos;
> ssize_t ret;
>
> /* verify in_file */
> in_inode = in_file->f_dentry->d_inode;
> if (!in_inode)
> return -EINVAL;
> if (!in_file->f_op || !in_file->f_op->sendfile)
> return -EINVAL;
>
> // !!! ppos is validated here
> if (!ppos)
> ppos = &in_file->f_pos;
> else
> if (!(in_file->f_mode & FMODE_PREAD))
> return -ESPIPE;
>
> ret = rw_verify_area(FLOCK_VERIFY_READ, in_file, ppos, count);
> if (ret)
> return ret;
>
> /* verify out_file */
> out_inode = out_file->f_dentry->d_inode;
> if (!out_inode)
> return -EINVAL;
> if (!out_file->f_op || !out_file->f_op->sendpage)
> return -EINVAL;
>
> ret = rw_verify_area(FLOCK_VERIFY_WRITE, out_file,
> &out_file->f_pos, count);
> if (ret)
> return ret;
>
> ret = security_file_permission (out_file, MAY_WRITE);
> if (ret)
> return ret;
>
> // !!! max is validated here
> if (!max)
> max = min(in_inode->i_sb->s_maxbytes,
> out_inode->i_sb->s_maxbytes);
>
> pos = *ppos;
> if (unlikely(pos < 0))
> return -EINVAL;
> if (unlikely(pos + count > max)) {
> if (pos >= max)
> return -EOVERFLOW;
> count = max - pos;
> }
>
> ret = in_file->f_op->sendfile(in_file, ppos, count,
> file_send_actor, out_file);
>
> if (*ppos > max)
> return -EOVERFLOW;
> return ret;
> }
>
> EXPORT_SYMBOL(vfs_sendfile);
>
> static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
> size_t count, loff_t max)
> {
> struct file * in_file, * out_file;
> ssize_t retval;
> int fput_needed_in, fput_needed_out;
>
> /*
> * Get input file, and verify that it is ok..
> */
> retval = -EBADF;
> in_file = fget_light(in_fd, &fput_needed_in);
> if (!in_file)
> goto out;
> if (!(in_file->f_mode & FMODE_READ))
> goto fput_in;
>
> retval = security_file_permission (in_file, MAY_READ);
> if (retval)
> goto fput_in;
>
> /*
> * Get output file, and verify that it is ok..
> */
> retval = -EBADF;
> out_file = fget_light(out_fd, &fput_needed_out);
> if (!out_file)
> goto fput_in;
> if (!(out_file->f_mode & FMODE_WRITE))
> goto fput_out;
>
> retval = vfs_sendfile(out_file, in_file, ppos, count, max);
>
> // !!! if vfs_sendfile returned -EOVERFLOW, it propagates out of
> do_sendfile too (and doesn't skip fput_XXX)
>
> if (retval > 0) {
> current->rchar += retval;
> current->wchar += retval;
> }
> current->syscr++;
> current->syscw++;
>
> fput_out:
> fput_light(out_file, fput_needed_out);
> fput_in:
> fput_light(in_file, fput_needed_in);
> out:
> return retval;
> }
> _______________________________________________
> Vserver mailing list
> Vserver@list.linux-vserver.org
> http://list.linux-vserver.org/mailman/listinfo/vserver
_______________________________________________
Vserver mailing list
Vserver@list.linux-vserver.org
http://list.linux-vserver.org/mailman/listinfo/vserver
Received on Fri Nov 4 01:09:15 2005

[Next/Previous Months] [Main vserver Project Homepage] [Howto Subscribe/Unsubscribe] [Paul Sladen's vserver stuff]
Generated on Fri 04 Nov 2005 - 01:09:18 GMT by hypermail 2.1.8