Respect RLIMIT_FSIZE when extending a file via truncat(2)/ftruncate(2).
authormillert <millert@openbsd.org>
Mon, 23 May 2022 15:17:11 +0000 (15:17 +0000)
committermillert <millert@openbsd.org>
Mon, 23 May 2022 15:17:11 +0000 (15:17 +0000)
This refactors the commin parts of sys_truncate() and sys_ftruncate()
into dotruncate().  If the new size of the file is larger than the
RLIMIT_FSIZE limit _and_ the file is being extended, not truncated,
return EFBIG.  Adapted from a diff by Piotr Durlej.
With help from and OK by deraadt@ guenther@.

lib/libc/sys/truncate.2
sys/kern/vfs_syscalls.c

index 820583c..6356841 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: truncate.2,v 1.20 2020/02/11 13:19:17 schwarze Exp $
+.\"    $OpenBSD: truncate.2,v 1.21 2022/05/23 15:17:11 millert Exp $
 .\"    $NetBSD: truncate.2,v 1.7 1995/02/27 12:39:00 cgd Exp $
 .\"
 .\" Copyright (c) 1983, 1991, 1993
@@ -30,7 +30,7 @@
 .\"
 .\"     @(#)truncate.2 8.1 (Berkeley) 6/4/93
 .\"
-.Dd $Mdocdate: February 11 2020 $
+.Dd $Mdocdate: May 23 2022 $
 .Dt TRUNCATE 2
 .Os
 .Sh NAME
@@ -73,7 +73,8 @@ is a negative value.
 .It Bq Er EFBIG
 The
 .Fa length
-exceeds the maximum file size of the underlying filesystem.
+exceeds the process's file size limit or the maximum file size
+of the underlying filesystem.
 .It Bq Er EIO
 An I/O error occurred updating the inode.
 .El
index 31122a2..4a8e37a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vfs_syscalls.c,v 1.356 2022/02/17 03:12:34 rob Exp $  */
+/*     $OpenBSD: vfs_syscalls.c,v 1.357 2022/05/23 15:17:11 millert Exp $      */
 /*     $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $        */
 
 /*
@@ -60,6 +60,8 @@
 #include <sys/ktrace.h>
 #include <sys/unistd.h>
 #include <sys/specdev.h>
+#include <sys/resourcevar.h>
+#include <sys/signalvar.h>
 
 #include <sys/syscallargs.h>
 
@@ -2808,6 +2810,35 @@ dofutimens(struct proc *p, int fd, struct timespec ts[2])
        return (dovutimens(p, vp, ts));
 }
 
+/*
+ * Truncate a file given a vnode.
+ */
+int
+dotruncate(struct proc *p, struct vnode *vp, off_t len)
+{
+       struct vattr vattr;
+       int error;
+
+       if (len < 0)
+               return EINVAL;
+       if (vp->v_type == VDIR)
+               return EISDIR;
+       if ((error = vn_writechk(vp)) != 0)
+               return error;
+       if (vp->v_type == VREG && len > lim_cur_proc(p, RLIMIT_FSIZE)) {
+               if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0)
+                       return error;
+               if (len > vattr.va_size) {
+                       /* if extending over the limit, send signal and fail */
+                       psignal(p, SIGXFSZ);
+                       return EFBIG;
+               }
+       }
+       VATTR_NULL(&vattr);
+       vattr.va_size = len;
+       return VOP_SETATTR(vp, &vattr, p->p_ucred, p);
+}
+
 /*
  * Truncate a file given its path name.
  */
@@ -2819,7 +2850,6 @@ sys_truncate(struct proc *p, void *v, register_t *retval)
                syscallarg(off_t) length;
        } */ *uap = v;
        struct vnode *vp;
-       struct vattr vattr;
        int error;
        struct nameidata nd;
 
@@ -2830,14 +2860,8 @@ sys_truncate(struct proc *p, void *v, register_t *retval)
                return (error);
        vp = nd.ni_vp;
        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-       if (vp->v_type == VDIR)
-               error = EISDIR;
-       else if ((error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0 &&
-           (error = vn_writechk(vp)) == 0) {
-               VATTR_NULL(&vattr);
-               vattr.va_size = SCARG(uap, length);
-               error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
-       }
+       if ((error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0)
+               error = dotruncate(p, vp, SCARG(uap, length));
        vput(vp);
        return (error);
 }
@@ -2852,28 +2876,19 @@ sys_ftruncate(struct proc *p, void *v, register_t *retval)
                syscallarg(int) fd;
                syscallarg(off_t) length;
        } */ *uap = v;
-       struct vattr vattr;
        struct vnode *vp;
        struct file *fp;
-       off_t len;
        int error;
 
        if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
                return (error);
-       len = SCARG(uap, length);
-       if ((fp->f_flag & FWRITE) == 0 || len < 0) {
+       if ((fp->f_flag & FWRITE) == 0) {
                error = EINVAL;
                goto bad;
        }
        vp = fp->f_data;
        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-       if (vp->v_type == VDIR)
-               error = EISDIR;
-       else if ((error = vn_writechk(vp)) == 0) {
-               VATTR_NULL(&vattr);
-               vattr.va_size = len;
-               error = VOP_SETATTR(vp, &vattr, fp->f_cred, p);
-       }
+       error = dotruncate(p, vp, SCARG(uap, length));
        VOP_UNLOCK(vp);
 bad:
        FRELE(fp, p);