gpiod_request_commit() copies the pointer to the label
passed as an argument only to be used later. But there's a
chance the caller could immediately free the passed string
(e.g., local variable). This could trigger a use after free
when we use gpio label(e.g., gpiochip_unlock_as_irq(),
gpiochip_is_requested()).

To be on the safe side: duplicate the string with
kstrdup_const() so that if an unaware user passes an address
to a stack-allocated buffer, we won't get the arbitrary label.

Signed-off-by: Muchun Song <smuc...@gmail.com>
---
 drivers/gpio/gpiolib.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 25187403e3ac..e600c5f5d9a7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2270,6 +2270,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, 
const char *label)
        unsigned long           flags;
        unsigned                offset;
 
+       if (label) {
+               label = kstrdup_const(label, GFP_KERNEL);
+               if (!label)
+                       return -ENOMEM;
+       }
+
        spin_lock_irqsave(&gpio_lock, flags);
 
        /* NOTE:  gpio_request() can be called in early boot,
@@ -2280,6 +2286,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, 
const char *label)
                desc_set_label(desc, label ? : "?");
                status = 0;
        } else {
+               kfree_const(label);
                status = -EBUSY;
                goto done;
        }
@@ -2296,6 +2303,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, 
const char *label)
 
                if (status < 0) {
                        desc_set_label(desc, NULL);
+                       kfree_const(label);
                        clear_bit(FLAG_REQUESTED, &desc->flags);
                        goto done;
                }
@@ -2391,6 +2399,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
                        chip->free(chip, gpio_chip_hwgpio(desc));
                        spin_lock_irqsave(&gpio_lock, flags);
                }
+               kfree_const(desc->label);
                desc_set_label(desc, NULL);
                clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
                clear_bit(FLAG_REQUESTED, &desc->flags);
-- 
2.17.1

Reply via email to