DF-5636 / fix.diff
--- a/sys/bus/smbus/smbconf.c 2026-10-09 01:29:47.869160074 +0000 +++ b/sys/bus/smbus/smbconf.c 2026-10-09 01:45:45.460818539 +0000 @@ -28,6 +28,7 @@ * */ #include <sys/param.h> +#include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/bus.h> @@ -86,11 +87,11 @@ switch (how) { case (SMB_WAIT | SMB_INTR): - error = tsleep(sc, PCATCH, "smbreq", 0); + error = tsleep(sc, PCATCH, "smbreq", hz); break; case (SMB_WAIT | SMB_NOINTR): - error = tsleep(sc, 0, "smbreq", 0); + error = tsleep(sc, PCATCH, "smbreq", hz); break; default: @@ -128,16 +129,28 @@ if (sc->owner && sc->owner != dev) { crit_exit(); error = smbus_poll(sc, how); - if (error == EWOULDBLOCK) + /* + * A woken waiter MUST re-check ownership: the old + * code broke out with success on any wakeup without + * ever setting sc->owner = dev (phantom grant, + * DF-5636). A poll timeout on a WAIT request is + * the same: re-check. Only an interruption, or a + * refused SMB_DONTWAIT, unwinds the bridge request. + */ + if (error == 0) + continue; /* woken: re-check ownership */ + if (error == EWOULDBLOCK && (how & SMB_WAIT)) { error = 0; + continue; /* poll timeout: re-check */ + } + /* interrupted, or refused SMB_DONTWAIT: unwind */ + SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how); } else { sc->owner = dev; + error = 0; /* claimed: report success */ crit_exit(); - } - if (error == 0) break; - /* free any allocated resource */ - SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how); + } } return (error); @@ -156,23 +169,20 @@ int error; parent = device_get_parent(bus); - /* first, ask the underlying layers if the release is ok */ - error = SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL); - - if (error) - return (error); + /* validate ownership BEFORE touching the underlying layers */ crit_enter(); if (sc->owner == dev) { sc->owner = NULL; - - /* wakeup waiting processes */ - wakeup(sc); } else { - error = EACCES; + crit_exit(); + return (EACCES); } crit_exit(); + /* ownership confirmed: release the underlying layers */ + error = SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL); + /* wakeup waiting processes */ wakeup(sc); |