mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
Input: reject parent overrides on managed input devices
When an input device is allocated with devm_input_allocate_device(), the owner struct device is set as dev->dev.parent and holds the devm_input_device_release resource. Later, input_register_device() attaches the devm_input_device_unregister resource to dev->dev.parent. Clearing or overriding dev->dev.parent before registration splits devres ownership across different devices, which breaks managed lifetime assumptions. Verify via devres_find() that dev->dev.parent is non-NULL and still holds the matching devm_input_device_release entry before registering a managed input device, rejecting registration with -EINVAL otherwise. Also add KUnit test coverage in input_test.c verifying that reparented and NULL-parent managed input devices are rejected while preserving the owner parent succeeds. Assisted-by: LLM Link: https://patch.msgid.link/asmCc1VxVPLIMgY6@google.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
2b4bcdd40c
commit
7f0b4c25fd
|
|
@ -2396,6 +2396,14 @@ int input_register_device(struct input_dev *dev)
|
|||
}
|
||||
|
||||
if (dev->devres_managed) {
|
||||
if (!dev->dev.parent ||
|
||||
!devres_find(dev->dev.parent, devm_input_device_release,
|
||||
devm_input_device_match, dev)) {
|
||||
dev_err(&dev->dev,
|
||||
"Parent of managed input device was overridden, refusing to register\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
devres = devres_alloc(devm_input_device_unregister,
|
||||
sizeof(*devres), GFP_KERNEL);
|
||||
if (!devres)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
#define POLL_INTERVAL 100
|
||||
|
|
@ -161,11 +162,44 @@ static void input_test_grab(struct kunit *test)
|
|||
input_put_device(input_dev);
|
||||
}
|
||||
|
||||
static void input_test_managed_parent_override(struct kunit *test)
|
||||
{
|
||||
struct device *owner, *other_dev;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
owner = kunit_device_register(test, "input-test-owner");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, owner);
|
||||
|
||||
other_dev = kunit_device_register(test, "input-test-other");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_dev);
|
||||
|
||||
/* Overriding dev.parent to another device must be rejected */
|
||||
input_dev = devm_input_allocate_device(owner);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, input_dev);
|
||||
input_dev->name = "Managed input device (reparented)";
|
||||
input_dev->dev.parent = other_dev;
|
||||
KUNIT_EXPECT_EQ(test, input_register_device(input_dev), -EINVAL);
|
||||
|
||||
/* Clearing dev.parent to NULL must be rejected */
|
||||
input_dev = devm_input_allocate_device(owner);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, input_dev);
|
||||
input_dev->name = "Managed input device (null parent)";
|
||||
input_dev->dev.parent = NULL;
|
||||
KUNIT_EXPECT_EQ(test, input_register_device(input_dev), -EINVAL);
|
||||
|
||||
/* Preserving original owner as dev.parent succeeds */
|
||||
input_dev = devm_input_allocate_device(owner);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, input_dev);
|
||||
input_dev->name = "Managed input device (valid parent)";
|
||||
KUNIT_EXPECT_EQ(test, input_register_device(input_dev), 0);
|
||||
}
|
||||
|
||||
static struct kunit_case input_tests[] = {
|
||||
KUNIT_CASE(input_test_polling),
|
||||
KUNIT_CASE(input_test_timestamp),
|
||||
KUNIT_CASE(input_test_match_device_id),
|
||||
KUNIT_CASE(input_test_grab),
|
||||
KUNIT_CASE(input_test_managed_parent_override),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user