mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	fs: Pass AT_GETATTR_NOSEC flag to getattr interface function
When vfs_getattr_nosec() calls a filesystem's getattr interface function
then the 'nosec' should propagate into this function so that
vfs_getattr_nosec() can again be called from the filesystem's gettattr
rather than vfs_getattr(). The latter would add unnecessary security
checks that the initial vfs_getattr_nosec() call wanted to avoid.
Therefore, introduce the getattr flag GETATTR_NOSEC and allow to pass
with the new getattr_flags parameter to the getattr interface function.
In overlayfs and ecryptfs use this flag to determine which one of the
two functions to call.
In a recent code change introduced to IMA vfs_getattr_nosec() ended up
calling vfs_getattr() in overlayfs, which in turn called
security_inode_getattr() on an exiting process that did not have
current->fs set anymore, which then caused a kernel NULL pointer
dereference. With this change the call to security_inode_getattr() can
be avoided, thus avoiding the NULL pointer dereference.
Reported-by: <syzbot+a67fc5321ffb4b311c98@syzkaller.appspotmail.com>
Fixes: db1d1e8b98 ("IMA: use vfs_getattr_nosec to get the i_version")
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: <linux-fsdevel@vger.kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Tyler Hicks <code@tyhicks.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Suggested-by: Christian Brauner <brauner@kernel.org>
Co-developed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Link: https://lore.kernel.org/r/20231002125733.1251467-1-stefanb@linux.vnet.ibm.com
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									b85ea95d08
								
							
						
					
					
						commit
						8a924db2d7
					
				| @ -998,6 +998,14 @@ static int ecryptfs_getattr_link(struct mnt_idmap *idmap, | |||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int ecryptfs_do_getattr(const struct path *path, struct kstat *stat, | ||||||
|  | 			       u32 request_mask, unsigned int flags) | ||||||
|  | { | ||||||
|  | 	if (flags & AT_GETATTR_NOSEC) | ||||||
|  | 		return vfs_getattr_nosec(path, stat, request_mask, flags); | ||||||
|  | 	return vfs_getattr(path, stat, request_mask, flags); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int ecryptfs_getattr(struct mnt_idmap *idmap, | static int ecryptfs_getattr(struct mnt_idmap *idmap, | ||||||
| 			    const struct path *path, struct kstat *stat, | 			    const struct path *path, struct kstat *stat, | ||||||
| 			    u32 request_mask, unsigned int flags) | 			    u32 request_mask, unsigned int flags) | ||||||
| @ -1006,8 +1014,8 @@ static int ecryptfs_getattr(struct mnt_idmap *idmap, | |||||||
| 	struct kstat lower_stat; | 	struct kstat lower_stat; | ||||||
| 	int rc; | 	int rc; | ||||||
| 
 | 
 | ||||||
| 	rc = vfs_getattr(ecryptfs_dentry_to_lower_path(dentry), &lower_stat, | 	rc = ecryptfs_do_getattr(ecryptfs_dentry_to_lower_path(dentry), | ||||||
| 			 request_mask, flags); | 				 &lower_stat, request_mask, flags); | ||||||
| 	if (!rc) { | 	if (!rc) { | ||||||
| 		fsstack_copy_attr_all(d_inode(dentry), | 		fsstack_copy_attr_all(d_inode(dentry), | ||||||
| 				      ecryptfs_inode_to_lower(d_inode(dentry))); | 				      ecryptfs_inode_to_lower(d_inode(dentry))); | ||||||
|  | |||||||
| @ -171,7 +171,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, | |||||||
| 
 | 
 | ||||||
| 	type = ovl_path_real(dentry, &realpath); | 	type = ovl_path_real(dentry, &realpath); | ||||||
| 	old_cred = ovl_override_creds(dentry->d_sb); | 	old_cred = ovl_override_creds(dentry->d_sb); | ||||||
| 	err = vfs_getattr(&realpath, stat, request_mask, flags); | 	err = ovl_do_getattr(&realpath, stat, request_mask, flags); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
| @ -196,8 +196,8 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, | |||||||
| 					(!is_dir ? STATX_NLINK : 0); | 					(!is_dir ? STATX_NLINK : 0); | ||||||
| 
 | 
 | ||||||
| 			ovl_path_lower(dentry, &realpath); | 			ovl_path_lower(dentry, &realpath); | ||||||
| 			err = vfs_getattr(&realpath, &lowerstat, | 			err = ovl_do_getattr(&realpath, &lowerstat, lowermask, | ||||||
| 					  lowermask, flags); | 					     flags); | ||||||
| 			if (err) | 			if (err) | ||||||
| 				goto out; | 				goto out; | ||||||
| 
 | 
 | ||||||
| @ -249,8 +249,8 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, | |||||||
| 
 | 
 | ||||||
| 			ovl_path_lowerdata(dentry, &realpath); | 			ovl_path_lowerdata(dentry, &realpath); | ||||||
| 			if (realpath.dentry) { | 			if (realpath.dentry) { | ||||||
| 				err = vfs_getattr(&realpath, &lowerdatastat, | 				err = ovl_do_getattr(&realpath, &lowerdatastat, | ||||||
| 						  lowermask, flags); | 						     lowermask, flags); | ||||||
| 				if (err) | 				if (err) | ||||||
| 					goto out; | 					goto out; | ||||||
| 			} else { | 			} else { | ||||||
|  | |||||||
| @ -408,6 +408,14 @@ static inline bool ovl_open_flags_need_copy_up(int flags) | |||||||
| 	return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); | 	return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline int ovl_do_getattr(const struct path *path, struct kstat *stat, | ||||||
|  | 				 u32 request_mask, unsigned int flags) | ||||||
|  | { | ||||||
|  | 	if (flags & AT_GETATTR_NOSEC) | ||||||
|  | 		return vfs_getattr_nosec(path, stat, request_mask, flags); | ||||||
|  | 	return vfs_getattr(path, stat, request_mask, flags); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* util.c */ | /* util.c */ | ||||||
| int ovl_get_write_access(struct dentry *dentry); | int ovl_get_write_access(struct dentry *dentry); | ||||||
| void ovl_put_write_access(struct dentry *dentry); | void ovl_put_write_access(struct dentry *dentry); | ||||||
|  | |||||||
| @ -133,7 +133,8 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, | |||||||
| 	idmap = mnt_idmap(path->mnt); | 	idmap = mnt_idmap(path->mnt); | ||||||
| 	if (inode->i_op->getattr) | 	if (inode->i_op->getattr) | ||||||
| 		return inode->i_op->getattr(idmap, path, stat, | 		return inode->i_op->getattr(idmap, path, stat, | ||||||
| 					    request_mask, query_flags); | 					    request_mask, | ||||||
|  | 					    query_flags | AT_GETATTR_NOSEC); | ||||||
| 
 | 
 | ||||||
| 	generic_fillattr(idmap, request_mask, inode, stat); | 	generic_fillattr(idmap, request_mask, inode, stat); | ||||||
| 	return 0; | 	return 0; | ||||||
| @ -166,6 +167,9 @@ int vfs_getattr(const struct path *path, struct kstat *stat, | |||||||
| { | { | ||||||
| 	int retval; | 	int retval; | ||||||
| 
 | 
 | ||||||
|  | 	if (WARN_ON_ONCE(query_flags & AT_GETATTR_NOSEC)) | ||||||
|  | 		return -EPERM; | ||||||
|  | 
 | ||||||
| 	retval = security_inode_getattr(path); | 	retval = security_inode_getattr(path); | ||||||
| 	if (retval) | 	if (retval) | ||||||
| 		return retval; | 		return retval; | ||||||
|  | |||||||
| @ -116,5 +116,8 @@ | |||||||
| #define AT_HANDLE_FID		AT_REMOVEDIR	/* file handle is needed to | #define AT_HANDLE_FID		AT_REMOVEDIR	/* file handle is needed to | ||||||
| 					compare object identity and may not | 					compare object identity and may not | ||||||
| 					be usable to open_by_handle_at(2) */ | 					be usable to open_by_handle_at(2) */ | ||||||
|  | #if defined(__KERNEL__) | ||||||
|  | #define AT_GETATTR_NOSEC	0x80000000 | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| #endif /* _UAPI_LINUX_FCNTL_H */ | #endif /* _UAPI_LINUX_FCNTL_H */ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Stefan Berger
						Stefan Berger