Skip to content

Instantly share code, notes, and snippets.

@lucasnetau
Last active May 12, 2023 23:49
Show Gist options
  • Save lucasnetau/244ba31f321307e06177f48582273e86 to your computer and use it in GitHub Desktop.
Save lucasnetau/244ba31f321307e06177f48582273e86 to your computer and use it in GitHub Desktop.
Curl double free reproduce
<?php declare(strict_types=1);
ini_set('memory_limit', 16777216);
class Fifo {
const MAX_SIZE = 10000000000;
static private array $buffers = [];
static private array $hasWriter = [];
/** @var string Mode in which the stream was opened */
private string $mode;
protected string $bufferKey;
/** @var resource|null Stream context (this is set by PHP) */
public $context;
function stream_open($path, $mode, $options, &$opened_path): bool
{
$url = parse_url($path);
$this->bufferKey = $url["host"];
try {
match ($mode) {
"w" => $this->startWriter(),
"r" => $this->startReader(),
};
} catch (UnhandledMatchError $ex) {
return false;
}
$this->mode = $mode;
return true;
}
protected function startWriter() {
self::$hasWriter[$this->bufferKey] = true;
}
protected function startReader() {}
function stream_read(int $count): false|string|null
{
if ($this->mode === 'r') {
self::$buffers[$this->bufferKey] ??= '';
$bytes = substr(self::$buffers[$this->bufferKey], 0, $count);
$written = strlen($bytes);
if ($written === 0) {
if ($this->stream_eof()) {
return '';
}
return false; //No data available
}
if (self::$buffers[$this->bufferKey][$written] ?? false) {
self::$buffers[$this->bufferKey] = substr(self::$buffers[$this->bufferKey], $written);
} else {
self::$buffers[$this->bufferKey] = '';
}
return $bytes;
}
return null;
}
function stream_write(string $data): int
{
if ($this->mode === 'w') {
self::$buffers[$this->bufferKey] ??= '';
$toWrite = strlen($data);
$bufferSize = strlen(self::$buffers[$this->bufferKey]);
if (($bufferSize + $toWrite) > self::MAX_SIZE) {
$data = substr($data, 0, (self::MAX_SIZE-$bufferSize));
}
self::$buffers[$this->bufferKey] .= $data;
return strlen($data);
}
return 0;
}
function stream_eof(): bool
{
if ($this->mode === 'w' || (self::$hasWriter[$this->bufferKey] ?? false)) {
return false;
}
return strlen(self::$buffers[$this->bufferKey] ?? []) === 0;
}
function stream_close(): void
{
if ($this->mode === 'w') {
self::$hasWriter[$this->bufferKey] = false;
} elseif (!(self::$hasWriter[$this->bufferKey] ?? false)) {
unset(self::$buffers[$this->bufferKey]);
}
}
}
stream_register_wrapper('fifo', Fifo::class, STREAM_IS_URL);
$rand = rand(100,999);
$writeStream = fopen('fifo://' . $rand, 'w', false,);
$readStream = fopen('fifo://' . $rand, 'r', false,);
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'http://httpbin.org/post',
CURLOPT_PUT => true,
CURLOPT_INFILE => $readStream,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_VERBOSE => true,
]);
curl_setopt($curl, CURLOPT_HTTPHEADER, ["content-length: 100000000","user-agent:TestClient/1",'Transfer-Encoding:']);
$multi = curl_multi_init();
curl_multi_add_handle($multi, $curl);
do {
curl_multi_exec($multi, $still_running);
if ($still_running) {
fwrite($writeStream, str_repeat('x', 500));
usleep(10);
}
} while ($still_running);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment