Files
spine/tests/fuzz/fuzz_icmp_reply.c
478d330985 fix(ping): make IPv4 and IPv6 ICMP polling safe (#619)
## Summary

Consolidates the overlapping ICMP work from #555 and #619 into one
review of the complete socket and resolver state machine.

- add ICMPv6 echo polling and dispatch IPv6 devices to it
- prefer unprivileged datagram ICMP sockets when the kernel permits them
- retain bounded raw-socket fallback for IPv4 and IPv6
- give packet and socket resources one cleanup owner
- leave caller-owned resolver state untouched on lookup failure
- keep every retry inside one cached capability and privilege-transition
decision
- prevent LOCK_SETEUID retry deadlocks
- validate raw and datagram reply layouts, source, sequence,
identifiers, and payload
- retain fuzz and unprivileged-socket integration coverage

The consolidation also fixed an integration defect found locally: the
controlled successful-reply test constructed only a raw IPv4 packet even
when the production path selected SOCK_DGRAM. It now models the selected
socket format, and IPv6 raw retries use the same privilege-safety
invariant as IPv4.

## Verification

- warning-enabled local build completed with MariaDB Connector/C,
Net-SNMP, OpenSSL 3, and cmocka
- make check: 5/5 test binaries passed
- focused ping_icmp suite: 9/9 cases passed
- actionlint and git diff --check passed
- all commits are signed
- GitHub CI is authoritative for Linux capability, sanitizer, fuzz, and
unprivileged-network coverage

Closes #127.
Closes #250.
Closes #593.
Closes #611.
Supersedes #555.
Refs #597 and #618.

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: TheWitness <copilot@users.noreply.github.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
2026-09-23 09:34:15 -04:00

51 lines
1.6 KiB
C

/* libFuzzer target for the two ICMP reply classifiers.
*
* This parses a raw ICMP datagram straight off a shared SOCK_RAW socket, so
* the bytes come from any host that can reach the poller. The raw classifier
* walks the IP header length field before touching the ICMP header, which is
* exactly the shape that goes wrong; the datagram classifier is handed a
* buffer with no IP header at all. Both are driven here, chosen by an input
* byte. The real ping.c is linked, not a copy.
*/
#include <stdint.h>
#include <string.h>
#include "common.h"
#include "spine.h"
#include "ping.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const struct icmp *pkt = NULL;
spine_icmp_reply_t verdict;
uint16_t want_id, want_seq;
int dgram;
/* first byte picks the socket type, next four steer the identity check,
* the rest is the datagram */
if (size < 5) return 0;
dgram = (data[0] & 1) != 0;
want_id = (uint16_t)((data[1] << 8) | data[2]);
want_seq = (uint16_t)((data[3] << 8) | data[4]);
data += 5;
size -= 5;
if (dgram) {
verdict = spine_icmp_classify_dgram_reply(data, (ssize_t) size, want_seq, &pkt);
} else {
verdict = spine_icmp_classify_reply(data, (ssize_t) size, want_id, want_seq, &pkt);
}
/* a packet is only handed back when it was accepted, and it must then
* point inside the buffer we were given */
if (verdict == SPINE_ICMP_REPLY_OK) {
if (pkt == NULL) abort();
if ((const uint8_t *) pkt < data) abort();
if ((const uint8_t *) pkt + ICMP_HDR_SIZE > data + size) abort();
} else if (pkt != NULL) {
abort();
}
return 0;
}