networkd/resolved hardening fixes flagged by kres (#42736)
This commit is contained in:
@@ -672,6 +672,13 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
|
||||
FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
|
||||
FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
|
||||
|
||||
/* RFC 8415 defines the prefix length as 1…128; reject the out-of-range values the sender-side
|
||||
* counterpart option_append_pd_prefix() also refuses. */
|
||||
if (a->iapdprefix.prefixlen == 0 || a->iapdprefix.prefixlen > 128)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received a PD prefix with invalid prefix length %u, ignoring.",
|
||||
a->iapdprefix.prefixlen);
|
||||
|
||||
if (len > sizeof(struct iapdprefix)) {
|
||||
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
|
||||
if (r < 0)
|
||||
|
||||
@@ -199,6 +199,9 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
|
||||
sd_lldp_rx *lldp_rx = ASSERT_PTR(userdata);
|
||||
struct timespec ts;
|
||||
|
||||
/* Keep ref in case the callback drops the last reference, so we can use it below */
|
||||
_unused_ _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *ref = sd_lldp_rx_ref(lldp_rx);
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
space = next_datagram_size_fd(fd);
|
||||
@@ -421,6 +424,9 @@ static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_lldp_rx *lldp_rx = userdata;
|
||||
int r;
|
||||
|
||||
/* Keep ref in case the callback drops the last reference, so we can use it below */
|
||||
_unused_ _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *ref = sd_lldp_rx_ref(lldp_rx);
|
||||
|
||||
r = lldp_rx_make_space(lldp_rx, 0);
|
||||
if (r < 0) {
|
||||
log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
|
||||
|
||||
@@ -324,6 +324,17 @@ TEST(option_status) {
|
||||
/* PD prefix status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
};
|
||||
static const uint8_t option6[] = {
|
||||
/* IA PD */
|
||||
0x00, 0x19, 0x00, 0x29, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* IA PD Prefix, with an invalid prefix length of 0 */
|
||||
0x00, 0x1a, 0x00, 0x19,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
};
|
||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
||||
DHCP6Option *option;
|
||||
be32_t iaid;
|
||||
@@ -379,6 +390,14 @@ TEST(option_status) {
|
||||
assert_se(ia);
|
||||
assert_se(ia->addresses);
|
||||
ia = dhcp6_ia_free(ia);
|
||||
|
||||
/* An IA_PD whose only prefix carries an invalid (zero) prefix length must be refused, leaving no
|
||||
* valid prefix behind. */
|
||||
option = (DHCP6Option*) option6;
|
||||
assert_se(sizeof(option6) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -ENODATA);
|
||||
assert_se(!ia);
|
||||
}
|
||||
|
||||
TEST(client_parse_message_issue_22099) {
|
||||
|
||||
@@ -417,6 +417,44 @@ static void test_receive_oui_vlanid_packet(sd_event *e) {
|
||||
ASSERT_OK(stop_lldp_rx(lldp_rx));
|
||||
}
|
||||
|
||||
static int reentrant_unref_calls;
|
||||
|
||||
static void lldp_rx_reentrant_unref_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) {
|
||||
/* Drop the last external reference from inside the callback. The event-source callback holds a
|
||||
* transient self-ref, so lldp_rx must survive until the dispatch stack unwinds. */
|
||||
reentrant_unref_calls++;
|
||||
sd_lldp_rx_unref(lldp_rx);
|
||||
}
|
||||
|
||||
static void test_receive_reentrant_unref(sd_event *e) {
|
||||
/* Regression: a user callback dropping the last lldp_rx reference used to free the object while
|
||||
* inner library frames still dereferenced it (use-after-free). The event-source callbacks now take
|
||||
* a transient self-ref, so teardown is deferred until the dispatch stack unwinds. */
|
||||
static const uint8_t frame[] = {
|
||||
/* Ethernet header */
|
||||
0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
|
||||
0x88, 0xcc, /* Ethertype */
|
||||
/* LLDP mandatory TLVs */
|
||||
0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
|
||||
0x03, 0x04, 0x05,
|
||||
0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
|
||||
0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
|
||||
0x00, 0x00 /* End Of LLDPDU */
|
||||
};
|
||||
sd_lldp_rx *lldp_rx;
|
||||
|
||||
reentrant_unref_calls = 0;
|
||||
assert_se(start_lldp_rx(&lldp_rx, e, lldp_rx_reentrant_unref_handler, NULL) == 0);
|
||||
|
||||
assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
|
||||
sd_event_run(e, 0);
|
||||
assert_se(reentrant_unref_calls == 1);
|
||||
|
||||
/* lldp_rx was freed by the callback's unref once the dispatch stack unwound; do not touch it. */
|
||||
safe_close(test_fd[1]);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||
|
||||
@@ -429,6 +467,7 @@ int main(int argc, char *argv[]) {
|
||||
test_receive_oui_packet(e);
|
||||
test_multiple_neighbors_sorted(e);
|
||||
test_receive_oui_vlanid_packet(e);
|
||||
test_receive_reentrant_unref(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2055,10 +2055,6 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
|
||||
if (r == 0)
|
||||
return 1; /* Polkit will call us back */
|
||||
|
||||
r = hashmap_ensure_put(&m->dnssd_registered_services, &string_hash_ops, service->id, service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_track_new(sd_bus_message_get_bus(message), &bus_track, dnssd_registered_service_on_bus_track, service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -2069,6 +2065,10 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
|
||||
|
||||
service->manager = m;
|
||||
|
||||
r = hashmap_ensure_put(&m->dnssd_registered_services, &string_hash_ops, service->id, service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
service = NULL;
|
||||
|
||||
manager_refresh_rrs(m);
|
||||
|
||||
@@ -102,6 +102,19 @@ DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p) {
|
||||
p->udp_event_source = sd_event_source_disable_unref(p->udp_event_source);
|
||||
p->tcp_event_source = sd_event_source_disable_unref(p->tcp_event_source);
|
||||
|
||||
assert(p->manager);
|
||||
|
||||
/* Detach the raw back-pointers that DnsStream and DnsQuery objects keep to this listener before we
|
||||
* free it. Otherwise a later dns_stream_complete()/dns_query_free() during the same reload would
|
||||
* dereference the freed slab: dns_query_free() reads q->stub_listener_extra->queries_by_packet. */
|
||||
LIST_FOREACH(streams, s, p->manager->dns_streams)
|
||||
if (s->stub_listener_extra == p)
|
||||
s->stub_listener_extra = NULL;
|
||||
|
||||
LIST_FOREACH(queries, q, p->manager->dns_queries)
|
||||
if (q->stub_listener_extra == p)
|
||||
q->stub_listener_extra = NULL;
|
||||
|
||||
hashmap_free(p->queries_by_packet);
|
||||
|
||||
return mfree(p);
|
||||
|
||||
Reference in New Issue
Block a user