Skip to content

Commit e7617e7

Browse files
committed
Avoid buffer overflow in uevent_sender_send()
action, devpath, and other values are unbounded, so ensure that we don't overflow the property buffer. This does not cross a security boundary and is not typically used with untrusted data, so not really a vulnerability. But show a proper error message instead of crashing if it happens. Thanks to Simon Schmitt <[email protected]> for pointing this out!
1 parent 2826b62 commit e7617e7

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

src/uevent_sender.c

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,22 @@ string_hash32(const char *str)
194194
return h;
195195
}
196196

197+
static ssize_t
198+
append_property(char *array, size_t size, ssize_t offset, const char *name, const char *value)
199+
{
200+
int r;
201+
assert(offset < size);
202+
r = snprintf(array + offset, size - offset, "%s%s", name, value);
203+
// include the NUL terminator in the string length, as we need to keep it as a separator between keys
204+
++r;
205+
if (r + offset >= size) {
206+
fprintf(stderr, "ERROR: uevent_sender_send: Property buffer overflow\n");
207+
abort();
208+
}
209+
210+
return r;
211+
}
212+
197213
/* this mirrors the code from systemd/src/libudev/libudev-monitor.c,
198214
* udev_monitor_send_device() */
199215
void
@@ -206,6 +222,7 @@ uevent_sender_send(uevent_sender * sender, const char *devpath, const char *acti
206222
const char *subsystem;
207223
const char *devname;
208224
const char *devtype;
225+
char seqnumstr[20];
209226
struct udev_device *device;
210227
struct udev_monitor_netlink_header nlh;
211228
static unsigned long long seqnum = 1;
@@ -224,27 +241,15 @@ uevent_sender_send(uevent_sender * sender, const char *devpath, const char *acti
224241

225242
/* build NUL-terminated property array */
226243
count = 0;
227-
strcpy(props, "ACTION=");
228-
strcat(props, action);
229-
count += strlen(props) + 1;
230-
strcpy(props + count, "DEVPATH=");
231-
strcat(props + count, udev_device_get_devpath(device));
232-
count += strlen(props + count) + 1;
233-
strcpy(props + count, "SUBSYSTEM=");
234-
strcat(props + count, subsystem);
235-
count += strlen(props + count) + 1;
236-
snprintf(props + count, sizeof(props) - count, "SEQNUM=%llu", seqnum++);
237-
count += strlen(props + count) + 1;
238-
if (devname) {
239-
strcpy(props + count, "DEVNAME=");
240-
strcat(props + count, devname);
241-
count += strlen(props + count) + 1;
242-
}
243-
if (devtype) {
244-
strcpy(props + count, "DEVTYPE=");
245-
strcat(props + count, devtype);
246-
count += strlen(props + count) + 1;
247-
}
244+
count += append_property(props, sizeof (props), count, "ACTION=", action);
245+
count += append_property(props, sizeof (props), count, "DEVPATH=", udev_device_get_devpath(device));
246+
count += append_property(props, sizeof (props), count, "SUBSYSTEM=", subsystem);
247+
snprintf(seqnumstr, sizeof(seqnumstr), "%llu", seqnum++);
248+
count += append_property(props, sizeof (props), count, "SEQNUM=", seqnumstr);
249+
if (devname)
250+
count += append_property(props, sizeof (props), count, "DEVNAME=", devname);
251+
if (devtype)
252+
count += append_property(props, sizeof (props), count, "DEVTYPE=", devtype);
248253

249254
/* add versioned header */
250255
memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header));

tests/test-umockdev.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,46 @@ t_testbed_uevent_error(UMockdevTestbedFixture * fixture, gconstpointer data)
771771
udev_unref(udev);
772772
}
773773

774+
static void
775+
t_testbed_uevent_null_action(UMockdevTestbedFixture * fixture, gconstpointer data)
776+
{
777+
gchar *syspath;
778+
779+
syspath = umockdev_testbed_add_device(fixture->testbed, "pci", "mydev", NULL, NULL, NULL);
780+
g_assert(syspath);
781+
782+
if (g_test_subprocess()) {
783+
umockdev_testbed_uevent(fixture->testbed, syspath, NULL);
784+
}
785+
g_test_trap_subprocess(NULL, 0, 0);
786+
g_test_trap_assert_failed();
787+
g_test_trap_assert_stderr ("*action != NULL*");
788+
789+
g_free(syspath);
790+
}
791+
792+
static void
793+
t_testbed_uevent_action_overflow(UMockdevTestbedFixture * fixture, gconstpointer data)
794+
{
795+
gchar *syspath;
796+
797+
syspath = umockdev_testbed_add_device(fixture->testbed, "pci", "mydev", NULL, NULL, NULL);
798+
g_assert(syspath);
799+
800+
/* overly long action */
801+
if (g_test_subprocess()) {
802+
char long_action[4096];
803+
memset(long_action, 'a', sizeof(long_action));
804+
long_action[sizeof(long_action)-1] = '\0';
805+
umockdev_testbed_uevent(fixture->testbed, syspath, long_action);
806+
}
807+
g_test_trap_subprocess(NULL, 0, 0);
808+
g_test_trap_assert_failed();
809+
g_test_trap_assert_stderr ("*uevent_sender_send*Property buffer overflow*");
810+
811+
g_free(syspath);
812+
}
813+
774814
static void
775815
t_testbed_add_from_string(UMockdevTestbedFixture * fixture, gconstpointer data)
776816
{
@@ -2056,6 +2096,10 @@ main(int argc, char **argv)
20562096
t_testbed_uevent_no_listener, t_testbed_fixture_teardown);
20572097
g_test_add("/umockdev-testbed/uevent/error", UMockdevTestbedFixture, NULL, t_testbed_fixture_setup,
20582098
t_testbed_uevent_error, t_testbed_fixture_teardown);
2099+
g_test_add("/umockdev-testbed/uevent/null_action", UMockdevTestbedFixture, NULL, t_testbed_fixture_setup,
2100+
t_testbed_uevent_null_action, t_testbed_fixture_teardown);
2101+
g_test_add("/umockdev-testbed/uevent/action_overflow", UMockdevTestbedFixture, NULL, t_testbed_fixture_setup,
2102+
t_testbed_uevent_action_overflow, t_testbed_fixture_teardown);
20592103

20602104
/* tests for mocking USB devices */
20612105
g_test_add("/umockdev-testbed-usb/lsusb", UMockdevTestbedFixture, NULL, t_testbed_fixture_setup,

0 commit comments

Comments
 (0)