]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-device: refuse spurious properties
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 9 Mar 2026 04:24:03 +0000 (13:24 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 9 Mar 2026 21:32:30 +0000 (06:32 +0900)
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
src/libsystemd/sd-device/test-sd-device.c

index e6b05b636a8713cb9cac0f66c2a76fcaa28e56ad..ef67c649d1ebdf95d63d2e85c69d42bd8df1de15 100644 (file)
@@ -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
index db7c9420cc605fbf247fc2f0ef06eec5e7e64599..43b76f46e9baf82da690b9923baf732dcdfbad87 100644 (file)
@@ -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;