aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2016-10-24 07:59:15 -0400
committerLinus Walleij <linus.walleij@linaro.org>2016-10-31 16:23:44 -0400
commit953b956a2e6d35298e684f251bad98ea6c96f982 (patch)
treea18a8ab665392e1214ce1d5a7ef1cfd538c660e7 /drivers/gpio
parenta909d3e636995ba7c349e2ca5dbb528154d4ac30 (diff)
gpio: GPIO_GET_LINE{HANDLE,EVENT}_IOCTL: Fix file descriptor leak
When allocating a new line handle or event a file is allocated that it is associated to. The file is attached to a file descriptor of the current process and the file descriptor is returned to userspace using copy_to_user(). If this copy operation fails the line handle or event allocation is aborted, all acquired resources are freed and an error is returned. But the file struct is not freed and left attached to the userspace application and even though the file descriptor number was not copied it is trivial to guess. If a userspace application performs a IOCTL on such a left over file descriptor it will trigger a use-after-free and if the file descriptor is closed (latest when the application exits) a double-free is triggered. anon_inode_getfd() performs 3 tasks, allocate a file struct, allocate a file descriptor for the current process and install the file struct in the file descriptor. As soon as the file struct is installed in the file descriptor it is accessible by userspace (even if the IOCTL itself hasn't completed yet), this means uninstalling the fd on the error path is not an option, since userspace might already got a reference to the file. Instead anon_inode_getfd() needs to be broken into its individual steps. The allocation of the file struct and file descriptor is done first, then the copy_to_user() is executed and only if it succeeds the file is installed. Since the file struct is reference counted it can not be just freed, but its reference needs to be dropped, which will also call the release() callback, which will free the state attached to the file. So in this case the normal error cleanup path should not be taken. Cc: stable@vger.kernel.org Fixes: d932cd49182f ("gpio: free handles in fringe cases") Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpiolib.c57
1 files changed, 45 insertions, 12 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 20e09b7c2de3..93ed0e00c578 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -21,6 +21,7 @@
21#include <linux/uaccess.h> 21#include <linux/uaccess.h>
22#include <linux/compat.h> 22#include <linux/compat.h>
23#include <linux/anon_inodes.h> 23#include <linux/anon_inodes.h>
24#include <linux/file.h>
24#include <linux/kfifo.h> 25#include <linux/kfifo.h>
25#include <linux/poll.h> 26#include <linux/poll.h>
26#include <linux/timekeeping.h> 27#include <linux/timekeeping.h>
@@ -423,6 +424,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
423{ 424{
424 struct gpiohandle_request handlereq; 425 struct gpiohandle_request handlereq;
425 struct linehandle_state *lh; 426 struct linehandle_state *lh;
427 struct file *file;
426 int fd, i, ret; 428 int fd, i, ret;
427 429
428 if (copy_from_user(&handlereq, ip, sizeof(handlereq))) 430 if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
@@ -499,26 +501,41 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
499 i--; 501 i--;
500 lh->numdescs = handlereq.lines; 502 lh->numdescs = handlereq.lines;
501 503
502 fd = anon_inode_getfd("gpio-linehandle", 504 fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
503 &linehandle_fileops,
504 lh,
505 O_RDONLY | O_CLOEXEC);
506 if (fd < 0) { 505 if (fd < 0) {
507 ret = fd; 506 ret = fd;
508 goto out_free_descs; 507 goto out_free_descs;
509 } 508 }
510 509
510 file = anon_inode_getfile("gpio-linehandle",
511 &linehandle_fileops,
512 lh,
513 O_RDONLY | O_CLOEXEC);
514 if (IS_ERR(file)) {
515 ret = PTR_ERR(file);
516 goto out_put_unused_fd;
517 }
518
511 handlereq.fd = fd; 519 handlereq.fd = fd;
512 if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { 520 if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
513 ret = -EFAULT; 521 /*
514 goto out_free_descs; 522 * fput() will trigger the release() callback, so do not go onto
523 * the regular error cleanup path here.
524 */
525 fput(file);
526 put_unused_fd(fd);
527 return -EFAULT;
515 } 528 }
516 529
530 fd_install(fd, file);
531
517 dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", 532 dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
518 lh->numdescs); 533 lh->numdescs);
519 534
520 return 0; 535 return 0;
521 536
537out_put_unused_fd:
538 put_unused_fd(fd);
522out_free_descs: 539out_free_descs:
523 for (; i >= 0; i--) 540 for (; i >= 0; i--)
524 gpiod_free(lh->descs[i]); 541 gpiod_free(lh->descs[i]);
@@ -721,6 +738,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
721 struct gpioevent_request eventreq; 738 struct gpioevent_request eventreq;
722 struct lineevent_state *le; 739 struct lineevent_state *le;
723 struct gpio_desc *desc; 740 struct gpio_desc *desc;
741 struct file *file;
724 u32 offset; 742 u32 offset;
725 u32 lflags; 743 u32 lflags;
726 u32 eflags; 744 u32 eflags;
@@ -815,23 +833,38 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
815 if (ret) 833 if (ret)
816 goto out_free_desc; 834 goto out_free_desc;
817 835
818 fd = anon_inode_getfd("gpio-event", 836 fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
819 &lineevent_fileops,
820 le,
821 O_RDONLY | O_CLOEXEC);
822 if (fd < 0) { 837 if (fd < 0) {
823 ret = fd; 838 ret = fd;
824 goto out_free_irq; 839 goto out_free_irq;
825 } 840 }
826 841
842 file = anon_inode_getfile("gpio-event",
843 &lineevent_fileops,
844 le,
845 O_RDONLY | O_CLOEXEC);
846 if (IS_ERR(file)) {
847 ret = PTR_ERR(file);
848 goto out_put_unused_fd;
849 }
850
827 eventreq.fd = fd; 851 eventreq.fd = fd;
828 if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { 852 if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
829 ret = -EFAULT; 853 /*
830 goto out_free_irq; 854 * fput() will trigger the release() callback, so do not go onto
855 * the regular error cleanup path here.
856 */
857 fput(file);
858 put_unused_fd(fd);
859 return -EFAULT;
831 } 860 }
832 861
862 fd_install(fd, file);
863
833 return 0; 864 return 0;
834 865
866out_put_unused_fd:
867 put_unused_fd(fd);
835out_free_irq: 868out_free_irq:
836 free_irq(le->irq, le); 869 free_irq(le->irq, le);
837out_free_desc: 870out_free_desc: