mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Parse unusual Subversion protocol frames which contain extra whitespace
Summary: Fixes T13140. See PHI660. Recent versions of Subversion can send a `(get-file true false false )` protocol frame with extra space between "false" and "false". This is allowed by the protocol spec, but never normally happens, and we do not parse it correctly. Instead, parse it correctly. Test Plan: - Added unit tests. - Ran `svn proplist svn+ssh://.../diffusion/X/file.c` under SVN 1.10 before and after the change. - Before: indefinite hang. - After: completed in finite time. Reviewers: amckinley, asherkin Reviewed By: amckinley, asherkin Maniphest Tasks: T13140 Differential Revision: https://secure.phabricator.com/D19451
This commit is contained in:
parent
7aa12f192a
commit
3544620209
2 changed files with 82 additions and 4 deletions
|
@ -33,7 +33,24 @@ final class DiffusionSubversionWireProtocol extends Phobject {
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
while (true) {
|
while (true) {
|
||||||
if ($this->state == 'item') {
|
if ($this->state == 'space') {
|
||||||
|
// Consume zero or more extra spaces after matching an item. The
|
||||||
|
// protocol requires at least one space, but allows more than one.
|
||||||
|
|
||||||
|
$matches = null;
|
||||||
|
if (!preg_match('/^(\s*)\S/', $this->buffer, $matches)) {
|
||||||
|
// Wait for more data.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have zero or more spaces and then some other character, so throw
|
||||||
|
// the spaces away and continue parsing frames.
|
||||||
|
if (strlen($matches[1])) {
|
||||||
|
$this->buffer = substr($this->buffer, strlen($matches[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state = 'item';
|
||||||
|
} else if ($this->state == 'item') {
|
||||||
$match = null;
|
$match = null;
|
||||||
$result = null;
|
$result = null;
|
||||||
$buf = $this->buffer;
|
$buf = $this->buffer;
|
||||||
|
@ -69,6 +86,12 @@ final class DiffusionSubversionWireProtocol extends Phobject {
|
||||||
);
|
);
|
||||||
$this->raw = '';
|
$this->raw = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Consume any extra whitespace after an item. If we're in the
|
||||||
|
// "bytes" state, we aren't looking for whitespace.
|
||||||
|
if ($this->state == 'item') {
|
||||||
|
$this->state = 'space';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No matches yet, wait for more data.
|
// No matches yet, wait for more data.
|
||||||
break;
|
break;
|
||||||
|
@ -90,7 +113,7 @@ final class DiffusionSubversionWireProtocol extends Phobject {
|
||||||
// Strip off the terminal space.
|
// Strip off the terminal space.
|
||||||
$this->pushItem(substr($this->byteBuffer, 0, -1), 'string');
|
$this->pushItem(substr($this->byteBuffer, 0, -1), 'string');
|
||||||
$this->byteBuffer = '';
|
$this->byteBuffer = '';
|
||||||
$this->state = 'item';
|
$this->state = 'space';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Exception(pht("Invalid state '%s'!", $this->state));
|
throw new Exception(pht("Invalid state '%s'!", $this->state));
|
||||||
|
|
|
@ -59,6 +59,50 @@ final class DiffusionSubversionWireProtocolTestCase
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// This is testing that multiple spaces are parsed correctly. See T13140
|
||||||
|
// for discussion.
|
||||||
|
$this->assertSameSubversionMessages(
|
||||||
|
'( get-file true false ) ',
|
||||||
|
// ^-- Note extra space!
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'type' => 'word',
|
||||||
|
'value' => 'get-file',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'type' => 'word',
|
||||||
|
'value' => 'true',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'type' => 'word',
|
||||||
|
'value' => 'false',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'( get-file true false ) ');
|
||||||
|
|
||||||
|
$this->assertSameSubversionMessages(
|
||||||
|
'( duck 5:quack moo ) ',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'type' => 'word',
|
||||||
|
'value' => 'duck',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'value' => 'quack',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'type' => 'word',
|
||||||
|
'value' => 'moo',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'( duck 5:quack moo ) ');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubversionWireProtocolPartialFrame() {
|
public function testSubversionWireProtocolPartialFrame() {
|
||||||
|
@ -86,7 +130,11 @@ final class DiffusionSubversionWireProtocolTestCase
|
||||||
ipull($msg2, 'structure'));
|
ipull($msg2, 'structure'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertSameSubversionMessages($string, array $structs) {
|
private function assertSameSubversionMessages(
|
||||||
|
$string,
|
||||||
|
array $structs,
|
||||||
|
$serial_string = null) {
|
||||||
|
|
||||||
$proto = new DiffusionSubversionWireProtocol();
|
$proto = new DiffusionSubversionWireProtocol();
|
||||||
|
|
||||||
// Verify that the wire message parses into the structs.
|
// Verify that the wire message parses into the structs.
|
||||||
|
@ -100,6 +148,13 @@ final class DiffusionSubversionWireProtocolTestCase
|
||||||
$serial[] = $proto->serializeStruct($struct);
|
$serial[] = $proto->serializeStruct($struct);
|
||||||
}
|
}
|
||||||
$serial = implode('', $serial);
|
$serial = implode('', $serial);
|
||||||
$this->assertEqual($string, $serial, 'serialize<'.$string.'>');
|
|
||||||
|
if ($serial_string === null) {
|
||||||
|
$expect_serial = $string;
|
||||||
|
} else {
|
||||||
|
$expect_serial = $serial_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual($expect_serial, $serial, 'serialize<'.$string.'>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue