diff options
author | Marco Nelissen <marcone@google.com> | 2022-06-03 16:28:12 -0700 |
---|---|---|
committer | Ji Soo Shin <jisshin@google.com> | 2023-03-06 23:26:37 +0000 |
commit | 6bda453bac6190cbf254439383c770793650fa18 (patch) | |
tree | 480bfe7aef66cb8c63b3ba9c597d8dfab89153f1 | |
parent | 36d7af334ec1418e1734e2370bdc0db99dbbd513 (diff) | |
download | trusty-6bda453bac6190cbf254439383c770793650fa18.tar.gz |
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
(cherry picked from commit 435655cef705e1db72d44f4db05bf417eb21b54d)
Signed-off-by: Will McVicker <willmcvicker@google.com>
-rw-r--r-- | drivers/trusty/trusty-log.c | 76 |
1 files changed, 68 insertions, 8 deletions
diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 8812cec..654bf0b 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -188,6 +188,7 @@ struct trusty_log_state { 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 @@ -209,6 +210,7 @@ struct trusty_log_state { 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) @@ -617,6 +619,7 @@ static int trusty_log_sfile_dev_open(struct inode *inode, struct file *file) sfile->private = ls; s = container_of(ls, struct trusty_log_state, log_sfile); s->have_first_reader = true; + kref_get(&s->refcount); return 0; } @@ -636,6 +639,12 @@ static unsigned int trusty_log_sfile_dev_poll(struct file *filp, 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; @@ -656,12 +665,50 @@ static unsigned int trusty_log_sfile_dev_poll(struct file *filp, 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) @@ -685,6 +732,7 @@ static int trusty_log_sfile_register(struct trusty_log_state *s) ret); return ret; } + s->registered = true; dev_info(s->dev, "/dev/%s registered\n", ls->device_name); return 0; @@ -698,6 +746,7 @@ static void trusty_log_sfile_unregister(struct trusty_log_state *s) if (s->dev) { dev_info(s->dev, "/dev/%s unregistered\n", ls->misc.name); + s->registered = false; } } @@ -826,6 +875,7 @@ static int trusty_log_init(struct platform_device *pdev) goto error_log_sfile; } + kref_init(&s->refcount); platform_set_drvdata(pdev, s); return 0; @@ -877,11 +927,23 @@ static int trusty_log_probe(struct platform_device *pdev) 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); @@ -889,14 +951,14 @@ static int trusty_log_remove(struct platform_device *pdev) 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 { /* @@ -907,8 +969,6 @@ static int trusty_log_remove(struct platform_device *pdev) } kfree(s->sg); kfree(s); - - return 0; } static const struct of_device_id trusty_test_of_match[] = { |