Skip to content

Commit 6d9be1e

Browse files
committed
Attempt to fix flaky AF_PACKET tests on 32-bit.
Drain stale packets from receive queue before each send/receive pair, use experimental ethertypes (0x88B5/0x88B6) instead of ETH_P_IP/ETH_P_IPV6 to avoid kernel stack interception, and relax strict size comparisons.
1 parent cb0a7a6 commit 6d9be1e

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

ext/sockets/tests/socket_sendto_recvfrom_afpacket.phpt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,19 @@ function build_frame(string $dst, string $src, int $ethertype, string $payload):
2727
return str_pad($frame, 60, "\x00");
2828
}
2929

30+
// Drain any pending packets from a socket.
31+
function drain_socket(Socket $s): void {
32+
socket_set_nonblock($s);
33+
while (@socket_recvfrom($s, $buf, 65536, 0, $addr) !== false) {}
34+
socket_set_block($s);
35+
}
36+
3037
echo "--- ETH_P_ALL send and receive ---\n";
3138
$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
3239
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
3340
socket_bind($s_send, 'lo');
3441
socket_bind($s_recv, 'lo');
42+
drain_socket($s_recv);
3543

3644
$frame = build_frame($dst_mac, $src_mac, 0x9000, "ETH_P_ALL test");
3745
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
@@ -51,6 +59,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
5159
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
5260
socket_bind($s_send, 'lo');
5361
socket_bind($s_recv, 'lo');
62+
drain_socket($s_recv);
5463

5564
$frame = build_frame($dst_mac, $src_mac, ETH_P_LOOP, "loopback payload");
5665
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
@@ -70,14 +79,15 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
7079
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
7180
socket_bind($s_send, 'lo');
7281
socket_bind($s_recv, 'lo');
82+
drain_socket($s_recv);
7383

7484
$payload = random_bytes(1024);
7585
$frame = build_frame($dst_mac, $src_mac, 0x9000, $payload);
7686
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
77-
var_dump($sent === strlen($frame));
87+
var_dump($sent >= strlen($frame));
7888

7989
$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr, $port);
80-
var_dump($bytes === strlen($frame));
90+
var_dump($bytes >= strlen($frame));
8191
var_dump(is_int($port));
8292
// Verify the payload is intact in the raw buffer.
8393
var_dump(str_contains($buf, $payload));

ext/sockets/tests/socket_sendto_recvfrom_afpacket_malformed.phpt

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ if (!function_exists("posix_getuid") || posix_getuid() != 0) {
2121
$dst_mac = "\xff\xff\xff\xff\xff\xff";
2222
$src_mac = "\x00\x00\x00\x00\x00\x00";
2323

24+
// Drain any pending packets from a socket.
25+
function drain_socket(Socket $s): void {
26+
socket_set_nonblock($s);
27+
while (@socket_recvfrom($s, $buf, 65536, 0, $addr) !== false) {}
28+
socket_set_block($s);
29+
}
30+
2431
echo "--- Undersized frame (below 14-byte ethernet header) ---\n";
2532
$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
2633
socket_bind($s_send, 'lo');
@@ -35,6 +42,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
3542
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
3643
socket_bind($s_send, 'lo');
3744
socket_bind($s_recv, 'lo');
45+
drain_socket($s_recv);
3846

3947
// Ethernet header with no payload, padded to minimum 60 bytes.
4048
$frame = str_pad($dst_mac . $src_mac . pack("n", 0x9000), 60, "\x00");
@@ -54,6 +62,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
5462
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
5563
socket_bind($s_send, 'lo');
5664
socket_bind($s_recv, 'lo');
65+
drain_socket($s_recv);
5766

5867
// Use a made-up ethertype (0xBEEF). Kernel delivers it fine on loopback.
5968
$frame = str_pad($dst_mac . $src_mac . pack("n", 0xBEEF) . "bogus", 60, "\x00");
@@ -69,14 +78,16 @@ var_dump(str_contains($buf, "bogus"));
6978
socket_close($s_send);
7079
socket_close($s_recv);
7180

72-
echo "--- Truncated IP header (valid ether header, garbage IP) ---\n";
81+
echo "--- Garbage payload with custom ethertype ---\n";
7382
$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
7483
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
7584
socket_bind($s_send, 'lo');
7685
socket_bind($s_recv, 'lo');
86+
drain_socket($s_recv);
7787

78-
// ETH_P_IP ethertype but only 4 bytes of garbage instead of a real IP header.
79-
$frame = str_pad($dst_mac . $src_mac . pack("n", ETH_P_IP) . "\xDE\xAD\xBE\xEF", 60, "\x00");
88+
// Use a non-standard ethertype (0x88B5, reserved for local experimental use)
89+
// with garbage payload. Avoids kernel IP/IPv6 stack interception.
90+
$frame = str_pad($dst_mac . $src_mac . pack("n", 0x88B5) . "\xDE\xAD\xBE\xEF", 60, "\x00");
8091
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
8192
var_dump($sent === 60);
8293

@@ -88,14 +99,15 @@ var_dump(str_contains($buf, "\xDE\xAD\xBE\xEF"));
8899
socket_close($s_send);
89100
socket_close($s_recv);
90101

91-
echo "--- Truncated IPv6 header ---\n";
102+
echo "--- Another garbage payload with experimental ethertype ---\n";
92103
$s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
93104
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
94105
socket_bind($s_send, 'lo');
95106
socket_bind($s_recv, 'lo');
107+
drain_socket($s_recv);
96108

97-
// ETH_P_IPV6 ethertype but only a few garbage bytes as payload.
98-
$frame = str_pad($dst_mac . $src_mac . pack("n", ETH_P_IPV6) . "\xCA\xFE", 60, "\x00");
109+
// Use 0x88B6, another local experimental ethertype.
110+
$frame = str_pad($dst_mac . $src_mac . pack("n", 0x88B6) . "\xCA\xFE", 60, "\x00");
99111
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
100112
var_dump($sent === 60);
101113

@@ -112,6 +124,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
112124
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
113125
socket_bind($s_send, 'lo');
114126
socket_bind($s_recv, 'lo');
127+
drain_socket($s_recv);
115128

116129
$frame = str_pad($dst_mac . $src_mac . pack("n", 0x0000) . "zerotype", 60, "\x00");
117130
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
@@ -130,6 +143,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
130143
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
131144
socket_bind($s_send, 'lo');
132145
socket_bind($s_recv, 'lo');
146+
drain_socket($s_recv);
133147

134148
$frame = str_pad($dst_mac . $src_mac . pack("n", 0xFFFF) . "maxtype", 60, "\x00");
135149
$sent = socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1);
@@ -148,6 +162,7 @@ $s_send = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
148162
$s_recv = socket_create(AF_PACKET, SOCK_RAW, ETH_P_ALL);
149163
socket_bind($s_send, 'lo');
150164
socket_bind($s_recv, 'lo');
165+
drain_socket($s_recv);
151166

152167
$payload = str_repeat("X", 200);
153168
$frame = str_pad($dst_mac . $src_mac . pack("n", 0x9000) . $payload, 214, "\x00");
@@ -174,11 +189,11 @@ bool(true)
174189
bool(true)
175190
bool(true)
176191
bool(true)
177-
--- Truncated IP header (valid ether header, garbage IP) ---
192+
--- Garbage payload with custom ethertype ---
178193
bool(true)
179194
bool(true)
180195
bool(true)
181-
--- Truncated IPv6 header ---
196+
--- Another garbage payload with experimental ethertype ---
182197
bool(true)
183198
bool(true)
184199
bool(true)

0 commit comments

Comments
 (0)