Re: [PATCH 6/6] efivarfs: fix error on write to new variable leaving remnants

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

 



On Tue, Dec 10, 2024 at 12:02:24PM -0500, James Bottomley wrote:
> Make variable cleanup go through the fops release mechanism and use
> zero inode size as the indicator to delete the file.  Since all EFI
> variables must have an initial u32 attribute, zero size occurs either
> because the update deleted the variable or because an unsuccessful
> write after create caused the size never to be set in the first place.
> 
> Even though this fixes the bug that a create either not followed by a
> write or followed by a write that errored would leave a remnant file
> for the variable, the file will appear momentarily globally visible
> until the close of the fd deletes it.  This is safe because the normal
> filesystem operations will mediate any races; however, it is still
> possible for a directory listing at that instant between create and
> close contain a variable that doesn't exist in the EFI table.
> 
> Signed-off-by: James Bottomley <James.Bottomley@xxxxxxxxxxxxxxxxxxxxx>
> ---
>  fs/efivarfs/file.c | 31 ++++++++++++++++++++++---------
>  1 file changed, 22 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 23c51d62f902..edf363f395f5 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -38,22 +38,24 @@ static ssize_t efivarfs_file_write(struct file *file,
>  
>  	bytes = efivar_entry_set_get_size(var, attributes, &datasize,
>  					  data, &set);
> -	if (!set && bytes) {
> +	if (!set) {
>  		if (bytes == -ENOENT)
>  			bytes = -EIO;
>  		goto out;
>  	}
>  
> +	inode_lock(inode);
>  	if (bytes == -ENOENT) {
> -		drop_nlink(inode);
> -		d_delete(file->f_path.dentry);
> -		dput(file->f_path.dentry);
> +		/*
> +		 * zero size signals to release that the write deleted
> +		 * the variable
> +		 */
> +		i_size_write(inode, 0);
>  	} else {
> -		inode_lock(inode);
>  		i_size_write(inode, datasize + sizeof(attributes));
>  		inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
> -		inode_unlock(inode);
>  	}
> +	inode_unlock(inode);
>  
>  	bytes = count;
>  
> @@ -106,8 +108,19 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
>  	return size;
>  }
>  
> +static int efivarfs_file_release(struct inode *inode, struct file *file)
> +{
> +	if (i_size_read(inode) == 0) {
> +		drop_nlink(inode);
> +		d_delete(file->f_path.dentry);
> +		dput(file->f_path.dentry);
> +	}

Without wider context the dput() looks UAF-y as __fput() will do:

struct dentry *dentry = file->f_path.dentry;
if (file->f_op->release)
        file->f_op->release(inode, file);
dput(dentry);

Is there an extra reference on file->f_path.dentry taken somewhere?




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux