From a62cd5a153ffe18c27aff02685ed75c5bc4509a2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 9 Mar 2026 13:24:03 +0900 Subject: [PATCH] sd-device: refuse spurious properties Properties are set through uevent, udev rules, or program output by IMPORT. They may contain spurious characters and udev database parsers may be confused. Let's refuse spurious properties. --- src/libsystemd/sd-device/sd-device.c | 23 +++++++++++ src/libsystemd/sd-device/test-sd-device.c | 48 +++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index e6b05b636a..ef67c649d1 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -30,6 +30,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "utf8.h" int device_new_aux(sd_device **ret) { sd_device *device; @@ -81,12 +82,34 @@ static sd_device* device_free(sd_device *device) { DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free); +static bool property_is_valid(const char *key, const char *value) { + /* Device properties may be saved to database file, then may be parsed from the file. When if a + * property contains spurious characters, then the parser may be confused. Let's refuse spurious + * properties, even if it is internal, which will not be saved to database file, for consistency. */ + + if (isempty(key) || !in_charset(key, ALPHANUMERICAL "_.")) + return false; + + /* an empty value means unset the property, hence that's fine. */ + if (isempty(value)) + return true; + + /* refuse invalid UTF8 and control characters */ + if (!utf8_is_valid(value) || string_has_cc(value, /* ok= */ NULL)) + return false; + + return true; +} + int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db) { OrderedHashmap **properties; assert(device); assert(key); + if (!property_is_valid(key, value)) + return -EINVAL; + if (db) properties = &device->properties_db; else diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index db7c9420cc..43b76f46e9 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -841,6 +841,54 @@ TEST(devname_from_devnum) { } } +TEST(device_add_property) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *val; + + ASSERT_OK(sd_device_new_from_syspath(&dev, "/sys/class/net/lo")); + + /* add a property */ + ASSERT_OK(device_add_property(dev, "hoge", "foo")); + ASSERT_OK(sd_device_get_property_value(dev, "hoge", &val)); + ASSERT_STREQ(val, "foo"); + + /* update an existing property */ + ASSERT_OK(device_add_property(dev, "hoge", "bar")); + ASSERT_OK(sd_device_get_property_value(dev, "hoge", &val)); + ASSERT_STREQ(val, "bar"); + + /* remove an existing property */ + ASSERT_OK(device_add_property(dev, "hoge", NULL)); + ASSERT_ERROR(sd_device_get_property_value(dev, "hoge", &val), ENOENT); + + /* add a property again */ + ASSERT_OK(device_add_property(dev, "hoge", "foo")); + ASSERT_OK(sd_device_get_property_value(dev, "hoge", &val)); + ASSERT_STREQ(val, "foo"); + + /* remove it with an empty string */ + ASSERT_OK(device_add_property(dev, "hoge", "")); + ASSERT_ERROR(sd_device_get_property_value(dev, "hoge", &val), ENOENT); + + /* check internal property (starting with dot) */ + ASSERT_OK(device_add_property(dev, ".hoge", "baz")); + ASSERT_OK(sd_device_get_property_value(dev, ".hoge", &val)); + ASSERT_STREQ(val, "baz"); + + /* refuse invalid property names */ + ASSERT_ERROR(device_add_property(dev, "hoge-hoge", "aaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge=hoge", "aaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge hoge", "aaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge\nhoge", "aaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge\rhoge", "aaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge\thoge", "aaa"), EINVAL); + + /* refuse invalid property values */ + ASSERT_ERROR(device_add_property(dev, "hoge", "aaa\naaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge", "aaa\raaa"), EINVAL); + ASSERT_ERROR(device_add_property(dev, "hoge", "aaa\taaa"), EINVAL); +} + static int intro(void) { int r;