summaryrefslogtreecommitdiff
path: root/device.c
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2021-09-27 18:56:21 +0200
committerFelix Fietkau <nbd@nbd.name>2021-09-27 18:58:01 +0200
commit5a4ac30c7a15712d01110befec1acfe86c2cbed0 (patch)
treead52b0310a781078efb9b016fbe80d89ca50001d /device.c
parent08e954e137ffcf7770200bbd6476dc36bbd326f5 (diff)
downloadnetifd-5a4ac30c7a15712d01110befec1acfe86c2cbed0.tar.gz
netifd: rework/fix device free handling
Instead of explicitly preventing free in specific code sections using device_lock/device_unlock, defer all device free handling via uloop timeout This avoids an entire class of lurking use-after-free bugs triggered by device event processing and simplifies the code Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'device.c')
-rw-r--r--device.c51
1 files changed, 20 insertions, 31 deletions
diff --git a/device.c b/device.c
index bb39ea7..b3d0e85 100644
--- a/device.c
+++ b/device.c
@@ -99,18 +99,6 @@ device_type_get(const char *tname)
return NULL;
}
-void device_lock(void)
-{
- __devlock++;
-}
-
-void device_unlock(void)
-{
- __devlock--;
- if (!__devlock)
- device_free_unused(NULL);
-}
-
static int device_vlan_len(struct kvlist *kv, const void *data)
{
return sizeof(unsigned int);
@@ -895,14 +883,27 @@ device_free(struct device *dev)
}
static void
-__device_free_unused(struct device *dev)
+__device_free_unused(struct uloop_timeout *timeout)
{
- if (!safe_list_empty(&dev->users) ||
- !safe_list_empty(&dev->aliases) ||
- dev->current_config || __devlock)
- return;
+ struct device *dev, *tmp;
+
+ avl_for_each_element_safe(&devices, dev, avl, tmp) {
+ if (!safe_list_empty(&dev->users) ||
+ !safe_list_empty(&dev->aliases) ||
+ dev->current_config)
+ continue;
+
+ device_free(dev);
+ }
+}
+
+void device_free_unused(void)
+{
+ static struct uloop_timeout free_timer = {
+ .cb = __device_free_unused,
+ };
- device_free(dev);
+ uloop_timeout_set(&free_timer, 1);
}
void device_remove_user(struct device_user *dep)
@@ -919,19 +920,7 @@ void device_remove_user(struct device_user *dep)
safe_list_del(&dep->list);
dep->dev = NULL;
D(DEVICE, "Remove user for device '%s', refcount=%d\n", dev->ifname, device_refcount(dev));
- __device_free_unused(dev);
-}
-
-void
-device_free_unused(struct device *dev)
-{
- struct device *tmp;
-
- if (dev)
- return __device_free_unused(dev);
-
- avl_for_each_element_safe(&devices, dev, avl, tmp)
- __device_free_unused(dev);
+ device_free_unused();
}
void