NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/3019: a client can write to a read-only exported file system



The following reply was made to PR kern/3019; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Manuel Bouyer <manuel.bouyer%lip6.fr@localhost>
Cc: gnats-bugs%NetBSD.org@localhost, netbsd-bugs%NetBSD.org@localhost
Subject: Re: kern/3019: a client can write to a read-only exported file system
Date: Tue, 2 Apr 2024 22:13:22 +0000

 Can you still reproduce PR 3019?
 
 I can't reproduce it.
 
 I set up an nfs server with a read/write file system /export/erlite3,
 and a read-only export in /etc/exports, on 10.10.2.1:
 
 server# grep -e -ro /etc/exports
 /export/erlite3 -ro -network 10.10.2.0/24 -maproot=3Droot:wheel
 server# mount | grep erlite3
 rpool/export/erlite3 on /export/erlite3 type zfs (noatime, NFS exported, lo=
 cal)
 
 I mounted the file system read/write on the client 10.10.2.2:
 
 client# mount
 10.10.2.1:/export/erlite3 on / type nfs
 
 And write attempts on the client fail with EROFS:
 
 client# cd /tmp
 client# touch x
 touch: x: Read-only file system
 client# cat >toto
 -sh: cannot create toto: read-only file system
 
 
 I reviewed all the NFS server RPC definitions in sys/nfs, and I
 couldn't find one that fails to block writes on read-only exports with
 EROFS.
 
 Here's my notes from auditing these paths.  I also skimmed through the
 same paths in the netbsd-1-1 branch, and I couldn't find any holes
 there either, although I don't have any 1.1 or 1.2_BETA systems handy
 to test.  So I'm struggling to find how this bug could have ever been
 there, and if it was, how it was fixed in the time between when it was
 filed and now.
 
 
 * nfsrv_null
 
 doesn't do anything
 
 * nfsrv_getattr
 
 read-only, VOP_GETATTR
 
 * nfsrv_setattr
 
 All paths go through a read-only check that fails with EROFS:
 
     359 	if (va.va_size =3D=3D ((u_quad_t)((quad_t) -1))) {
     360 		if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
     361 			error =3D EROFS;
     362 			goto out;
     363 		}
     364 	} else {
     365 		if (vp->v_type =3D=3D VDIR) {
     366 			error =3D EISDIR;
     367 			goto out;
     368 		} else if ((error =3D nfsrv_access(vp, VWRITE, cred, rdonly,
     369 			lwp, 0)) !=3D 0)
     370 			goto out;
     371 	}
     372 	error =3D VOP_SETATTR(vp, &va, cred);
 
 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#359
 
 * nfsrv_lookup
 
 read-only, namei LOOKUP
 
 * nfsrv3_access
 
 read-only, VOP_GETATTR and VOP_ACCESS
 
 * nfsrv_readlink
 
 read-only, VOP_READLINK and VOP_GETATTR
 
 * nfsrv_read
 
 read-only, VOP_GETATTR, VOP_ACCESS, VOP_READ, uvm loan
 
 * nfsrv_write
 
 All paths to VOP_WRITE go through nfsrv_access check:
 
     927 	error =3D nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
     928 		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
     929 	if (error) {
     930 		nfsm_reply(2 * NFSX_UNSIGNED);
     931 		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
     932 		return (0);
     933 	}
     934 	if (v3)
     935 		forat_ret =3D VOP_GETATTR(vp, &forat, cred);
     936 	if (vp->v_type !=3D VREG) {
     937 		if (v3)
     938 			error =3D EINVAL;
     939 		else
     940 			error =3D (vp->v_type =3D=3D VDIR) ? EISDIR : EACCES;
     941 	}
     942 	if (!error) {
     943 		nqsrv_getl(vp, ND_WRITE);
     944 		error =3D nfsrv_access(vp, VWRITE, cred, rdonly, lwp, 1);
     945 	}
     946 	if (error) {
     947 		vput(vp);
     948 		nfsm_reply(NFSX_WCCDATA(v3));
     949 		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
     950 		return (0);
     951 	}
 ...
     984 		error =3D VOP_WRITE(vp, uiop, ioflags, cred);
 
 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#927
 
 * nfsrv_create
 
 This goes through namei with nameiop CREATE:
 
 
    1450 	error =3D nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
    1451 		&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
    1452 		lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
 
 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#1450
 
     177 	error =3D nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp,
     178 	    nam, &rdonly, kerbflag, pubflag);
     179 	if (error)
     180 		goto out;
     181 	if (dp->v_type !=3D VDIR) {
     182 		vrele(dp);
     183 		error =3D ENOTDIR;
     184 		goto out;
     185 	}
     186=20
     187 	if (rdonly)
     188 		cnp->cn_flags |=3D RDONLY;
 ...
     271 	error =3D lookup_for_nfsd(ndp, dp, neverfollow);
 
 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_srvsubs.c#177
 
 lookup_for_nfsd goes through namei_tryemulroot:
 
    1978 	namei_init(&state, ndp);
    1979 	error =3D namei_tryemulroot(&state,
    1980 				  neverfollow, 1/*inhibitmagic*/, 1/*isnfsd*/);
    1981 	namei_cleanup(&state);
    1982=20
    1983 	if (error) {
    1984 		/* make sure no stray refs leak out */
    1985 		KASSERT(ndp->ni_dvp =3D=3D NULL);
    1986 		KASSERT(ndp->ni_vp =3D=3D NULL);
    1987 	}
    1988=20
    1989 	return error;
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1978
 
 namei_tryemulroot goes through namei_oneroot:
 
    1906 	error =3D namei_oneroot(state, neverfollow, inhibitmagic, isnfsd);
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1906
 
 namei_oneroot sets state->rdonly according to cnp->cn_flags & RDONLY:
 
    1507 	state->rdonly =3D cnp->cn_flags & RDONLY;
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1507
 
 It starts with lookup_fastforward:
 
    1533 		/*
    1534 		 * Parse out the first path name component that we need to
    1535 		 * to consider.  While doing this, attempt to use the name
    1536 		 * cache to fast-forward through as many "easy" to find
    1537 		 * components of the path as possible.
    1538 		 */
    1539 		error =3D lookup_fastforward(state, &searchdir, &foundobj);
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1533
 
 But that deliberately doesn't handle the last component:
 
    1306 		/*
    1307 		 * Can't deal with last component when modifying; this needs
    1308 		 * searchdir locked and VOP_LOOKUP() called (which can and
    1309 		 * does modify state, despite the name).  NB: this case means
    1310 		 * terminal is never set true when LOCKPARENT.
    1311 		 */
    1312 		if ((cnp->cn_flags & ISLASTCN) !=3D 0) {
    1313 			if (cnp->cn_nameiop !=3D LOOKUP ||
    1314 			    (cnp->cn_flags & LOCKPARENT) !=3D 0) {
    1315 				error =3D EOPNOTSUPP;
    1316 				break;
    1317 			}
    1318 		}
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1306
 
 Back in namei_oneroot, it calls into lookup_once to handle the last
 step:
 
    1541 		/*
    1542 		 * If we didn't get a good answer from the namecache, then
    1543 		 * go directly to the file system.
    1544 		 */
    1545 		if (error =3D=3D EOPNOTSUPP) {
    1546 			error =3D lookup_once(state, searchdir, &searchdir,
    1547 			    &foundobj, &searchdir_locked);
    1548 		}
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1541
 
 And for the end of a path, lookup_once refuses with EROFS to create
 anything if state->rdonly is set:
 
    1201 		/*
    1202 		 * If creating and at end of pathname, then can consider
    1203 		 * allowing file to be created.
    1204 		 */
    1205 		if (state->rdonly) {
    1206 			error =3D EROFS;
    1207 			goto done;
    1208 		}
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1201
 
 * nfsrv_mkdir
 
 Blocked by namei CREATE like nfsrv_create.
 
 * nfsrv_symlink
 
 Blocked by namei CREATE like nfsrv_create.
 
 * nfsrv_mknod
 
 Blocked by namei CREATE like nfsrv_create.
 
 * nfsrv_remove
 
 Blocked by namei DELETE:
 
    1818 		/*
    1819 		 * Disallow directory write attempts on read-only lookups.
    1820 		 * Prefers EEXIST over EROFS for the CREATE case.
    1821 		 */
    1822 		if (state->rdonly &&
    1823 		    (cnp->cn_nameiop =3D=3D DELETE || cnp->cn_nameiop =3D=3D RENA=
 ME)) {
    1824 			if (searchdir) {
    1825 				if (searchdir_locked) {
    1826 					vput(searchdir);
    1827 					searchdir_locked =3D false;
    1828 				} else {
    1829 					vrele(searchdir);
    1830 				}
    1831 				searchdir =3D NULL;
    1832 			}
    1833 			vrele(foundobj);
    1834 			foundobj =3D NULL;
    1835 			ndp->ni_dvp =3D NULL;
    1836 			ndp->ni_vp =3D NULL;
    1837 			state->attempt_retry =3D 1;
    1838 			return EROFS;
    1839 		}
 
 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1818
 
 * nfsrv_rmdir
 
 Blocked by namei DELETE like nfsrv_remove.
 
 * nfsrv_rename
 
 Blocked by namei DELETE and RENAME like nfsrv_remove.
 
 * nfsrv_link
 
 Blocked by namei CREATE like nfsrv_create.
 
 * nfsrv_readdir
 
 read-only, VOP_GETATTR and VOP_READDIR
 
 * nfsrv_readdirplus
 
 read-only, VOP_GETATTR, VOP_READDIR, and VOP_VGET
 
 * nfsrv_statfs
 
 read-only, VFS_STATVFS and VOP_GETATTR
 
 * nfsrv_fsinfo
 
 read-only, VOP_GETATTR
 
 * nfsrv_pathconf
 
 read-only, VOP_PATHCONF
 
 * nfsrv_commit
 
 XXX VOP_FSYNC -- maybe this should be forbidden?
 
 * nfsrv_noop
 
 read-only, noop
 


Home | Main Index | Thread Index | Old Index