ANDROID: fuse-bpf: Ensure bpf field can never be nulled

By putting and nulling fuse_inode's bpf field in fuse_evict_inode, we
left a race condition - this inode can still be active. Do not put the
bpf program until we are doing the final free in fuse_free_inode. This
was the root cause of the reported bug.

The backing inode cannot be put in fuse_free_inode, since put_inode can
sleep and this is called from an RCU handler. But the backing inode
cannot be freed until an RCU interval, so move the put_inode to the same
location as in overlayfs, which is destroy_inode.

Remove a path in fuse_handle_bpf_prog whereby bpf can be nulled out.
When we want to be able to null/change the bpf_prog in the future, we
will have to use a mutex or maybe RCU to protect existing users. But
until this time, ban this path.

Bug: 284450048
Bug: 286939538
Test: fuse_test passes, Pixel 6 passes basic tests
Change-Id: Ie6844242f279a5b202eb021eac5a2dd3d08bf09d
Signed-off-by: Paul Lawrence <[email protected]>
diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index e292c55..19e690a 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -1273,14 +1273,12 @@ int fuse_handle_bpf_prog(struct fuse_entry_bpf *feb, struct inode *parent,
 	}
 
 	/* Cannot change existing program */
-	if (*bpf && new_bpf) {
-		bpf_prog_put(new_bpf);
+	if (*bpf) {
+		if (new_bpf)
+			bpf_prog_put(new_bpf);
 		return new_bpf == *bpf ? 0 : -EINVAL;
 	}
 
-	if (*bpf)
-		bpf_prog_put(*bpf);
-
 	*bpf = new_bpf;
 	return 0;
 }
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e1de6b1..c67c668 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -115,6 +115,10 @@ static void fuse_free_inode(struct inode *inode)
 #ifdef CONFIG_FUSE_DAX
 	kfree(fi->dax);
 #endif
+#ifdef CONFIG_FUSE_BPF
+	if (fi->bpf)
+		bpf_prog_put(fi->bpf);
+#endif
 	kmem_cache_free(fuse_inode_cachep, fi);
 }
 
@@ -125,13 +129,6 @@ static void fuse_evict_inode(struct inode *inode)
 	/* Will write inode on close/munmap and in all other dirtiers */
 	WARN_ON(inode->i_state & I_DIRTY_INODE);
 
-#ifdef CONFIG_FUSE_BPF
-	iput(fi->backing_inode);
-	if (fi->bpf)
-		bpf_prog_put(fi->bpf);
-	fi->bpf = NULL;
-#endif
-
 	truncate_inode_pages_final(&inode->i_data);
 	clear_inode(inode);
 	if (inode->i_sb->s_flags & SB_ACTIVE) {
@@ -151,6 +148,15 @@ static void fuse_evict_inode(struct inode *inode)
 	}
 }
 
+#ifdef CONFIG_FUSE_BPF
+static void fuse_destroy_inode(struct inode *inode)
+{
+	struct fuse_inode *fi = get_fuse_inode(inode);
+
+	iput(fi->backing_inode);
+}
+#endif
+
 static int fuse_reconfigure(struct fs_context *fc)
 {
 	struct super_block *sb = fc->root->d_sb;
@@ -1084,6 +1090,9 @@ static const struct export_operations fuse_export_operations = {
 
 static const struct super_operations fuse_super_operations = {
 	.alloc_inode    = fuse_alloc_inode,
+#ifdef CONFIG_FUSE_BPF
+	.destroy_inode  = fuse_destroy_inode,
+#endif
 	.free_inode     = fuse_free_inode,
 	.evict_inode	= fuse_evict_inode,
 	.write_inode	= fuse_write_inode,