Module Name:    src
Committed By:   hannken
Date:           Thu Aug 11 10:17:44 UTC 2022

Modified Files:
        src/sys/kern: vfs_trans.c

Log Message:
Finish previous, evaluate the lowest mount on first access
to "struct mount_info" and store it here so we no longer
derefence the "struct mount" from fstrans_alloc_lwp_info().

Reported-by: syzbot+5a79214d043395b55...@syzkaller.appspotmail.com


To generate a diff of this commit:
cvs rdiff -u -r1.66 -r1.67 src/sys/kern/vfs_trans.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/vfs_trans.c
diff -u src/sys/kern/vfs_trans.c:1.66 src/sys/kern/vfs_trans.c:1.67
--- src/sys/kern/vfs_trans.c:1.66	Fri Jul  8 07:42:47 2022
+++ src/sys/kern/vfs_trans.c	Thu Aug 11 10:17:44 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_trans.c,v 1.66 2022/07/08 07:42:47 hannken Exp $	*/
+/*	$NetBSD: vfs_trans.c,v 1.67 2022/08/11 10:17:44 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2020 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.66 2022/07/08 07:42:47 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.67 2022/08/11 10:17:44 hannken Exp $");
 
 /*
  * File system transaction operations.
@@ -87,6 +87,7 @@ struct fstrans_mount_info {
 	SLIST_ENTRY(fstrans_mount_info) fmi_hash;
 	LIST_HEAD(, fscow_handler) fmi_cow_handler;
 	struct mount *fmi_mount;
+	struct fstrans_mount_info *fmi_lower_info;
 	struct lwp *fmi_owner;
 };
 SLIST_HEAD(fstrans_mount_hashhead, fstrans_mount_info);
@@ -229,13 +230,29 @@ static inline struct fstrans_mount_info 
 fstrans_mount_get(struct mount *mp)
 {
 	uint32_t indx;
-	struct fstrans_mount_info *fmi;
+	struct fstrans_mount_info *fmi, *fmi_lower;
 
 	KASSERT(mutex_owned(&fstrans_lock));
 
 	indx = fstrans_mount_hash(mp);
 	SLIST_FOREACH(fmi, &fstrans_mount_hashtab[indx], fmi_hash) {
 		if (fmi->fmi_mount == mp) {
+			if (__predict_false(mp->mnt_lower != NULL &&
+			    fmi->fmi_lower_info == NULL)) {
+				/*
+				 * Intern the lower/lowest mount into
+				 * this mount info on first lookup.
+				 */
+				KASSERT(fmi->fmi_ref_cnt == 1);
+
+				fmi_lower = fstrans_mount_get(mp->mnt_lower);
+				if (fmi_lower && fmi_lower->fmi_lower_info)
+					fmi_lower = fmi_lower->fmi_lower_info;
+				if (fmi_lower == NULL)
+					return NULL;
+				fmi->fmi_lower_info = fmi_lower;
+				fmi->fmi_lower_info->fmi_ref_cnt += 1;
+			}
 			return fmi;
 		}
 	}
@@ -262,6 +279,9 @@ fstrans_mount_dtor(struct fstrans_mount_
 	KASSERT(LIST_FIRST(&fmi->fmi_cow_handler) == NULL);
 	KASSERT(fmi->fmi_owner == NULL);
 
+	if (fmi->fmi_lower_info)
+		fstrans_mount_dtor(fmi->fmi_lower_info);
+
 	KASSERT(fstrans_gone_count > 0);
 	fstrans_gone_count -= 1;
 
@@ -287,6 +307,7 @@ fstrans_mount(struct mount *mp)
 	LIST_INIT(&newfmi->fmi_cow_handler);
 	newfmi->fmi_cow_change = false;
 	newfmi->fmi_mount = mp;
+	newfmi->fmi_lower_info = NULL;
 	newfmi->fmi_owner = NULL;
 
 	mutex_enter(&fstrans_lock);
@@ -374,7 +395,7 @@ fstrans_clear_lwp_info(void)
 static struct fstrans_lwp_info *
 fstrans_alloc_lwp_info(struct mount *mp)
 {
-	struct fstrans_lwp_info *fli;
+	struct fstrans_lwp_info *fli, *fli_lower;
 	struct fstrans_mount_info *fmi;
 
 	for (fli = curlwp->l_fstrans; fli; fli = fli->fli_succ) {
@@ -383,6 +404,32 @@ fstrans_alloc_lwp_info(struct mount *mp)
 	}
 
 	/*
+	 * Lookup mount info and get lower mount per lwp info.
+	 */
+	mutex_enter(&fstrans_lock);
+	fmi = fstrans_mount_get(mp);
+	if (fmi == NULL) {
+		mutex_exit(&fstrans_lock);
+		return NULL;
+	}
+	fmi->fmi_ref_cnt += 1;
+	mutex_exit(&fstrans_lock);
+
+	if (fmi->fmi_lower_info) {
+		fli_lower =
+		    fstrans_alloc_lwp_info(fmi->fmi_lower_info->fmi_mount);
+		if (fli_lower == NULL) {
+			mutex_enter(&fstrans_lock);
+			fstrans_mount_dtor(fmi);
+			mutex_exit(&fstrans_lock);
+
+			return NULL;
+		}
+	} else {
+		fli_lower = NULL;
+	}
+
+	/*
 	 * Allocate a new entry.
 	 */
 	fli = pool_cache_get(fstrans_lwp_cache, PR_WAITOK);
@@ -395,30 +442,18 @@ fstrans_alloc_lwp_info(struct mount *mp)
 	KASSERT(fli->fli_self == NULL);
 
 	/*
-	 * Attach the mount info if it is valid.
+	 * Attach the mount info and alias.
 	 */
 
-	mutex_enter(&fstrans_lock);
-	fmi = fstrans_mount_get(mp);
-	if (fmi == NULL) {
-		mutex_exit(&fstrans_lock);
-		pool_cache_put(fstrans_lwp_cache, fli);
-		return NULL;
-	}
 	fli->fli_self = curlwp;
 	fli->fli_mount = mp;
 	fli->fli_mountinfo = fmi;
-	fmi->fmi_ref_cnt += 1;
-	do {
-		mp = mp->mnt_lower;
-	} while (mp && mp->mnt_lower);
-	mutex_exit(&fstrans_lock);
 
 	fli->fli_succ = curlwp->l_fstrans;
 	curlwp->l_fstrans = fli;
 
-	if (mp) {
-		fli->fli_alias = fstrans_alloc_lwp_info(mp);
+	if (fli_lower) {
+		fli->fli_alias = fli_lower;
 		fli->fli_alias->fli_alias_cnt++;
 		fli = fli->fli_alias;
 	}
@@ -499,9 +534,6 @@ _fstrans_start(struct mount *mp, enum fs
 
 	s = pserialize_read_enter();
 	if (__predict_true(grant_lock(fmi, lock_type))) {
-
-		KASSERT(!fmi->fmi_gone);
-
 		fli->fli_trans_cnt = 1;
 		fli->fli_lock_type = lock_type;
 		pserialize_read_exit(s);

Reply via email to