mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-29 20:04:57 +01:00
gpioc: fix race in ioctl(GPIOCONFIGEVENTS)
A race can occur in gpioc_ioctl when it is called with GPIOCONFIGEVENTS closely followed by GPIOSETCONFIG. GPIOSETCONFIG can alter the priv->pins list, making it no longer empty and opening the door for access to priv->events while we are reallocating it. Fix this by holding priv->mtx while handling GPIOCONFIGEVENTS. Reported by: Qiu-ji Chen PR: 289120 Reviewed by: mmel MFC after: 1 day Differential Revision: https://reviews.freebsd.org/D52783
This commit is contained in:
1 parent
fa26e44554
commit
d000adfe41
1 file changed
+19
-10
+19
-10
@@ -793,6 +793,7 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
|
||||
struct gpio_access_32 *a32;
|
||||
struct gpio_config_32 *c32;
|
||||
struct gpio_event_config *evcfg;
|
||||
struct gpioc_pin_event *tmp;
|
||||
uint32_t caps, intrflags;
|
||||
|
||||
switch (cmd) {
|
||||
@@ -908,27 +909,35 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
|
||||
res = devfs_get_cdevpriv((void **)&priv);
|
||||
if (res != 0)
|
||||
break;
|
||||
/* If any pins have been configured, changes aren't allowed. */
|
||||
if (!SLIST_EMPTY(&priv->pins)) {
|
||||
res = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (evcfg->gp_report_type != GPIO_EVENT_REPORT_DETAIL &&
|
||||
evcfg->gp_report_type != GPIO_EVENT_REPORT_SUMMARY) {
|
||||
res = EINVAL;
|
||||
break;
|
||||
}
|
||||
priv->report_option = evcfg->gp_report_type;
|
||||
/* Reallocate the events buffer if the user wants it bigger. */
|
||||
if (priv->report_option == GPIO_EVENT_REPORT_DETAIL &&
|
||||
tmp = NULL;
|
||||
if (evcfg->gp_report_type == GPIO_EVENT_REPORT_DETAIL &&
|
||||
priv->numevents < evcfg->gp_fifo_size) {
|
||||
free(priv->events, M_GPIOC);
|
||||
priv->numevents = evcfg->gp_fifo_size;
|
||||
priv->events = malloc(priv->numevents *
|
||||
tmp = malloc(priv->numevents *
|
||||
sizeof(struct gpioc_pin_event), M_GPIOC,
|
||||
M_WAITOK | M_ZERO);
|
||||
}
|
||||
mtx_lock(&priv->mtx);
|
||||
/* If any pins have been configured, changes aren't allowed. */
|
||||
if (!SLIST_EMPTY(&priv->pins)) {
|
||||
mtx_unlock(&priv->mtx);
|
||||
free(tmp, M_GPIOC);
|
||||
res = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (tmp != NULL) {
|
||||
free(priv->events, M_GPIOC);
|
||||
priv->events = tmp;
|
||||
priv->numevents = evcfg->gp_fifo_size;
|
||||
priv->evidx_head = priv->evidx_tail = 0;
|
||||
}
|
||||
priv->report_option = evcfg->gp_report_type;
|
||||
mtx_unlock(&priv->mtx);
|
||||
break;
|
||||
case FIONBIO:
|
||||
/*
|
||||
|
||||
Reference in new issue
Block a user