gpio fixes for v6.9
- fix a performance regression in GPIO requesting and releasing after the conversion to SRCU - fix a use-after-free bug due to a race-condition - fix leaking stack memory to user-space in a GPIO uABI corner case -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmY+Y+0ACgkQEacuoBRx 13L54A/+Mm8dSiQ8svgSzsvdf3lSzk5uROiR5CLlVv2NkkyquWwx0d58i4xyhe2Z JIbSvLJxYi9DlGrpkMJglHdYpd8FfCxnTV50bdKH6FMi9KVOZ9oaUuSC6NcYkoL1 9Vyo3KXigfWCCJ1ay6VmFhgl+5UIJ2Ddbwe4Xh5pQ7gzab30fCL3RCD8V0Aixhtd pmDCVv1cjlwAdTOmAfEbGVEGAeghsNRv8a0+PEVNM3uYYlmwzVyr9hZLQLKuSqau DWLaHxGRXJUpPy5rDWn9mDushYUlAJSRlZA4dNbQVMp24poRqCVSyQfHmWhmpYr/ SdyPuEcUuov45H8FTqav/sOxoTBs7f1tb1tvrdkhlrSLnJydw8AE7GIhJWWnvyb4 cFfSHQL42lrvJmFOTbtJTKXHJ1nLp2eSX7BhkaVYGAmQ59owbTnya1xFEru3vzFi p1rHcCcIW10QXIq1ebtqa5tsuA2UI9S7tpDDPCULaqxL3hjn7BJnHdxZYBOJopl9 AhVRS8uI4Eig4wSquhFqaem9yVhKwdlwWlUfic61cSEfsHJQu4B9gQYPHBbUJCVt k6Vu0gzLNGmGa7hv72Udx1SvA9X7Mnb59QGqnmrenxnf3au/5l1k1+X8zA2dOGCj o+6YBc66ODyMGpU/p2CP/LFXZe/RQim0jksJbq79qBsRcQFoAI0= =fdl3 -----END PGP SIGNATURE----- Merge tag 'gpio-fixes-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux Pull gpio fixes from Bartosz Golaszewski: "Some last-minute fixes for this release from the GPIO subsystem. The first two address a regression in performance reported to me after the conversion to using SRCU in GPIOLIB that was merged during the v6.9 merge window. The second patch is not technically a fix but since after the first one we no longer need to use a per-descriptor SRCU struct, I think it's worth to simplify the code before it gets released on Sunday. The next two commits fix two memory issues: one use-after-free bug and one instance of possibly leaking kernel stack memory to user-space. Summary: - fix a performance regression in GPIO requesting and releasing after the conversion to SRCU - fix a use-after-free bug due to a race-condition - fix leaking stack memory to user-space in a GPIO uABI corner case" * tag 'gpio-fixes-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: gpiolib: cdev: fix uninitialised kfifo gpiolib: cdev: Fix use after free in lineinfo_changed_notify gpiolib: use a single SRCU struct for all GPIO descriptors gpiolib: fix the speed of descriptor label setting with SRCU
This commit is contained in:
commit
cfb4be1a61
|
@ -1193,6 +1193,8 @@ static int edge_detector_update(struct line *line,
|
|||
struct gpio_v2_line_config *lc,
|
||||
unsigned int line_idx, u64 edflags)
|
||||
{
|
||||
u64 eflags;
|
||||
int ret;
|
||||
u64 active_edflags = READ_ONCE(line->edflags);
|
||||
unsigned int debounce_period_us =
|
||||
gpio_v2_line_config_debounce_period(lc, line_idx);
|
||||
|
@ -1204,6 +1206,18 @@ static int edge_detector_update(struct line *line,
|
|||
/* sw debounced and still will be...*/
|
||||
if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
|
||||
line_set_debounce_period(line, debounce_period_us);
|
||||
/*
|
||||
* ensure event fifo is initialised if edge detection
|
||||
* is now enabled.
|
||||
*/
|
||||
eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
|
||||
if (eflags && !kfifo_initialized(&line->req->events)) {
|
||||
ret = kfifo_alloc(&line->req->events,
|
||||
line->req->event_buffer_size,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2351,7 +2365,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
|
|||
|
||||
dflags = READ_ONCE(desc->flags);
|
||||
|
||||
scoped_guard(srcu, &desc->srcu) {
|
||||
scoped_guard(srcu, &desc->gdev->desc_srcu) {
|
||||
label = gpiod_get_label(desc);
|
||||
if (label && test_bit(FLAG_REQUESTED, &dflags))
|
||||
strscpy(info->consumer, label,
|
||||
|
@ -2799,11 +2813,11 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
|
|||
struct gpio_chardev_data *cdev = file->private_data;
|
||||
struct gpio_device *gdev = cdev->gdev;
|
||||
|
||||
bitmap_free(cdev->watched_lines);
|
||||
blocking_notifier_chain_unregister(&gdev->device_notifier,
|
||||
&cdev->device_unregistered_nb);
|
||||
blocking_notifier_chain_unregister(&gdev->line_state_notifier,
|
||||
&cdev->lineinfo_changed_nb);
|
||||
bitmap_free(cdev->watched_lines);
|
||||
gpio_device_put(gdev);
|
||||
kfree(cdev);
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ static bool gpiolib_initialized;
|
|||
|
||||
const char *gpiod_get_label(struct gpio_desc *desc)
|
||||
{
|
||||
struct gpio_desc_label *label;
|
||||
unsigned long flags;
|
||||
|
||||
flags = READ_ONCE(desc->flags);
|
||||
|
@ -108,23 +109,36 @@ const char *gpiod_get_label(struct gpio_desc *desc)
|
|||
!test_bit(FLAG_REQUESTED, &flags))
|
||||
return "interrupt";
|
||||
|
||||
return test_bit(FLAG_REQUESTED, &flags) ?
|
||||
srcu_dereference(desc->label, &desc->srcu) : NULL;
|
||||
if (!test_bit(FLAG_REQUESTED, &flags))
|
||||
return NULL;
|
||||
|
||||
label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu,
|
||||
srcu_read_lock_held(&desc->gdev->desc_srcu));
|
||||
|
||||
return label->str;
|
||||
}
|
||||
|
||||
static void desc_free_label(struct rcu_head *rh)
|
||||
{
|
||||
kfree(container_of(rh, struct gpio_desc_label, rh));
|
||||
}
|
||||
|
||||
static int desc_set_label(struct gpio_desc *desc, const char *label)
|
||||
{
|
||||
const char *new = NULL, *old;
|
||||
struct gpio_desc_label *new = NULL, *old;
|
||||
|
||||
if (label) {
|
||||
new = kstrdup_const(label, GFP_KERNEL);
|
||||
new = kzalloc(struct_size(new, str, strlen(label) + 1),
|
||||
GFP_KERNEL);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(new->str, label);
|
||||
}
|
||||
|
||||
old = rcu_replace_pointer(desc->label, new, 1);
|
||||
synchronize_srcu(&desc->srcu);
|
||||
kfree_const(old);
|
||||
if (old)
|
||||
call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -695,10 +709,10 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
|
|||
static void gpiodev_release(struct device *dev)
|
||||
{
|
||||
struct gpio_device *gdev = to_gpio_device(dev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < gdev->ngpio; i++)
|
||||
cleanup_srcu_struct(&gdev->descs[i].srcu);
|
||||
/* Call pending kfree()s for descriptor labels. */
|
||||
synchronize_srcu(&gdev->desc_srcu);
|
||||
cleanup_srcu_struct(&gdev->desc_srcu);
|
||||
|
||||
ida_free(&gpio_ida, gdev->id);
|
||||
kfree_const(gdev->label);
|
||||
|
@ -975,6 +989,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
if (ret)
|
||||
goto err_remove_from_list;
|
||||
|
||||
ret = init_srcu_struct(&gdev->desc_srcu);
|
||||
if (ret)
|
||||
goto err_cleanup_gdev_srcu;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
INIT_LIST_HEAD(&gdev->pin_ranges);
|
||||
#endif
|
||||
|
@ -982,23 +1000,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
if (gc->names) {
|
||||
ret = gpiochip_set_desc_names(gc);
|
||||
if (ret)
|
||||
goto err_cleanup_gdev_srcu;
|
||||
goto err_cleanup_desc_srcu;
|
||||
}
|
||||
ret = gpiochip_set_names(gc);
|
||||
if (ret)
|
||||
goto err_cleanup_gdev_srcu;
|
||||
goto err_cleanup_desc_srcu;
|
||||
|
||||
ret = gpiochip_init_valid_mask(gc);
|
||||
if (ret)
|
||||
goto err_cleanup_gdev_srcu;
|
||||
goto err_cleanup_desc_srcu;
|
||||
|
||||
for (desc_index = 0; desc_index < gc->ngpio; desc_index++) {
|
||||
struct gpio_desc *desc = &gdev->descs[desc_index];
|
||||
|
||||
ret = init_srcu_struct(&desc->srcu);
|
||||
if (ret)
|
||||
goto err_cleanup_desc_srcu;
|
||||
|
||||
if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) {
|
||||
assign_bit(FLAG_IS_OUT,
|
||||
&desc->flags, !gc->get_direction(gc, desc_index));
|
||||
|
@ -1010,7 +1024,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
|
||||
ret = of_gpiochip_add(gc);
|
||||
if (ret)
|
||||
goto err_cleanup_desc_srcu;
|
||||
goto err_free_valid_mask;
|
||||
|
||||
ret = gpiochip_add_pin_ranges(gc);
|
||||
if (ret)
|
||||
|
@ -1057,10 +1071,10 @@ err_free_hogs:
|
|||
gpiochip_remove_pin_ranges(gc);
|
||||
err_remove_of_chip:
|
||||
of_gpiochip_remove(gc);
|
||||
err_cleanup_desc_srcu:
|
||||
while (desc_index--)
|
||||
cleanup_srcu_struct(&gdev->descs[desc_index].srcu);
|
||||
err_free_valid_mask:
|
||||
gpiochip_free_valid_mask(gc);
|
||||
err_cleanup_desc_srcu:
|
||||
cleanup_srcu_struct(&gdev->desc_srcu);
|
||||
err_cleanup_gdev_srcu:
|
||||
cleanup_srcu_struct(&gdev->srcu);
|
||||
err_remove_from_list:
|
||||
|
@ -2390,7 +2404,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
|
|||
if (!test_bit(FLAG_REQUESTED, &desc->flags))
|
||||
return NULL;
|
||||
|
||||
guard(srcu)(&desc->srcu);
|
||||
guard(srcu)(&desc->gdev->desc_srcu);
|
||||
|
||||
label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
|
||||
if (!label)
|
||||
|
@ -4781,7 +4795,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
|||
}
|
||||
|
||||
for_each_gpio_desc(gc, desc) {
|
||||
guard(srcu)(&desc->srcu);
|
||||
guard(srcu)(&desc->gdev->desc_srcu);
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags)) {
|
||||
gpiod_get_direction(desc);
|
||||
is_out = test_bit(FLAG_IS_OUT, &desc->flags);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
* @chip: pointer to the corresponding gpiochip, holding static
|
||||
* data for this device
|
||||
* @descs: array of ngpio descriptors.
|
||||
* @desc_srcu: ensures consistent state of GPIO descriptors exposed to users
|
||||
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
|
||||
* of the @descs array.
|
||||
* @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep
|
||||
|
@ -61,6 +62,7 @@ struct gpio_device {
|
|||
struct module *owner;
|
||||
struct gpio_chip __rcu *chip;
|
||||
struct gpio_desc *descs;
|
||||
struct srcu_struct desc_srcu;
|
||||
int base;
|
||||
u16 ngpio;
|
||||
bool can_sleep;
|
||||
|
@ -137,6 +139,11 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
|||
|
||||
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
|
||||
|
||||
struct gpio_desc_label {
|
||||
struct rcu_head rh;
|
||||
char str[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_desc - Opaque descriptor for a GPIO
|
||||
*
|
||||
|
@ -145,7 +152,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
|
|||
* @label: Name of the consumer
|
||||
* @name: Line name
|
||||
* @hog: Pointer to the device node that hogs this line (if any)
|
||||
* @srcu: SRCU struct protecting the label pointer.
|
||||
*
|
||||
* These are obtained using gpiod_get() and are preferable to the old
|
||||
* integer-based handles.
|
||||
|
@ -177,13 +183,12 @@ struct gpio_desc {
|
|||
#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
|
||||
|
||||
/* Connection label */
|
||||
const char __rcu *label;
|
||||
struct gpio_desc_label __rcu *label;
|
||||
/* Name of the GPIO */
|
||||
const char *name;
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
struct device_node *hog;
|
||||
#endif
|
||||
struct srcu_struct srcu;
|
||||
};
|
||||
|
||||
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
|
||||
|
@ -251,7 +256,7 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
|||
|
||||
#define gpiod_err(desc, fmt, ...) \
|
||||
do { \
|
||||
scoped_guard(srcu, &desc->srcu) { \
|
||||
scoped_guard(srcu, &desc->gdev->desc_srcu) { \
|
||||
pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
|
||||
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
|
||||
} \
|
||||
|
@ -259,7 +264,7 @@ do { \
|
|||
|
||||
#define gpiod_warn(desc, fmt, ...) \
|
||||
do { \
|
||||
scoped_guard(srcu, &desc->srcu) { \
|
||||
scoped_guard(srcu, &desc->gdev->desc_srcu) { \
|
||||
pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
|
||||
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
|
||||
} \
|
||||
|
@ -267,7 +272,7 @@ do { \
|
|||
|
||||
#define gpiod_dbg(desc, fmt, ...) \
|
||||
do { \
|
||||
scoped_guard(srcu, &desc->srcu) { \
|
||||
scoped_guard(srcu, &desc->gdev->desc_srcu) { \
|
||||
pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
|
||||
gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
|
||||
} \
|
||||
|
|
Loading…
Reference in New Issue