On Sun, Oct 26, 2025 at 05:20:03PM -0300, Thadeu Lima de Souza Cascardo wrote:
On Sat, Oct 25, 2025 at 11:54:55AM -0400, Sasha Levin wrote:
- Backport notes
- Older trees (like this one) use a 64-bit dynamic minor bitmap with indices mapped via `i = DYNAMIC_MINORS - misc->minor - 1` and `clear_bit(i, misc_minors)` (drivers/char/misc.c:241–250), not `misc_minor_free()`. The equivalent backport should reset `misc->minor = MISC_DYNAMIC_MINOR` only if the minor was dynamically allocated, which can be inferred by the same range check already used before clearing the bit:
- If `i < DYNAMIC_MINORS && i >= 0` then it was a dynamic minor; after `clear_bit(i, misc_minors);` set `misc->minor = MISC_DYNAMIC_MINOR;`.
- Newer trees using `misc_minor_free()` may use a different condition (as in the diff). Adjust the condition to the tree’s semantics; the intent is “if this was a dynamically allocated minor, reset it.”
The LLM got it right here. This won't work for 6.6.y and 6.12.y. The check for dynamically allocated minors is different on those versions.
Risk assessment
- Very low risk:
- Static-minor devices are unaffected.
- Dynamic-minor devices now always behave as “dynamic” on re- register, which is the intended contract.
- Change is localized, under the same mutex as the rest of the deregistration path.
- Positive impact:
- Fixes real user-visible failures on unbind/rebind or probe/remove cycles.
- Consistent with `misc_register()` error path behavior (drivers/char/misc.c:214).
Stable criteria
- Fixes a real bug that affects users (unbind/rebind failures).
- Small, contained change in a well-scoped subsystem.
- No new features or architectural changes.
- Signed-off-by by Greg Kroah-Hartman, matching subsystem ownership.
Given the above, this is a strong candidate for stable backport.
drivers/char/misc.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 558302a64dd90..255a164eec86d 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -282,6 +282,8 @@ void misc_deregister(struct miscdevice *misc) list_del(&misc->list); device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor)); misc_minor_free(misc->minor);
- if (misc->minor > MISC_DYNAMIC_MINOR)
misc->minor = MISC_DYNAMIC_MINOR;For 6.12 and 6.6, this should be:
if (misc->minor > MISC_DYNAMIC_MINOR || (misc->minor < DYNAMIC_MINORS && misc->minor >= 15)) misc->minor = MISC_DYNAMIC_MINOR;
Or pick 31b636d2c416 ("char: misc: restrict the dynamic range to exclude reserved minors"), or just drop this from 6.6 and 6.12.
I've picked 31b636d2c416 up for 6.12 and 6.6, thanks!