Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Bug demonstration script for PHP/libevent 0.0.4 Segfault.
<?php
/**
* Run this script as-is and it will segfault. My setup is Ubuntu
* 11.10, library details:
*
* php5-cli : 5.3.6-13ubuntu3.6 (output from dpkg -l)
* libevent : 0.0.5 beta (output from pecl list)
* php-pear : 5.3.6-13ubuntu3.6 (output from dpkg -l)
*
* There are 2 things you can do to stop the segfault:
*
* 1. Comment out the given line in ELoop->checkAddEvent()
* 2. Only add a single client to the loop
*/
class ELoop
{
private $evBase;
private $clients = array();
function addClient ($client) {
$this->clients[] = $client;
}
function loop () {
$this->initLibevent();
$hasWork = false;
foreach ($this->clients as $c) {
if ($ev = $this->checkAddEvent($c)) {
$hasWork = true;
}
}
if ($hasWork) {
event_base_loop($this->evBase);
}
}
private function checkAddEvent ($c, $ev=null) {
$flags = 0;
if ($c->canRead())
$flags = $flags | EV_READ;
if ($c->canWrite())
$flags = $flags | EV_WRITE;
if (! $flags) {
if ($ev) {
event_del($ev);
event_free($ev); // Comment this line and the segfault doesn't happen
}
if (false === ($i = array_search($c, $this->clients))) {
throw new \Exception("Failed to locate client within local collection during removal", 9062);
} else {
unset($this->clients[$i]);
}
return;
}
if (! $ev)
$ev = event_new();
event_set($ev, $c->getSocketHandle(), $flags, array($this, 'loopEventDispatch'), array($c, $ev));
event_base_set($ev, $this->evBase);
event_add($ev);
return $ev;
}
function loopEventDispatch ($fd, $events, $params) {
list($client, $event) = $params;
if ($events & EV_READ)
$client->read();
if ($events & EV_WRITE)
$client->write();
$this->checkAddEvent($client, $event);
}
private function initLibevent () {
$this->evBase = event_base_new();
}
}
class SocketClient
{
private $host, $port, $sock;
function open ($host, $port) {
$this->host = $host;
$this->port = $port;
if (! ($this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) {
throw new \Exception("Failed to create inet socket", 7895);
} else if (! socket_connect($this->sock, $this->host, $this->port)) {
throw new \Exception("Failed to connect inet socket ({$this->host}, {$this->port})", 7564);
} else if (! socket_set_nonblock($this->sock)) {
throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357);
}
}
function getSocketHandle () {
return $this->sock;
}
function read () {
$buff = '';
while (@socket_recv($this->sock, $tmp, 4096, MSG_DONTWAIT)) {
$buff .= $tmp;
}
return $buff;
}
function write ($buff='') {
if (($tmp = socket_write($this->sock, $buff)) === false) {
throw new \Exception(sprintf("Socket write failed: %s",
socket_strerror(socket_last_error($this->sock))), 7854);
}
}
}
class HttpClient extends SocketClient
{
private $state = 0, $domain;
function __construct ($domain) {
$ip = gethostbyname($domain);
if ($ip == $domain) {
throw new \Exception("Failed to resolve HTTP host", 2351);
}
$this->open($ip, 80);
$this->domain = $domain;
}
function canRead () {
switch ($this->state) {
case 1:
return true;
default:
return false;
}
}
function canWrite () {
switch ($this->state) {
case 0:
return true;
default:
return false;
}
}
function read () {
$this->state = 2;
$buff = parent::read();
printf("[http] Reads response (%d):\n%s\n", strlen($buff), substr($buff, 0, strpos($buff, "\r\n")));
}
function write ($buff='') {
if ($this->state == 0) {
// Write HTTP header.
parent::write("GET / HTTP/1.1\r\nHost: {$this->domain}\r\n" .
"Connection: close\r\n" .
"User-Agent: php libevent test\r\n" .
"Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7\r\n" .
"Cache-Control: no-cache\r\n" .
"Accept-Language: de,en;q=0.7,en-us;q=0.3\r\n\r\n");
$this->state = 1;
}
}
}
$el = new ELoop;
$el->addClient(new HttpClient('microsoft.com'));
$el->addClient(new HttpClient('bluelines.org'));
$el->loop();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.