From: Ahmad Fatoum <[email protected]>

Removing a variable frees it and clears the inode's pointer to it, but
an open file keeps the inode around with the old size. Reading, writing
or truncating it then dereferences the NULL pointer. The shell can get
there with a loop mount: after mount -o loop on a variable and rm of
it, md on /dev/loop0 or reading a file from the mount crashes.

Let's set the size of a removed variable to zero and return -ENOENT
for any further access to it.

Fixes: 8daaa21b3949 ("fs: Add a driver to access U-Boot environment variables")
Assisted-by: Claude:opus-5.5
Signed-off-by: Ahmad Fatoum <[email protected]>
---
 fs/ubootvarfs.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/fs/ubootvarfs.c b/fs/ubootvarfs.c
index b0353d7cedef..e2d2a2f4aa77 100644
--- a/fs/ubootvarfs.c
+++ b/fs/ubootvarfs.c
@@ -266,7 +266,9 @@ static int ubootvarfs_unlink(struct inode *dir, struct 
dentry *dentry)
 
                list_del(&var->list);
                free(var);
+               /* open files keep the inode, see ubootvarfs_io() */
                node->var = NULL;
+               inode->i_size = 0;
        }
 
        return simple_unlink(dir, dentry);
@@ -348,7 +350,13 @@ static int ubootvarfs_io(struct file *f, void *buf, size_t 
insize, bool read)
 {
        struct inode *inode = f->f_inode;
        struct ubootvarfs_inode *node = inode_to_node(inode);
-       void *ptr = node->var->start + f->f_pos;
+       void *ptr;
+
+       /* the variable was removed while the file was open */
+       if (!node->var)
+               return -ENOENT;
+
+       ptr = node->var->start + f->f_pos;
 
        if (read)
                memcpy(buf, ptr, insize);
@@ -376,6 +384,9 @@ static int ubootvarfs_truncate(struct file *f, loff_t size)
        struct ubootvarfs_var *var = node->var;
        loff_t delta;
 
+       if (!var)
+               return -ENOENT;
+
        if (size < 0)
                return -EINVAL;
 
-- 
2.47.3


Reply via email to