]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
misc: misc_minor_alloc to use ida for all dynamic/misc dynamic minors
authorVimal Agrawal <vimal.agrawal@sophos.com>
Mon, 21 Oct 2024 13:38:12 +0000 (13:38 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Jan 2025 12:18:10 +0000 (13:18 +0100)
misc_minor_alloc was allocating id using ida for minor only in case of
MISC_DYNAMIC_MINOR but misc_minor_free was always freeing ids
using ida_free causing a mismatch and following warn:
> > WARNING: CPU: 0 PID: 159 at lib/idr.c:525 ida_free+0x3e0/0x41f
> > ida_free called for id=127 which is not allocated.
> > <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
...
> > [<60941eb4>] ida_free+0x3e0/0x41f
> > [<605ac993>] misc_minor_free+0x3e/0xbc
> > [<605acb82>] misc_deregister+0x171/0x1b3

misc_minor_alloc is changed to allocate id from ida for all minors
falling in the range of dynamic/ misc dynamic minors

Fixes: ab760791c0cf ("char: misc: Increase the maximum number of dynamic misc devices to 1048448")
Signed-off-by: Vimal Agrawal <vimal.agrawal@sophos.com>
Reviewed-by: Dirk VanDerMerwe <dirk.vandermerwe@sophos.com>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20241021133812.23703-1-vimal.agrawal@sophos.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/char/misc.c

index 541edc26ec89a14ef3735da111452c78d553b79e..2cf595d2e10b85fef701f44275ca0987600a1b3c 100644 (file)
@@ -63,16 +63,30 @@ static DEFINE_MUTEX(misc_mtx);
 #define DYNAMIC_MINORS 128 /* like dynamic majors */
 static DEFINE_IDA(misc_minors_ida);
 
-static int misc_minor_alloc(void)
+static int misc_minor_alloc(int minor)
 {
-       int ret;
-
-       ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
-       if (ret >= 0) {
-               ret = DYNAMIC_MINORS - ret - 1;
+       int ret = 0;
+
+       if (minor == MISC_DYNAMIC_MINOR) {
+               /* allocate free id */
+               ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
+               if (ret >= 0) {
+                       ret = DYNAMIC_MINORS - ret - 1;
+               } else {
+                       ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
+                                             MINORMASK, GFP_KERNEL);
+               }
        } else {
-               ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
-                                     MINORMASK, GFP_KERNEL);
+               /* specific minor, check if it is in dynamic or misc dynamic range  */
+               if (minor < DYNAMIC_MINORS) {
+                       minor = DYNAMIC_MINORS - minor - 1;
+                       ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
+               } else if (minor > MISC_DYNAMIC_MINOR) {
+                       ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
+               } else {
+                       /* case of non-dynamic minors, no need to allocate id */
+                       ret = 0;
+               }
        }
        return ret;
 }
@@ -219,7 +233,7 @@ int misc_register(struct miscdevice *misc)
        mutex_lock(&misc_mtx);
 
        if (is_dynamic) {
-               int i = misc_minor_alloc();
+               int i = misc_minor_alloc(misc->minor);
 
                if (i < 0) {
                        err = -EBUSY;
@@ -228,6 +242,7 @@ int misc_register(struct miscdevice *misc)
                misc->minor = i;
        } else {
                struct miscdevice *c;
+               int i;
 
                list_for_each_entry(c, &misc_list, list) {
                        if (c->minor == misc->minor) {
@@ -235,6 +250,12 @@ int misc_register(struct miscdevice *misc)
                                goto out;
                        }
                }
+
+               i = misc_minor_alloc(misc->minor);
+               if (i < 0) {
+                       err = -EBUSY;
+                       goto out;
+               }
        }
 
        dev = MKDEV(MISC_MAJOR, misc->minor);