svn commit: r344756 - head/sys/fs/ext2fs
Fedor Uporov
fsu at FreeBSD.org
Mon Mar 4 11:27:48 UTC 2019
Author: fsu
Date: Mon Mar 4 11:27:47 2019
New Revision: 344756
URL: https://svnweb.freebsd.org/changeset/base/344756
Log:
Do not read the on-disk inode in case of vnode allocation.
Reported by: Christopher Krah <krah at protonmail.com>
Reported as: FS-6-EXT2-4: Denial Of Service in mkdir-0 (ext2_mkdir/vn_rdwr)
Reviewed by: pfg
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D19327
Modified:
head/sys/fs/ext2fs/ext2_alloc.c
Modified: head/sys/fs/ext2fs/ext2_alloc.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_alloc.c Mon Mar 4 11:19:21 2019 (r344755)
+++ head/sys/fs/ext2fs/ext2_alloc.c Mon Mar 4 11:27:47 2019 (r344756)
@@ -373,10 +373,12 @@ int
ext2_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp)
{
struct timespec ts;
- struct inode *pip;
struct m_ext2fs *fs;
- struct inode *ip;
struct ext2mount *ump;
+ struct inode *pip;
+ struct inode *ip;
+ struct vnode *vp;
+ struct thread *td;
ino_t ino, ipref;
int error, cg;
@@ -404,33 +406,69 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred
}
ipref = cg * fs->e2fs->e2fs_ipg + 1;
ino = (ino_t)ext2_hashalloc(pip, cg, (long)ipref, mode, ext2_nodealloccg);
-
if (ino == 0)
goto noinodes;
- error = VFS_VGET(pvp->v_mount, ino, LK_EXCLUSIVE, vpp);
+
+ td = curthread;
+ error = vfs_hash_get(ump->um_mountp, ino, LK_EXCLUSIVE, td, vpp, NULL, NULL);
+ if (error || *vpp != NULL) {
+ EXT2_UNLOCK(ump);
+ return (error);
+ }
+
+ ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO);
+ if (ip == NULL) {
+ EXT2_UNLOCK(ump);
+ return (ENOMEM);
+ }
+
+ /* Allocate a new vnode/inode. */
+ if ((error = getnewvnode("ext2fs", ump->um_mountp, &ext2_vnodeops, &vp)) != 0) {
+ free(ip, M_EXT2NODE);
+ EXT2_UNLOCK(ump);
+ return (error);
+ }
+
+ vp->v_data = ip;
+ ip->i_vnode = vp;
+ ip->i_e2fs = fs = ump->um_e2fs;
+ ip->i_ump = ump;
+ ip->i_number = ino;
+ ip->i_block_group = ino_to_cg(fs, ino);
+ ip->i_next_alloc_block = 0;
+ ip->i_next_alloc_goal = 0;
+
+ lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
+ error = insmntque(vp, ump->um_mountp);
if (error) {
- ext2_vfree(pvp, ino, mode);
+ free(ip, M_EXT2NODE);
+ EXT2_UNLOCK(ump);
return (error);
}
- ip = VTOI(*vpp);
- /*
- * The question is whether using VGET was such good idea at all:
- * Linux doesn't read the old inode in when it is allocating a
- * new one. I will set at least i_size and i_blocks to zero.
- */
- ip->i_flag = 0;
- ip->i_size = 0;
- ip->i_blocks = 0;
- ip->i_mode = 0;
- ip->i_flags = 0;
+ error = vfs_hash_insert(vp, ino, LK_EXCLUSIVE, td, vpp, NULL, NULL);
+ if (error || *vpp != NULL) {
+ *vpp = NULL;
+ free(ip, M_EXT2NODE);
+ EXT2_UNLOCK(ump);
+ return (error);
+ }
+
+ if ((error = ext2_vinit(ump->um_mountp, &ext2_fifoops, &vp)) != 0) {
+ vput(vp);
+ *vpp = NULL;
+ free(ip, M_EXT2NODE);
+ EXT2_UNLOCK(ump);
+ return (error);
+ }
+
if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_EXTENTS)
&& (S_ISREG(mode) || S_ISDIR(mode)))
ext4_ext_tree_init(ip);
else
memset(ip->i_data, 0, sizeof(ip->i_data));
-
+
/*
* Set up a new generation number for this inode.
* Avoid zero values.
@@ -443,10 +481,10 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred
ip->i_birthtime = ts.tv_sec;
ip->i_birthnsec = ts.tv_nsec;
-/*
-printf("ext2_valloc: allocated inode %d\n", ino);
-*/
+ *vpp = vp;
+
return (0);
+
noinodes:
EXT2_UNLOCK(ump);
ext2_fserr(fs, cred->cr_uid, "out of inodes");
More information about the svn-src-all
mailing list