git: 4472ecb362b0 - stable/13 - gpioc: fix race in ioctl(GPIOCONFIGEVENTS)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 01 Oct 2025 09:38:48 UTC
The branch stable/13 has been updated by vexeduxr:
URL: https://cgit.FreeBSD.org/src/commit/?id=4472ecb362b068400f26abfce2db2a2b10a88d95
commit 4472ecb362b068400f26abfce2db2a2b10a88d95
Author: Ahmad Khalifa <vexeduxr@FreeBSD.org>
AuthorDate: 2025-09-30 11:09:50 +0000
Commit: Ahmad Khalifa <vexeduxr@FreeBSD.org>
CommitDate: 2025-10-01 08:52:21 +0000
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
(cherry picked from commit d000adfe41e6f2fe8f3dbe92d8fc2d34ae882086)
---
sys/dev/gpio/gpioc.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c
index 1a5c483b0c3e..536ebeb9ae39 100644
--- a/sys/dev/gpio/gpioc.c
+++ b/sys/dev/gpio/gpioc.c
@@ -783,6 +783,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;
bus = GPIO_GET_BUS(sc->sc_pdev);
@@ -892,27 +893,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:
/*