Enforce RLIMIT_FSIZE before VOP_WRITE. This adds support to file

system drivers where it was missing from and fixes one buggy
implementation.  The arguably weird semantics of the check are
maintained (v_size vs. va_bytes, overwrite).
This commit is contained in:
pooka 2010-04-23 15:38:46 +00:00
parent f7be22cf8e
commit 0c20c076ce
7 changed files with 77 additions and 83 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: msdosfs_vnops.c,v 1.64 2010/04/09 08:09:18 hannken Exp $ */ /* $NetBSD: msdosfs_vnops.c,v 1.65 2010/04/23 15:38:46 pooka Exp $ */
/*- /*-
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
@ -48,7 +48,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.64 2010/04/09 08:09:18 hannken Exp $"); __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.65 2010/04/23 15:38:46 pooka Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -564,7 +564,6 @@ msdosfs_write(void *v)
vsize_t bytelen; vsize_t bytelen;
off_t oldoff; off_t oldoff;
struct uio *uio = ap->a_uio; struct uio *uio = ap->a_uio;
struct proc *p = curproc;
struct vnode *vp = ap->a_vp; struct vnode *vp = ap->a_vp;
struct denode *dep = VTODE(vp); struct denode *dep = VTODE(vp);
struct msdosfsmount *pmp = dep->de_pmp; struct msdosfsmount *pmp = dep->de_pmp;
@ -599,17 +598,6 @@ msdosfs_write(void *v)
if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX) if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
return (EFBIG); return (EFBIG);
/*
* If they've exceeded their filesize limit, tell them about it.
*/
if (((uio->uio_offset + uio->uio_resid) >
p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
mutex_enter(proc_lock);
psignal(p, SIGXFSZ);
mutex_exit(proc_lock);
return (EFBIG);
}
fstrans_start(vp->v_mount, FSTRANS_SHARED); fstrans_start(vp->v_mount, FSTRANS_SHARED);
/* /*
* If the offset we are starting the write at is beyond the end of * If the offset we are starting the write at is beyond the end of

View File

@ -1,4 +1,4 @@
/* $NetBSD: smbfs_io.c,v 1.33 2009/06/22 21:13:50 njoly Exp $ */ /* $NetBSD: smbfs_io.c,v 1.34 2010/04/23 15:38:47 pooka Exp $ */
/* /*
* Copyright (c) 2000-2001, Boris Popov * Copyright (c) 2000-2001, Boris Popov
@ -36,7 +36,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smbfs_io.c,v 1.33 2009/06/22 21:13:50 njoly Exp $"); __KERNEL_RCSID(0, "$NetBSD: smbfs_io.c,v 1.34 2010/04/23 15:38:47 pooka Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -244,9 +244,8 @@ smbfs_writevnode(struct vnode *vp, struct uio *uiop,
{ {
struct smbmount *smp = VTOSMBFS(vp); struct smbmount *smp = VTOSMBFS(vp);
struct smbnode *np = VTOSMB(vp); struct smbnode *np = VTOSMB(vp);
struct smb_cred scred;
struct lwp *l = curlwp; struct lwp *l = curlwp;
struct proc *p = l->l_proc; struct smb_cred scred;
int error = 0; int error = 0;
int extended = 0; int extended = 0;
size_t resid = uiop->uio_resid; size_t resid = uiop->uio_resid;
@ -270,6 +269,8 @@ smbfs_writevnode(struct vnode *vp, struct uio *uiop,
} }
if (ioflag & IO_APPEND) { if (ioflag & IO_APPEND) {
#if notyet #if notyet
struct proc *p = curproc;
/* /*
* File size can be changed by another client * File size can be changed by another client
*/ */
@ -277,18 +278,19 @@ smbfs_writevnode(struct vnode *vp, struct uio *uiop,
error = VOP_GETATTR(vp, &vattr, cred, td); error = VOP_GETATTR(vp, &vattr, cred, td);
if (error) if (error)
return (error); return (error);
if (np->n_size + uiop->uio_resid >
p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(p, SIGXFSZ);
mutex_exit(proc_lock);
return EFBIG;
}
#endif #endif
uiop->uio_offset = np->n_size; uiop->uio_offset = np->n_size;
} }
} }
if (uiop->uio_resid == 0) if (uiop->uio_resid == 0)
return 0; return 0;
if (p && uiop->uio_offset + uiop->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(p, SIGXFSZ);
mutex_exit(proc_lock);
return EFBIG;
}
smb_makescred(&scred, l, cred); smb_makescred(&scred, l, cred);
error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
SMBVDEBUG("after: ofs=%lld,resid=%zu,err=%d\n", SMBVDEBUG("after: ofs=%lld,resid=%zu,err=%d\n",

View File

@ -1,4 +1,4 @@
/* $NetBSD: tmpfs_vnops.c,v 1.68 2010/03/29 13:11:33 pooka Exp $ */ /* $NetBSD: tmpfs_vnops.c,v 1.69 2010/04/23 15:38:47 pooka Exp $ */
/* /*
* Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@ -35,7 +35,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.68 2010/03/29 13:11:33 pooka Exp $"); __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.69 2010/04/23 15:38:47 pooka Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/dirent.h> #include <sys/dirent.h>
@ -594,7 +594,6 @@ tmpfs_write(void *v)
bool extended; bool extended;
int error; int error;
off_t oldsize; off_t oldsize;
struct proc *p = curproc;
struct tmpfs_node *node; struct tmpfs_node *node;
struct uvm_object *uobj; struct uvm_object *uobj;
@ -613,15 +612,6 @@ tmpfs_write(void *v)
goto out; goto out;
} }
if (((uio->uio_offset + uio->uio_resid) >
p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
mutex_enter(proc_lock);
psignal(p, SIGXFSZ);
mutex_exit(proc_lock);
error = EFBIG;
goto out;
}
if (ioflag & IO_APPEND) if (ioflag & IO_APPEND)
uio->uio_offset = node->tn_size; uio->uio_offset = node->tn_size;

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_vnops.c,v 1.170 2010/03/29 13:11:32 pooka Exp $ */ /* $NetBSD: vfs_vnops.c,v 1.171 2010/04/23 15:38:46 pooka Exp $ */
/*- /*-
* Copyright (c) 2009 The NetBSD Foundation, Inc. * Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -66,7 +66,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.170 2010/03/29 13:11:32 pooka Exp $"); __KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.171 2010/04/23 15:38:46 pooka Exp $");
#include "veriexec.h" #include "veriexec.h"
@ -351,6 +351,32 @@ vn_close(struct vnode *vp, int flags, kauth_cred_t cred)
return (error); return (error);
} }
static int
enforce_rlimit_fsize(struct vnode *vp, struct uio *uio, int ioflag)
{
struct lwp *l = curlwp;
off_t testoff;
if (uio->uio_rw != UIO_WRITE || vp->v_type != VREG)
return 0;
KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
if (ioflag & IO_APPEND)
testoff = vp->v_size;
else
testoff = uio->uio_offset;
if (testoff + uio->uio_resid >
l->l_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(l->l_proc, SIGXFSZ);
mutex_exit(proc_lock);
return EFBIG;
}
return 0;
}
/* /*
* Package up an I/O request on a vnode into a uio and do it. * Package up an I/O request on a vnode into a uio and do it.
*/ */
@ -382,16 +408,23 @@ vn_rdwr(enum uio_rw rw, struct vnode *vp, void *base, int len, off_t offset,
} else { } else {
auio.uio_vmspace = l->l_proc->p_vmspace; auio.uio_vmspace = l->l_proc->p_vmspace;
} }
if ((error = enforce_rlimit_fsize(vp, &auio, ioflg)) != 0)
goto out;
if (rw == UIO_READ) { if (rw == UIO_READ) {
error = VOP_READ(vp, &auio, ioflg, cred); error = VOP_READ(vp, &auio, ioflg, cred);
} else { } else {
error = VOP_WRITE(vp, &auio, ioflg, cred); error = VOP_WRITE(vp, &auio, ioflg, cred);
} }
if (aresid) if (aresid)
*aresid = auio.uio_resid; *aresid = auio.uio_resid;
else else
if (auio.uio_resid && error == 0) if (auio.uio_resid && error == 0)
error = EIO; error = EIO;
out:
if ((ioflg & IO_NODELOCKED) == 0) { if ((ioflg & IO_NODELOCKED) == 0) {
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
} }
@ -520,7 +553,12 @@ vn_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
uio->uio_offset = *offset; uio->uio_offset = *offset;
count = uio->uio_resid; count = uio->uio_resid;
if ((error = enforce_rlimit_fsize(vp, uio, ioflag)) != 0)
goto out;
error = VOP_WRITE(vp, uio, ioflag, cred); error = VOP_WRITE(vp, uio, ioflag, cred);
if (flags & FOF_UPDATE_OFFSET) { if (flags & FOF_UPDATE_OFFSET) {
if (ioflag & IO_APPEND) { if (ioflag & IO_APPEND) {
/* /*
@ -535,6 +573,8 @@ vn_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
} else } else
*offset += count - uio->uio_resid; *offset += count - uio->uio_resid;
} }
out:
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
return (error); return (error);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: nfs_bio.c,v 1.183 2009/03/14 14:46:11 dsl Exp $ */ /* $NetBSD: nfs_bio.c,v 1.184 2010/04/23 15:38:47 pooka Exp $ */
/* /*
* Copyright (c) 1989, 1993 * Copyright (c) 1989, 1993
@ -35,7 +35,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.183 2009/03/14 14:46:11 dsl Exp $"); __KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.184 2010/04/23 15:38:47 pooka Exp $");
#ifdef _KERNEL_OPT #ifdef _KERNEL_OPT
#include "opt_nfs.h" #include "opt_nfs.h"
@ -480,6 +480,19 @@ nfs_write(void *v)
if (error) if (error)
return (error); return (error);
uio->uio_offset = np->n_size; uio->uio_offset = np->n_size;
/*
* This is already checked above VOP_WRITE, but recheck
* the append case here to make sure our idea of the
* file size is as fresh as possible.
*/
if (uio->uio_offset + uio->uio_resid >
l->l_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(l->l_proc, SIGXFSZ);
mutex_exit(proc_lock);
return (EFBIG);
}
} }
if (uio->uio_offset < 0) if (uio->uio_offset < 0)
return (EINVAL); return (EINVAL);
@ -487,17 +500,6 @@ nfs_write(void *v)
return (EFBIG); return (EFBIG);
if (uio->uio_resid == 0) if (uio->uio_resid == 0)
return (0); return (0);
/*
* Maybe this should be above the vnode op call, but so long as
* file servers have no limits, i don't think it matters
*/
if (l && l->l_proc && uio->uio_offset + uio->uio_resid >
l->l_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(l->l_proc, SIGXFSZ);
mutex_exit(proc_lock);
return (EFBIG);
}
origoff = uio->uio_offset; origoff = uio->uio_offset;
do { do {

View File

@ -1,4 +1,4 @@
/* $NetBSD: ext2fs_readwrite.c,v 1.55 2009/10/19 18:41:17 bouyer Exp $ */ /* $NetBSD: ext2fs_readwrite.c,v 1.56 2010/04/23 15:38:46 pooka Exp $ */
/*- /*-
* Copyright (c) 1993 * Copyright (c) 1993
@ -60,7 +60,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.55 2009/10/19 18:41:17 bouyer Exp $"); __KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.56 2010/04/23 15:38:46 pooka Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -224,7 +224,6 @@ ext2fs_write(void *v)
struct inode *ip; struct inode *ip;
struct m_ext2fs *fs; struct m_ext2fs *fs;
struct buf *bp; struct buf *bp;
struct proc *p;
struct ufsmount *ump; struct ufsmount *ump;
daddr_t lbn; daddr_t lbn;
off_t osize; off_t osize;
@ -270,19 +269,6 @@ ext2fs_write(void *v)
if (uio->uio_offset < 0 || if (uio->uio_offset < 0 ||
(uint64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize) (uint64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize)
return (EFBIG); return (EFBIG);
/*
* Maybe this should be above the vnode op call, but so long as
* file servers have no limits, I don't think it matters.
*/
p = curproc;
if (vp->v_type == VREG && p &&
uio->uio_offset + uio->uio_resid >
p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(p, SIGXFSZ);
mutex_exit(proc_lock);
return (EFBIG);
}
if (uio->uio_resid == 0) if (uio->uio_resid == 0)
return (0); return (0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ufs_readwrite.c,v 1.94 2009/02/22 20:28:07 ad Exp $ */ /* $NetBSD: ufs_readwrite.c,v 1.95 2010/04/23 15:38:46 pooka Exp $ */
/*- /*-
* Copyright (c) 1993 * Copyright (c) 1993
@ -32,7 +32,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: ufs_readwrite.c,v 1.94 2009/02/22 20:28:07 ad Exp $"); __KERNEL_RCSID(1, "$NetBSD: ufs_readwrite.c,v 1.95 2010/04/23 15:38:46 pooka Exp $");
#ifdef LFS_READWRITE #ifdef LFS_READWRITE
#define FS struct lfs #define FS struct lfs
@ -217,7 +217,6 @@ WRITE(void *v)
struct inode *ip; struct inode *ip;
FS *fs; FS *fs;
struct buf *bp; struct buf *bp;
struct lwp *l;
kauth_cred_t cred; kauth_cred_t cred;
daddr_t lbn; daddr_t lbn;
off_t osize, origoff, oldoff, preallocoff, endallocoff, nsize; off_t osize, origoff, oldoff, preallocoff, endallocoff, nsize;
@ -272,19 +271,6 @@ WRITE(void *v)
if (vp == fs->lfs_ivnode) if (vp == fs->lfs_ivnode)
return (EPERM); return (EPERM);
#endif #endif
/*
* Maybe this should be above the vnode op call, but so long as
* file servers have no limits, I don't think it matters.
*/
l = curlwp;
if (vp->v_type == VREG && l &&
uio->uio_offset + uio->uio_resid >
l->l_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
mutex_enter(proc_lock);
psignal(l->l_proc, SIGXFSZ);
mutex_exit(proc_lock);
return (EFBIG);
}
if (uio->uio_resid == 0) if (uio->uio_resid == 0)
return (0); return (0);