ANDROID: trusty-log: fix potential use-after-free

Bug: 216130110
Test: unbind driver while in use

Signed-off-by: Marco Nelissen <marcone@google.com>
Change-Id: Ie1a43f90daea0e4c2d0cd7a1093640a26eb7dce2
diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c
index c2c5688..79948ee 100644
--- a/drivers/trusty/trusty-log.c
+++ b/drivers/trusty/trusty-log.c
@@ -179,6 +179,7 @@
 	struct device *dev;
 	struct device *trusty_dev;
 	struct trusty_log_sfile log_sfile;
+	struct kref refcount;
 
 	/*
 	 * This lock is here to ensure only one consumer will read
@@ -200,6 +201,7 @@
 	spinlock_t wake_up_lock;
 	u32 last_wake_put;
 	bool have_first_reader;
+	bool registered;
 };
 
 static inline u32 u32_add_overflow(u32 a, u32 b)
@@ -608,6 +610,7 @@
 	sfile->private = ls;
 	s = container_of(ls, struct trusty_log_state, log_sfile);
 	s->have_first_reader = true;
+	kref_get(&s->refcount);
 	return 0;
 }
 
@@ -627,6 +630,12 @@
 	sfile = filp->private_data;
 	lb = sfile->private;
 	s = container_of(lb, struct trusty_log_state, log_sfile);
+
+	if (!s->registered) {
+		dev_err(s->dev, "invalid poll fd\n");
+		return -EINVAL;
+	}
+
 	poll_wait(filp, &s->poll_waiters, wait);
 	log = s->log;
 
@@ -647,12 +656,50 @@
 	return 0;
 }
 
+static void trusty_log_cleanup(struct kref *ref);
+
+static int trusty_log_sfile_dev_release(struct inode *inode,
+					struct file *filp)
+{
+	struct seq_file *sfile;
+	struct trusty_log_sfile *lb;
+	struct trusty_log_state *s;
+
+	sfile = filp->private_data;
+	lb = sfile->private;
+	s = container_of(lb, struct trusty_log_state, log_sfile);
+
+	kref_put(&s->refcount, trusty_log_cleanup);
+
+	seq_release(inode, filp);
+	return 0;
+}
+
+ssize_t trusty_log_sfile_dev_read(struct file *filp, char __user *buf,
+				  size_t size, loff_t *ppos)
+{
+	struct seq_file *sfile;
+	struct trusty_log_sfile *lb;
+	struct trusty_log_state *s;
+
+	sfile = filp->private_data;
+	lb = sfile->private;
+	s = container_of(lb, struct trusty_log_state, log_sfile);
+
+	if (!s->registered) {
+		dev_err(s->dev, "invalid read fd\n");
+		return -EINVAL;
+	}
+
+	return seq_read(filp, buf, size, ppos);
+}
+
 static const struct file_operations log_sfile_dev_operations = {
 	.owner = THIS_MODULE,
 	.open = trusty_log_sfile_dev_open,
 	.poll = trusty_log_sfile_dev_poll,
-	.read = seq_read,
-	.release = seq_release,
+	.read = trusty_log_sfile_dev_read,
+	.release = trusty_log_sfile_dev_release,
 };
 
 static int trusty_log_sfile_register(struct trusty_log_state *s)
@@ -676,6 +723,7 @@
 			ret);
 		return ret;
 	}
+	s->registered = true;
 	dev_info(s->dev, "/dev/%s registered\n",
 		 ls->device_name);
 	return 0;
@@ -689,6 +737,7 @@
 	if (s->dev) {
 		dev_info(s->dev, "/dev/%s unregistered\n",
 			 ls->misc.name);
+		s->registered = false;
 	}
 }
 
@@ -817,6 +866,7 @@
 		goto error_log_sfile;
 	}
 
+	kref_init(&s->refcount);
 	platform_set_drvdata(pdev, s);
 
 	return 0;
@@ -868,11 +918,23 @@
 
 static int trusty_log_remove(struct platform_device *pdev)
 {
-	int result;
 	struct trusty_log_state *s = platform_get_drvdata(pdev);
-	trusty_shared_mem_id_t mem_id = s->log_pages_shared_mem_id;
 
 	trusty_log_sfile_unregister(s);
+	kref_put(&s->refcount, trusty_log_cleanup);
+	return 0;
+}
+
+static void trusty_log_cleanup(struct kref *ref)
+{
+	int result;
+	struct trusty_log_state *s;
+	trusty_shared_mem_id_t mem_id;
+
+	s = container_of(ref, struct trusty_log_state, refcount);
+	dev_info(s->dev, "log_cleanup\n");
+	mem_id = s->log_pages_shared_mem_id;
+
 	atomic_notifier_chain_unregister(&panic_notifier_list,
 					 &s->panic_notifier);
 	trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier);
@@ -880,14 +942,14 @@
 	result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM,
 				   (u32)mem_id, (u32)(mem_id >> 32), 0);
 	if (result) {
-		dev_err(&pdev->dev,
+		dev_err(s->dev,
 			"trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n",
 			result);
 	}
 	result = trusty_reclaim_memory(s->trusty_dev, mem_id, s->sg,
 				       s->log_num_pages);
 	if (WARN_ON(result)) {
-		dev_err(&pdev->dev,
+		dev_err(s->dev,
 			"trusty failed to remove shared memory: %d\n", result);
 	} else {
 		/*
@@ -898,8 +960,6 @@
 	}
 	kfree(s->sg);
 	kfree(s);
-
-	return 0;
 }
 
 static const struct of_device_id trusty_test_of_match[] = {