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;