From nobody Sun Feb 8 14:20:53 2026 Received: from fanzine2.igalia.com (fanzine.igalia.com [178.60.130.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C8D320F08C for ; Thu, 23 Jan 2025 12:33:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.60.130.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635588; cv=none; b=qokZkfuLIoAWELh0erUdACsX5FwfbsGfaBPJldxzhDNCi3aHbXkEGn6VkLs4x/VhDMsxeii/tXBgrUpDfNd/m26Q17JGDVyZmds7MSjnmF5tnyaE3bo2DUAvD6afJ+oazSbZ0Mk+lkgqKR22PDPi//vJUe8ESYB7IjCyYLrL01I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635588; c=relaxed/simple; bh=FZN6LIhsx42udoQ6Bg+WFs8IBa0gH0kXLbVxKuPoVNA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=r6+sJa3q87Vfazb6TiTRKI1ONLUJI5SwulKMsZBAN6NaD6oZX0ZnhNXOEC56qit6za5wuunYUx/GlBf2r7EtNTZck4sGEkTtDRndiDrL0GAIn1xoCBlOaUQhUeIh90yq0z7Lt0NYuAseUZYEjsk3LCAuuLnfCYEq458rwNF87ew= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=amiBBY9w; arc=none smtp.client-ip=178.60.130.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="amiBBY9w" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=bMQoxABypR0UB7KISfgpW8UUl2xhkm8hNTmM8KjOWUc=; b=amiBBY9w4XNfN+uFx4eMQ0X8lS YVXi87eOE0nqV1T1PDgmD1Bnv+IfuouXQch18pUZO8TqpTDposgx4tmd9BvhzCExtDvmmSOnLFCYP fI+jHYo1b9uLGNPWaJSchJA3tBqEa9fBLFxe5yrUUH5k22lkS/oAymYm2fB7OzezY6uyXozM8sTtK mc13mp+jxjtSobVuTUqeYof9acjn+ZRKvDjQmvOeoSxovlp5b9a9fjHXrngSPKn01MJv4vrKYYDzg 7GyEtcfTXYeTYLxvBwXEXnSH8ugVMCOz3OIJzWLUvAXw+D7j45+13m93sMPI2C89Z3gBDKXDypAd1 wg+GiEfw==; Received: from [177.180.73.242] (helo=quatroqueijos..) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1tawOO-001CCB-K4; Thu, 23 Jan 2025 13:33:04 +0100 From: Thadeu Lima de Souza Cascardo To: linux-kernel@vger.kernel.org Cc: Arnd Bergmann , Greg Kroah-Hartman , Andrew Morton , Dirk VanDerMerwe , Vimal Agrawal , kernel-dev@igalia.com, Thadeu Lima de Souza Cascardo Subject: [PATCH 1/4] char: misc: improve testing Kconfig description Date: Thu, 23 Jan 2025 09:32:46 -0300 Message-Id: <20250123123249.4081674-2-cascardo@igalia.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250123123249.4081674-1-cascardo@igalia.com> References: <20250123123249.4081674-1-cascardo@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Describe that it tests the miscdevice API and include the usual disclaimer about KUnit not being fit for production kernels. While at it, also fix KUnit capitalization. Signed-off-by: Thadeu Lima de Souza Cascardo --- lib/Kconfig.debug | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 775966cf6114..8612ba8771aa 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2493,13 +2493,20 @@ config TEST_IDA tristate "Perform selftest on IDA functions" =20 config TEST_MISC_MINOR - tristate "Basic misc minor Kunit test" if !KUNIT_ALL_TESTS + tristate "miscdevice KUnit test" if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS help - Kunit test for the misc minor. - It tests misc minor functions for dynamic and misc dynamic minor. - This include misc_xxx functions + Kunit test for miscdevice API, specially its behavior in respect to + static and dynamic minor numbers. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running the KUnit test harness, and not intended for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. =20 If unsure, say N. =20 --=20 2.34.1 From nobody Sun Feb 8 14:20:53 2026 Received: from fanzine2.igalia.com (fanzine.igalia.com [178.60.130.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 03DD020F085 for ; Thu, 23 Jan 2025 12:33:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.60.130.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635593; cv=none; b=hASVAsji3HmIuD0PzWZgdf5gMFiJLwqAiZMAOW+fI2+Q3IZNwhbdtrf3j4IbKwHQZihByeplavTCOXhaSKxbsUjEFT/xIO90hCZtj8XuzUK37PFwsVbCWLhUiALM1tHvZaq2w6xqsiavPd8zlAhLhexq0LAqk7YOY2ZNsC+tYGc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635593; c=relaxed/simple; bh=D+pJ341QzireUl++JXycOA5EDpdPCX1LTMsS5+cXTK8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b8p1tJnaPtcnMHRXUiT0UzBIdw04jXDCpiZGMl1Bbdat5b4odSPGfREQe3YQiOAG3HEFoQGHztkZmCKRl2pgKRZdONqbBjvuKhMowOAGaqHMIYDaHs5ggTHxVl2J778pTzeprIHa6T6kVYTbATiY6nqNbVencLUB7NQhJt4DW3I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=nHl4ZlgB; arc=none smtp.client-ip=178.60.130.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="nHl4ZlgB" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=sBIbA+1n50gykMYnO6IfijehXTv+xEsqd1A5wIGj8vU=; b=nHl4ZlgB0HhU1MbkU62Yx1gu4A tBJNNTQFcGAFDS3AENGgiV35o/ibnROIpfMM7iBHRUyNjeqQt0+YCpaHkfFvJgbGsqZdUrUKsoGIO 0Vm8/6MnnD1qbuYw2PhnBt/+N+c0HdhRTsh57YCyVQIZ0xz1X4Ni7bGvdK2XOstBvWeDo0+prS/py lsGbQJY+g6+8seaTBHWOrb/+HbecQYbQh1kIzUln1ciRXogOJoQ42Jacj+HkLKaQbLqlwFNM51fvs f5lZbNxb+S8tz1XeTktTJma74QaHZzljpI9pvH98tD0af0HoZwFyPZaMZpTr9MokTq+esUOpcUoLA ctPX0r3g==; Received: from [177.180.73.242] (helo=quatroqueijos..) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1tawOS-001CCB-Bq; Thu, 23 Jan 2025 13:33:08 +0100 From: Thadeu Lima de Souza Cascardo To: linux-kernel@vger.kernel.org Cc: Arnd Bergmann , Greg Kroah-Hartman , Andrew Morton , Dirk VanDerMerwe , Vimal Agrawal , kernel-dev@igalia.com, Thadeu Lima de Souza Cascardo Subject: [PATCH 2/4] char: misc: add test cases Date: Thu, 23 Jan 2025 09:32:47 -0300 Message-Id: <20250123123249.4081674-3-cascardo@igalia.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250123123249.4081674-1-cascardo@igalia.com> References: <20250123123249.4081674-1-cascardo@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add test cases for static and dynamic minor number allocation and deallocation. While at it, improve description and test suite name. Some of the cases include: - that static and dynamic allocation reserved the expected minors. - that registering duplicate minors or duplicate names will fail. - that failing to create a sysfs file (due to duplicate names) will deallocate the dynamic minor correctly. - that dynamic allocation does not allocate a minor number in the static range. - that there are no collisions when mixing dynamic and static allocations. - that registering a static number in the dynamic range won't conflict with a dynamic allocation. This last test verifies the bug fixed by commit 6d04d2b554b1 ("misc: misc_minor_alloc to use ida for all dynamic/misc dynamic minors") has not regressed. Signed-off-by: Thadeu Lima de Souza Cascardo --- drivers/misc/misc_minor_kunit.c | 515 +++++++++++++++++++++++++++++++- 1 file changed, 513 insertions(+), 2 deletions(-) diff --git a/drivers/misc/misc_minor_kunit.c b/drivers/misc/misc_minor_kuni= t.c index 293e0fb7e43e..84e13bc5c61c 100644 --- a/drivers/misc/misc_minor_kunit.c +++ b/drivers/misc/misc_minor_kunit.c @@ -51,19 +51,530 @@ static void kunit_misc_dynamic_minor(struct kunit *tes= t) misc_deregister(&dev_misc_dynamic_minor); } =20 +struct miscdev_test_case { + const char *str; + int minor; +}; + +static struct miscdev_test_case miscdev_test_ranges[] =3D { + { + .str =3D "lower static range, top", + .minor =3D 15, + }, + { + .str =3D "upper static range, bottom", + .minor =3D 130, + }, + { + .str =3D "lower static range, bottom", + .minor =3D 0, + }, + { + .str =3D "upper static range, top", + .minor =3D MISC_DYNAMIC_MINOR - 1, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str); + +static int miscdev_find_minors(struct kunit_suite *suite) +{ + int ret; + struct miscdevice miscstat =3D { + .name =3D "miscstat", + }; + int i; + + for (i =3D 15; i >=3D 0; i--) { + miscstat.minor =3D i; + ret =3D misc_register(&miscstat); + if (ret =3D=3D 0) + break; + } + + if (ret =3D=3D 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[0].minor =3D miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i =3D 128; i < MISC_DYNAMIC_MINOR; i++) { + miscstat.minor =3D i; + ret =3D misc_register(&miscstat); + if (ret =3D=3D 0) + break; + } + + if (ret =3D=3D 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[1].minor =3D miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i =3D 0; i < miscdev_test_ranges[0].minor; i++) { + miscstat.minor =3D i; + ret =3D misc_register(&miscstat); + if (ret =3D=3D 0) + break; + } + + if (ret =3D=3D 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[2].minor =3D miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i =3D MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--)= { + miscstat.minor =3D i; + ret =3D misc_register(&miscstat); + if (ret =3D=3D 0) + break; + } + + if (ret =3D=3D 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[3].minor =3D miscstat.minor; + misc_deregister(&miscstat); + } + + return ret; +} + +static bool is_valid_dynamic_minor(int minor) +{ + if (minor < 0) + return false; + if (minor =3D=3D MISC_DYNAMIC_MINOR) + return false; + if (minor >=3D 0 && minor <=3D 15) + return false; + if (minor >=3D 128 && minor < MISC_DYNAMIC_MINOR) + return false; + return true; +} + +static void miscdev_test_static_basic(struct kunit *test) +{ + struct miscdevice misc_test =3D { + .name =3D "misc_test", + }; + int ret; + const struct miscdev_test_case *params =3D test->param_value; + + misc_test.minor =3D params->minor; + + ret =3D misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret =3D=3D 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_dynamic_basic(struct kunit *test) +{ + struct miscdevice misc_test =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc_test", + }; + int ret; + + ret =3D misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor)); + if (ret =3D=3D 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_twice(struct kunit *test) +{ + struct miscdevice misc_test =3D { + .name =3D "misc_test", + }; + int ret; + const struct miscdev_test_case *params =3D test->param_value; + + misc_test.minor =3D params->minor; + + ret =3D misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret =3D=3D 0) + misc_deregister(&misc_test); + + ret =3D misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret =3D=3D 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_duplicate_minor(struct kunit *test) +{ + struct miscdevice misc1 =3D { + .name =3D "misc1", + }; + struct miscdevice misc2 =3D { + .name =3D "misc2", + }; + int ret; + const struct miscdev_test_case *params =3D test->param_value; + + misc1.minor =3D params->minor; + misc2.minor =3D params->minor; + + ret =3D misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc1.minor, params->minor); + + ret =3D misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret =3D=3D 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +static void miscdev_test_duplicate_name(struct kunit *test) +{ + struct miscdevice misc1 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc1", + }; + struct miscdevice misc2 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc1", + }; + int ret; + + ret =3D misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + ret =3D misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret =3D=3D 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +/* + * Test that after a duplicate name failure, the reserved minor number is + * freed to be allocated next. + */ +static void miscdev_test_duplicate_name_leak(struct kunit *test) +{ + struct miscdevice misc1 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc1", + }; + struct miscdevice misc2 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc1", + }; + struct miscdevice misc3 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "misc3", + }; + int ret; + int dyn_minor; + + ret =3D misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + /* + * Find out what is the next minor number available. + */ + ret =3D misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + dyn_minor =3D misc3.minor; + misc_deregister(&misc3); + misc3.minor =3D MISC_DYNAMIC_MINOR; + + ret =3D misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret =3D=3D 0) + misc_deregister(&misc2); + + /* + * Now check that we can still get the same minor we found before. + */ + ret =3D misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor); + misc_deregister(&misc3); + + misc_deregister(&misc1); +} + +/* + * Try to register a static minor with a duplicate name. That might not + * deallocate the minor, preventing it from being used again. + */ +static void miscdev_test_duplicate_error(struct kunit *test) +{ + struct miscdevice miscdyn =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "name1", + }; + struct miscdevice miscstat =3D { + .name =3D "name1", + }; + struct miscdevice miscnew =3D { + .name =3D "name2", + }; + int ret; + const struct miscdev_test_case *params =3D test->param_value; + + miscstat.minor =3D params->minor; + miscnew.minor =3D params->minor; + + ret =3D misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + ret =3D misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret =3D=3D 0) + misc_deregister(&miscstat); + + ret =3D misc_register(&miscnew); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor); + if (ret =3D=3D 0) + misc_deregister(&miscnew); + + misc_deregister(&miscdyn); +} + +static void miscdev_test_dynamic_only_range(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + const int dynamic_minors =3D 256; + int i; + bool found_16 =3D false; + + miscdev =3D kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i =3D 0; i < dynamic_minors; i++) { + miscdev[i].minor =3D MISC_DYNAMIC_MINOR; + miscdev[i].name =3D kasprintf(GFP_KERNEL, "misc_test%d", i); + ret =3D misc_register(&miscdev[i]); + if (ret !=3D 0) + break; + /* + * This is the bug we are looking for! + * We asked for a dynamic minor and got a minor in the static range spac= e. + */ + if (miscdev[i].minor >=3D 0 && miscdev[i].minor <=3D 15) { + KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor= ); + i++; + break; + } + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >=3D 0; i--) { + /* Verify we are still able to allocate minor number 16. */ + if (miscdev[i].minor =3D=3D 16) + found_16 =3D true; + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + KUNIT_EXPECT_TRUE(test, found_16); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void miscdev_test_collision(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat =3D { + .name =3D "miscstat", + }; + const int dynamic_minors =3D 256; + int i; + + miscdev =3D kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + miscstat.minor =3D miscdev_test_ranges[0].minor; + ret =3D misc_register(&miscstat); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + + for (i =3D 0; i < dynamic_minors; i++) { + miscdev[i].minor =3D MISC_DYNAMIC_MINOR; + miscdev[i].name =3D kasprintf(GFP_KERNEL, "misc_test%d", i); + ret =3D misc_register(&miscdev[i]); + if (ret !=3D 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >=3D 0; i--) { + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + misc_deregister(&miscstat); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void miscdev_test_collision_reverse(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat =3D { + .name =3D "miscstat", + }; + const int dynamic_minors =3D 256; + int i; + + miscdev =3D kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i =3D 0; i < dynamic_minors; i++) { + miscdev[i].minor =3D MISC_DYNAMIC_MINOR; + miscdev[i].name =3D kasprintf(GFP_KERNEL, "misc_test%d", i); + ret =3D misc_register(&miscdev[i]); + if (ret !=3D 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + KUNIT_EXPECT_EQ(test, ret, 0); + + miscstat.minor =3D miscdev_test_ranges[0].minor; + ret =3D misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + if (ret =3D=3D 0) + misc_deregister(&miscstat); + + for (i--; i >=3D 0; i--) { + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } +} + +static void miscdev_test_conflict(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn =3D { + .name =3D "miscdyn", + .minor =3D MISC_DYNAMIC_MINOR, + }; + struct miscdevice miscstat =3D { + .name =3D "miscstat", + }; + + ret =3D misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + /* + * Try to register a static minor with the same minor as the + * dynamic one. + */ + miscstat.minor =3D miscdyn.minor; + ret =3D misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret =3D=3D 0) + misc_deregister(&miscstat); + + misc_deregister(&miscdyn); +} + +static void miscdev_test_conflict_reverse(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn =3D { + .name =3D "miscdyn", + .minor =3D MISC_DYNAMIC_MINOR, + }; + struct miscdevice miscstat =3D { + .name =3D "miscstat", + }; + + /* + * Find the first available dynamic minor to use it as a static + * minor later on. + */ + ret =3D misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + miscstat.minor =3D miscdyn.minor; + misc_deregister(&miscdyn); + + ret =3D misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdyn.minor); + + /* + * Try to register a dynamic minor after registering a static minor + * within the dynamic range. It should work but get a different + * minor. + */ + miscdyn.minor =3D MISC_DYNAMIC_MINOR; + ret =3D misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_NE(test, miscdyn.minor, miscstat.minor); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + if (ret =3D=3D 0) + misc_deregister(&miscdyn); + + misc_deregister(&miscstat); +} + static struct kunit_case test_cases[] =3D { KUNIT_CASE(kunit_dynamic_minor), KUNIT_CASE(kunit_static_minor), KUNIT_CASE(kunit_misc_dynamic_minor), + KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_basic), + KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params), + KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params), + KUNIT_CASE(miscdev_test_duplicate_name), + KUNIT_CASE(miscdev_test_duplicate_name_leak), + KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_only_range), + KUNIT_CASE(miscdev_test_collision), + KUNIT_CASE(miscdev_test_collision_reverse), + KUNIT_CASE(miscdev_test_conflict), + KUNIT_CASE(miscdev_test_conflict_reverse), {} }; =20 static struct kunit_suite test_suite =3D { - .name =3D "misc_minor_test", + .name =3D "miscdev", + .suite_init =3D miscdev_find_minors, .test_cases =3D test_cases, }; kunit_test_suite(test_suite); =20 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vimal Agrawal"); -MODULE_DESCRIPTION("misc minor testing"); +MODULE_AUTHOR("Thadeu Lima de Souza Cascardo "); +MODULE_DESCRIPTION("Test module for misc character devices"); --=20 2.34.1 From nobody Sun Feb 8 14:20:53 2026 Received: from fanzine2.igalia.com (fanzine.igalia.com [178.60.130.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1506320F095 for ; Thu, 23 Jan 2025 12:33:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.60.130.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635596; cv=none; b=Rp3CNYW95JeXep3rau5Sd/nwRgb52h6dPzLvN7tfRY1ocr/e1WNodeI27fFRUiK4CTU7Hy9fagVdNBQK+xWgtCQ86qhA8omobSWfTIXNdnNrfNkNUo7c70oneUzuyJnTHhm7AAiauMz/0pOleZ2y33Xp/fB2TZLbWt2ZGgoqiW4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635596; c=relaxed/simple; bh=eN2NwzDCq8AKqVoBz0SQAD+2xxELeXCSoeB/C7IJZXM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MYwKjC47bo6c1UWq7kB88HV9irOnCQtsJnDuUo4CnWwBd7GJTwd2OIPZUM1Z2Btufyo73nDBbsbxCoJkdfCedmPARfQDO2kwmiZaaJJXPy/dsGRGCIainXQjx/nYxcH3eVUb4ETlhb9LBrt6AoFosOv2XFbiU921YVTY/yAKpqc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=l6qfud5p; arc=none smtp.client-ip=178.60.130.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="l6qfud5p" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=nH9ot4bqj3q3bMquAfmURiDQsYK6EZv7ECena6dqKmc=; b=l6qfud5pM4nff8ZeDtu7AjNpLh tbiVQnyvQ2ZkIxdhQwIFeXZ1L00eiTP5JLAeqVvM8EJ/6Is+P05tNUeXgGJH8gk+5Q71e/4Hgh3oM tl5pZ9JYII3/twuM+9T+eP8L1TT6XS5O0HabTvPxKhntfUHqTcR/Um17CQ1XdOWu6xSaL4ntwLPYV N80gFi34Z5B6UqALj3ojf8Jy82xsrIeC9IIYQjRBtyj/bVMCrz7WrZxyWLXeAnGjgoHtAn4YyOUGT CnikpcTFa4fJVg9Fqg0lvPLOv6zYy4QqKLzl/AU/kt5yNcQuarjOfTs1IuDZdEuOwvvC2hO8HzfkM JCGvlsQA==; Received: from [177.180.73.242] (helo=quatroqueijos..) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1tawOW-001CCB-Mg; Thu, 23 Jan 2025 13:33:13 +0100 From: Thadeu Lima de Souza Cascardo To: linux-kernel@vger.kernel.org Cc: Arnd Bergmann , Greg Kroah-Hartman , Andrew Morton , Dirk VanDerMerwe , Vimal Agrawal , kernel-dev@igalia.com, Thadeu Lima de Souza Cascardo Subject: [PATCH 3/4] char: misc: restrict the dynamic range to exclude reserved minors Date: Thu, 23 Jan 2025 09:32:48 -0300 Message-Id: <20250123123249.4081674-4-cascardo@igalia.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250123123249.4081674-1-cascardo@igalia.com> References: <20250123123249.4081674-1-cascardo@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When this was first reported [1], the possibility of having sufficient number of dynamic misc devices was theoretical. What we know from commit ab760791c0cf ("char: misc: Increase the maximum number of dynamic misc devices to 1048448"), is that the miscdevice interface has been used for allocating more than the single-shot devices it was designed for. On such systems, it is certain that the dynamic allocation will allocate certain reserved minor numbers, leading to failures when a later driver tries to claim its reserved number. Fixing this is a simple matter of defining the IDA range to allocate from to exclude minors up to and including 15. [1] https://lore.kernel.org/all/1257813017-28598-3-git-send-email-cascardo@= holoscopio.com/ Signed-off-by: Thadeu Lima de Souza Cascardo --- drivers/char/misc.c | 4 +++- include/linux/miscdevice.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 2cf595d2e10b..7a768775e558 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -68,8 +68,10 @@ static int misc_minor_alloc(int minor) int ret =3D 0; =20 if (minor =3D=3D MISC_DYNAMIC_MINOR) { + int max =3D DYNAMIC_MINORS - 1 - MISC_STATIC_MAX_MINOR - 1; /* allocate free id */ - ret =3D ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL); + /* Minors from 0 to 15 are reserved. */ + ret =3D ida_alloc_max(&misc_minors_ida, max, GFP_KERNEL); if (ret >=3D 0) { ret =3D DYNAMIC_MINORS - ret - 1; } else { diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 69e110c2b86a..911a294d17b5 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -21,6 +21,7 @@ #define APOLLO_MOUSE_MINOR 7 /* unused */ #define PC110PAD_MINOR 9 /* unused */ /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ +#define MISC_STATIC_MAX_MINOR 15 /* Top of first reserved range */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ #define APM_MINOR_DEV 134 --=20 2.34.1 From nobody Sun Feb 8 14:20:53 2026 Received: from fanzine2.igalia.com (fanzine.igalia.com [178.60.130.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F06DC20F998; Thu, 23 Jan 2025 12:33:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.60.130.6 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635600; cv=none; b=Ez8h5os5VfHbi0nW/UXel3h/w1AAgC0jJ+oo8qaRL7SNpt2G4zmXFHG9jfin26RTYt8BcbSQVBe0sVBHYSh3WFyRnVsfxJDhlwlkzXLMoJuyo3PWAcNhiumOiH+hTXie7XI7/jQ5CkWIA/nanU2+qZ39X4xJQEUNL7TP0rs0mFQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1737635600; c=relaxed/simple; bh=C7yAxwNOJhR5ZUY/4+Hi83tor/0lXnWfYviV9iH2+HU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=bS2I3n0VtnzR0LEshR1cwcC8IGFT1R8n1F9ZgPfy134F04uj+1kU8q7hsmkdk3d/3PKi8Ra5xObtpv0gIgBbaBnCEr3SzWmvBhr396fMTzVaqWb/QWx8eQL2b3S3ABXv4IcBPfDu5muZbMYvAs6iYuaSnLzR7QkleFQ5sfP6Z9I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=HGSRykSr; arc=none smtp.client-ip=178.60.130.6 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="HGSRykSr" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=dn0Qmm3YjXebpTIvQaz7gZeWlcXp/17xwRMzaRLNhIA=; b=HGSRykSr6WzpR23l1Nz7gw0K/P eFejM6yC9zAx1XXkPh28P1IjtIORnzIlXhZNvpv9hCN6BBzXjHWG8G0nAJDmQwPZwRNV2WKFhdF1Y cxKHp8zepSTXNzpWbiSE8a9yJejvjg3aN1KpKBEsmeLl8fZj4O4Qk2LJPCvXcjiEuaeIQXOVR7+HA YooE8wGdynbdvUjyhSwtDn17W8z846Pwb1TPzYBdiLp8NTOdDcL5mrU1FoYv10r1XQHs02GlUwyhb n4/4RdH6l58IQIzllezR/3KTHaoQycViAgc6Gb5qmqPrbgYfcb1z9T+tHts/JOhi+3hu39MnyhxYS 0NPazpqw==; Received: from [177.180.73.242] (helo=quatroqueijos..) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1tawOb-001CCB-0M; Thu, 23 Jan 2025 13:33:17 +0100 From: Thadeu Lima de Souza Cascardo To: linux-kernel@vger.kernel.org Cc: Arnd Bergmann , Greg Kroah-Hartman , Andrew Morton , Dirk VanDerMerwe , Vimal Agrawal , kernel-dev@igalia.com, Thadeu Lima de Souza Cascardo , stable@vger.kernel.org Subject: [PATCH 4/4] char: misc: deallocate static minor in error path Date: Thu, 23 Jan 2025 09:32:49 -0300 Message-Id: <20250123123249.4081674-5-cascardo@igalia.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250123123249.4081674-1-cascardo@igalia.com> References: <20250123123249.4081674-1-cascardo@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When creating sysfs files fail, the allocated minor must be freed such that it can be later reused. That is specially harmful for static minor numbers, since those would always fail to register later on. Fixes: 6d04d2b554b1 ("misc: misc_minor_alloc to use ida for all dynamic/mis= c dynamic minors") Cc: stable@vger.kernel.org Signed-off-by: Thadeu Lima de Souza Cascardo --- drivers/char/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 7a768775e558..7843a1a34d64 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -266,8 +266,8 @@ int misc_register(struct miscdevice *misc) device_create_with_groups(&misc_class, misc->parent, dev, misc, misc->groups, "%s", misc->name); if (IS_ERR(misc->this_device)) { + misc_minor_free(misc->minor); if (is_dynamic) { - misc_minor_free(misc->minor); misc->minor =3D MISC_DYNAMIC_MINOR; } err =3D PTR_ERR(misc->this_device); --=20 2.34.1