The branch stable/13 has been updated by vexeduxr:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4472ecb362b068400f26abfce2db2a2b10a88d95

commit 4472ecb362b068400f26abfce2db2a2b10a88d95
Author:     Ahmad Khalifa <[email protected]>
AuthorDate: 2025-09-30 11:09:50 +0000
Commit:     Ahmad Khalifa <[email protected]>
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:
                /*

Reply via email to