genode_c_api/usb: prevent invalid pointer deref

Limit the use of Reg_list::for_each that caches a next pointer
of its items to allow destruction of items in its lambda body.
Instead provide an Reg_list::apply function in addition, which
takes a condition lambda to find the matching item, and a lambda
processed on it. In most use-cases where for_each was used, only
one item was searched for. Here we can use apply now., without
the need for a cached pointer, nor too many iterations.

Fixes genodelabs/genode#5349
This commit is contained in:
Stefan Kalkowski 2024-09-24 11:59:54 +02:00 committed by Christian Helmuth
parent a16ca36eb6
commit c0a0c0ae71

View File

@ -112,6 +112,15 @@ struct Reg_list
fn(e->_object); fn(e->_object);
} }
} }
void apply(auto const &condition, auto const & fn)
{
for (Element *e = _elements.first(); e; e = e->next())
if (condition(e->_object)) {
fn(e->_object);
return;
}
}
}; };
@ -907,8 +916,11 @@ Device_component::_handle_request(Constructible<Packet_descriptor> &cpd,
granted = true; granted = true;
break; break;
case P::SET_INTERFACE: case P::SET_INTERFACE:
_interfaces.for_each([&] (Interface_component & ic) { _interfaces.apply(
if (ic._iface_idx == cpd->index) granted = true; }); [&] (Interface_component & ic) {
return ic._iface_idx == cpd->index;
},
[&] (Interface_component &) { granted = true; });
if (granted) break; if (granted) break;
[[fallthrough]]; [[fallthrough]];
default: granted = _controls; default: granted = _controls;
@ -947,8 +959,10 @@ void Device_component::release_interface(Interface_capability cap)
if (!cap.valid()) if (!cap.valid())
return; return;
_interfaces.for_each([&] (Interface_component & ic) { _interfaces.apply(
if (cap.local_name() == ic.cap().local_name()) [&] (Interface_component & ic) {
return cap.local_name() == ic.cap().local_name(); },
[&] (Interface_component & ic) {
destroy(_heap, &ic); }); destroy(_heap, &ic); });
} }
@ -958,8 +972,10 @@ bool Device_component::request(genode_usb_req_callback_t const callback,
{ {
bool ret = false; bool ret = false;
_interfaces.for_each([&] (Interface_component & ic) { _interfaces.apply(
if (ic.request(callback, opaque_data)) ret = true; }); [&] (Interface_component & ic) {
return ic.request(callback, opaque_data); },
[&] (Interface_component &) { ret = true; });
return ret ? ret : Base::request(callback, opaque_data); return ret ? ret : Base::request(callback, opaque_data);
} }
@ -1003,10 +1019,11 @@ Device_component::handle_response(genode_usb_request_handle_t handle,
[&] (Packet_error) {}); [&] (Packet_error) {});
if (!ret) if (!ret)
_interfaces.for_each([&] (Interface_component & ic) { _interfaces.apply(
if (ret) return; [&] (Interface_component & ic) {
if (ic.handle_response(handle, value, actual_sizes)) ret = true; return ic.handle_response(handle, value, actual_sizes); },
}); [&] (Interface_component &) {
ret = true; });
return ret; return ret;
} }
@ -1143,12 +1160,13 @@ void Session_component::_release(Device_component &dc)
genode_usb_device::Label name = dc._device_label; genode_usb_device::Label name = dc._device_label;
destroy(_heap, &dc); destroy(_heap, &dc);
_devices.for_each([&] (genode_usb_device & device) { _devices.apply(
if (device.label() != name) [&] (genode_usb_device & device) {
return; return device.label() == name; },
_sessions.for_each([&] (Session_component &sc) { [&] (genode_usb_device & device) {
if (sc._matches(device)) sc.update_devices_rom(); }); _sessions.for_each([&] (Session_component &sc) {
_root.report(); if (sc._matches(device)) sc.update_devices_rom(); });
_root.report();
}); });
} }
@ -1157,22 +1175,24 @@ void Session_component::set_interface(genode_usb_device::Label label,
uint16_t num, uint16_t alt) uint16_t num, uint16_t alt)
{ {
bool changed = false; bool changed = false;
_devices.for_each([&] (genode_usb_device & d) { _devices.apply(
if (d.label() != label) [&] (genode_usb_device & d) {
return; return d.label() == label; },
d.configs.for_each([&] (genode_usb_configuration & c) { [&] (genode_usb_device & d) {
if (!c.active) d.configs.apply(
return; [&] (genode_usb_configuration & c) {
c.interfaces.for_each([&] (genode_usb_interface & i) { return c.active; },
if (i.desc.number != num) [&] (genode_usb_configuration & c) {
return; c.interfaces.apply(
[&] (genode_usb_interface & i) {
if (i.active != (i.desc.alt_settings == alt)) { return i.desc.number == num; },
i.active = (i.desc.alt_settings == alt); [&] (genode_usb_interface & i) {
changed = true; if (i.active != (i.desc.alt_settings == alt)) {
} i.active = (i.desc.alt_settings == alt);
changed = true;
}
});
}); });
});
}); });
if (changed) { if (changed) {
@ -1186,15 +1206,17 @@ void Session_component::set_configuration(genode_usb_device::Label label,
uint16_t num) uint16_t num)
{ {
bool changed = false; bool changed = false;
_devices.for_each([&] (genode_usb_device & d) { _devices.apply(
if (d.label() != label) [&] (genode_usb_device & d) {
return; return d.label() == label; },
d.configs.for_each([&] (genode_usb_configuration & c) { [&] (genode_usb_device & d) {
if (c.active != (c.desc.config_value == num)) { d.configs.apply(
c.active = (c.desc.config_value == num); [&] (genode_usb_configuration & c) {
changed = true; return c.active != (c.desc.config_value == num); },
} [&] (genode_usb_configuration & c) {
}); c.active = (c.desc.config_value == num);
changed = true;
});
}); });
if (changed) { if (changed) {
@ -1211,18 +1233,19 @@ bool Session_component::matches(genode_usb_device::Label label, uint8_t iface)
* all interfaces are allowed, otherwise check for the iface number * all interfaces are allowed, otherwise check for the iface number
*/ */
bool ret = false; bool ret = false;
_devices.for_each([&] (genode_usb_device const & d) { _devices.apply(
if (d.label() != label) [&] (genode_usb_device const & d) {
return; return d.label() == label; },
_device_policy(d, [&] (Xml_node dev_node) { [&] (genode_usb_device const & d) {
if (!dev_node.has_sub_node("interface")) _device_policy(d, [&] (Xml_node dev_node) {
ret = true; if (!dev_node.has_sub_node("interface"))
else ret = true;
dev_node.for_each_sub_node("interface", [&] (Xml_node & node) { else
if (node.attribute_value<uint8_t>("number", 255) == iface) dev_node.for_each_sub_node("interface", [&] (Xml_node & node) {
ret = true; if (node.attribute_value<uint8_t>("number", 255) == iface)
}); ret = true;
}); });
});
}); });
return ret; return ret;
} }
@ -1232,17 +1255,21 @@ template <typename FN>
void Session_component::for_each_ep(genode_usb_device::Label label, void Session_component::for_each_ep(genode_usb_device::Label label,
uint8_t iface_idx, FN const & fn) uint8_t iface_idx, FN const & fn)
{ {
_devices.for_each([&] (genode_usb_device const & d) { _devices.apply(
if (d.label() != label) [&] (genode_usb_device const & d) {
return; return d.label() == label; },
d.configs.for_each([&] (genode_usb_configuration const & cfg) { [&] (genode_usb_device & d) {
if (!cfg.active) d.configs.apply(
return; [&] (genode_usb_configuration const & cfg) {
cfg.interfaces.for_each([&] (genode_usb_interface const & iface) { return cfg.active; },
if (iface.desc.number == iface_idx) [&] (genode_usb_configuration & cfg) {
iface.endpoints.for_each(fn); cfg.interfaces.apply(
[&] (genode_usb_interface const & iface) {
return iface.desc.number == iface_idx; },
[&] (genode_usb_interface const & iface) {
iface.endpoints.for_each(fn);
});
}); });
});
}); });
} }
@ -1255,11 +1282,12 @@ void Session_component::announce_device(genode_usb_device const & device)
void Session_component::discontinue_device(genode_usb_device const & device) void Session_component::discontinue_device(genode_usb_device const & device)
{ {
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.apply(
if (dc._device_label != device.label()) [&] (Device_component & dc) {
return; return dc._device_label == device.label(); },
dc.disconnect(); [&] (Device_component & dc) {
update_devices_rom(); dc.disconnect();
update_devices_rom();
}); });
} }
@ -1267,13 +1295,14 @@ void Session_component::discontinue_device(genode_usb_device const & device)
void Session_component::update_policy() void Session_component::update_policy()
{ {
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.for_each([&] (Device_component & dc) {
_devices.for_each([&] (genode_usb_device const & device) { _devices.apply(
if (device.label() != dc._device_label) [&] (genode_usb_device const & device) {
return; return device.label() == dc._device_label; },
if (!_matches(device)) { [&] (genode_usb_device const & device) {
dc.disconnect(); if (!_matches(device)) {
_release_fn(device.bus, device.dev); dc.disconnect();
} _release_fn(device.bus, device.dev);
}
}); });
}); });
update_devices_rom(); update_devices_rom();
@ -1298,8 +1327,11 @@ bool Session_component::acquired(genode_usb_device const &dev)
return false; return false;
bool ret = false; bool ret = false;
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.apply(
if (dc._device_label == dev.label()) ret = dc.connected(); }); [&] (Device_component & dc) {
return dc._device_label == dev.label(); },
[&] (Device_component & dc) {
ret = dc.connected(); });
return ret; return ret;
} }
@ -1309,10 +1341,11 @@ bool Session_component::request(genode_usb_device const &dev,
void *opaque_data) void *opaque_data)
{ {
bool ret = false; bool ret = false;
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.apply(
if (dc._device_label == dev.label()) [&] (Device_component & dc) {
if (dc.request(callback, opaque_data)) ret = true; return dc._device_label == dev.label(); },
}); [&] (Device_component & dc) {
if (dc.request(callback, opaque_data)) ret = true; });
return ret; return ret;
} }
@ -1323,8 +1356,10 @@ Session_component::handle_response(genode_usb_request_handle_t handle,
uint32_t *actual_sizes) uint32_t *actual_sizes)
{ {
bool handled = false; bool handled = false;
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.apply(
if (!handled) handled = dc.handle_response(handle, v, actual_sizes); }); [&] (Device_component & dc) {
return dc.handle_response(handle, v, actual_sizes); },
[&] (Device_component &) { handled = true; });
return handled; return handled;
} }
@ -1341,23 +1376,27 @@ Device_capability Session_component::acquire_device(Device_name const &name)
Device_capability cap; Device_capability cap;
bool found = false; bool found = false;
_devices.for_each([&] (genode_usb_device & device) { _devices.apply(
if (device.label() != name || !_matches(device)) [&] (genode_usb_device & device) {
return; return device.label() == name && _matches(device); },
found = true; [&] (genode_usb_device & device) {
_sessions.for_each([&] (Session_component &sc) { found = true;
if (sc.acquired(device)) found = false; }); _sessions.apply(
[&] (Session_component &sc) {
return sc.acquired(device); },
[&] (Session_component &) {
found = false; });
if (!found) { if (!found) {
warning("USB device ", name, warning("USB device ", name,
"already acquired by another session"); "already acquired by another session");
} }
cap = _acquire(device.label(), true); cap = _acquire(device.label(), true);
_sessions.for_each([&] (Session_component &sc) { _sessions.for_each([&] (Session_component &sc) {
if (sc._matches(device)) sc.update_devices_rom(); }); if (sc._matches(device)) sc.update_devices_rom(); });
_root.report(); _root.report();
}); });
if (!found) if (!found)
@ -1371,21 +1410,25 @@ Device_capability Session_component::acquire_device(Device_name const &name)
Device_capability Session_component::acquire_single_device() Device_capability Session_component::acquire_single_device()
{ {
Device_capability cap; Device_capability cap;
_devices.for_each([&] (genode_usb_device & device) { _devices.apply(
if (cap.valid() || !_matches(device)) [&] (genode_usb_device & device) {
return; return !cap.valid() && _matches(device); },
[&] (genode_usb_device & device) {
bool acquired = false; bool acquired = false;
_sessions.for_each([&] (Session_component &sc) { _sessions.apply(
if (sc.acquired(device)) acquired = true; }); [&] (Session_component &sc) {
return sc.acquired(device); },
[&] (Session_component &) {
acquired = true; });
if (acquired) if (acquired)
return; return;
cap = _acquire(device.label(), true); cap = _acquire(device.label(), true);
_sessions.for_each([&] (Session_component &sc) { _sessions.for_each([&] (Session_component &sc) {
if (sc._matches(device)) sc.update_devices_rom(); }); if (sc._matches(device)) sc.update_devices_rom(); });
_root.report(); _root.report();
}); });
return cap; return cap;
} }
@ -1396,8 +1439,10 @@ void Session_component::release_device(Device_capability cap)
if (!cap.valid()) if (!cap.valid())
return; return;
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.apply(
if (cap.local_name() == dc.cap().local_name()) [&] (Device_component & dc) {
return cap.local_name() == dc.cap().local_name(); },
[&] (Device_component & dc) {
_release(dc); }); _release(dc); });
} }
@ -1409,8 +1454,10 @@ void Session_component::produce_xml(Xml_generator &xml)
return; return;
bool acquired_by_other_session = false; bool acquired_by_other_session = false;
_sessions.for_each([&] (Session_component &sc) { _sessions.apply(
if (sc.acquired(device) && &sc != this) [&] (Session_component &sc) {
return sc.acquired(device) && &sc != this; },
[&] (Session_component &) {
acquired_by_other_session = true; acquired_by_other_session = true;
}); });
if (acquired_by_other_session) if (acquired_by_other_session)
@ -1458,8 +1505,10 @@ Session_component::~Session_component()
{ {
_state = IN_DESTRUCTION; _state = IN_DESTRUCTION;
_device_sessions.for_each([&] (Device_component & dc) { _device_sessions.for_each([&] (Device_component & dc) {
_devices.for_each([&] (genode_usb_device & device) { _devices.apply(
if (device.label() == dc._device_label) [&] (genode_usb_device & device) {
return device.label() == dc._device_label; },
[&] (genode_usb_device & device) {
_release_fn(device.bus, device.dev); }); _release_fn(device.bus, device.dev); });
_release(dc); _release(dc);
@ -1507,8 +1556,11 @@ void ::Root::report()
_device_reporter->generate([&] (Reporter::Xml_generator &xml) { _device_reporter->generate([&] (Reporter::Xml_generator &xml) {
_devices.for_each([&] (genode_usb_device & d) { _devices.for_each([&] (genode_usb_device & d) {
bool acquired = false; bool acquired = false;
_sessions.for_each([&] (Session_component &sc) { _sessions.apply(
if (sc.acquired(d)) acquired = true; }); [&] (Session_component &sc) {
return sc.acquired(d); },
[&] (Session_component &) {
acquired = true; });
d.generate(xml, acquired); d.generate(xml, acquired);
}); });
}); });
@ -1627,23 +1679,23 @@ void ::Root::announce_device(genode_usb_bus_num_t bus,
void ::Root::discontinue_device(genode_usb_bus_num_t bus, void ::Root::discontinue_device(genode_usb_bus_num_t bus,
genode_usb_dev_num_t dev) genode_usb_dev_num_t dev)
{ {
_devices.for_each([&] (genode_usb_device & device) _devices.apply(
{ [&] (genode_usb_device & device) {
if (device.bus != bus || device.dev != dev) return device.bus == bus && device.dev == dev; },
return;
_sessions.for_each([&] (Session_component & sc) { [&] (genode_usb_device & device) {
sc.discontinue_device(device); }); _sessions.for_each([&] (Session_component & sc) {
sc.discontinue_device(device); });
device.configs.for_each([&] (genode_usb_configuration & cfg) { device.configs.for_each([&] (genode_usb_configuration & cfg) {
cfg.interfaces.for_each([&] (genode_usb_interface & iface) { cfg.interfaces.for_each([&] (genode_usb_interface & iface) {
iface.endpoints.for_each([&] (genode_usb_endpoint & endp) { iface.endpoints.for_each([&] (genode_usb_endpoint & endp) {
Genode::destroy(_heap, &endp); }); Genode::destroy(_heap, &endp); });
Genode::destroy(_heap, &iface); Genode::destroy(_heap, &iface);
});
Genode::destroy(_heap, &cfg);
}); });
Genode::destroy(_heap, &cfg); Genode::destroy(_heap, &device);
});
Genode::destroy(_heap, &device);
}); });
report(); report();
} }
@ -1652,10 +1704,15 @@ void ::Root::discontinue_device(genode_usb_bus_num_t bus,
bool ::Root::acquired(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev) bool ::Root::acquired(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev)
{ {
bool ret = false; bool ret = false;
_devices.for_each([&] (genode_usb_device & device) { _devices.apply(
if (device.bus == bus && device.dev == dev) [&] (genode_usb_device & device) {
_sessions.for_each([&] (Session_component & sc) { return device.bus == bus && device.dev == dev; },
if (sc.acquired(device)) ret = true; }); [&] (genode_usb_device & device) {
_sessions.apply(
[&] (Session_component & sc) {
return sc.acquired(device); },
[&] (Session_component &) {
ret = true; });
}); });
return ret; return ret;
} }
@ -1667,13 +1724,15 @@ bool ::Root::request(genode_usb_bus_num_t bus,
void *opaque_data) void *opaque_data)
{ {
bool ret = false; bool ret = false;
_devices.for_each([&] (genode_usb_device & device) _devices.apply(
{ [&] (genode_usb_device & device) {
if (device.bus != bus || device.dev != dev) return device.bus == bus && device.dev == dev; },
return;
_sessions.for_each([&] (Session_component & sc) { [&] (genode_usb_device & device) {
if (sc.request(device, callback, opaque_data)) ret = true; }); _sessions.apply(
[&] (Session_component & sc) {
return sc.request(device, callback, opaque_data); },
[&] (Session_component &) { ret = true; });
}); });
return ret; return ret;
} }
@ -1683,10 +1742,10 @@ void ::Root::handle_response(genode_usb_request_handle_t id,
genode_usb_request_ret_t ret, genode_usb_request_ret_t ret,
uint32_t *actual_sizes) uint32_t *actual_sizes)
{ {
bool handled = false; _sessions.apply(
_sessions.for_each([&] (Session_component & sc) { [&] (Session_component & sc) {
if (!handled) handled = sc.handle_response(id, ret, actual_sizes); return sc.handle_response(id, ret, actual_sizes); },
}); [&] (Session_component &) { });
} }