This diff back ports the fix for CVE-2013-4450 to 0.8.18.
More info here: http://www.openwall.com/lists/oss-security/2013/10/19/4
(I will send the fix for 5.4 shortly in a different email)
OK?
Index: Makefile
===================================================================
RCS file: /cvs/ports/lang/node/Makefile,v
retrieving revision 1.15
diff -u -p -r1.15 Makefile
--- Makefile 27 Jan 2013 03:56:06 -0000 1.15
+++ Makefile 24 Oct 2013 17:30:41 -0000
@@ -9,6 +9,7 @@ ONLY_FOR_ARCHS= amd64 i386
COMMENT= V8 JavaScript for clients and servers
NODE_VERSION= v0.8.18
+REVISION= 0
DISTNAME= node-${NODE_VERSION}
PKGNAME= ${DISTNAME:S/v//g}
Index: patches/patch-lib_http_js
===================================================================
RCS file: patches/patch-lib_http_js
diff -N patches/patch-lib_http_js
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-lib_http_js 24 Oct 2013 17:30:41 -0000
@@ -0,0 +1,69 @@
+$OpenBSD$
+--- lib/http.js.orig Fri Jan 18 13:15:41 2013
++++ lib/http.js Thu Oct 24 11:28:58 2013
+@@ -150,7 +150,7 @@ function parserOnMessageComplete() {
+ }
+ }
+
+- if (parser.socket.readable) {
++ if (parser.socket.readable && !parser.socket._drain_paused) {
+ // force to read the next incoming message
+ parser.socket.resume();
+ }
+@@ -1783,6 +1783,7 @@ function connectionListener(socket) {
+ });
+
+ socket.ondata = function(d, start, end) {
++ assert(!socket._drain_paused);
+ var ret = parser.execute(d, start, end - start);
+ if (ret instanceof Error) {
+ debug('parse error');
+@@ -1809,6 +1810,12 @@ function connectionListener(socket) {
+ socket.destroy();
+ }
+ }
++
++ if (socket._drain_paused) {
++ // onIncoming paused the socket, we should pause the parser as well
++ debug('pause parser');
++ socket.parser.pause();
++ }
+ };
+
+ socket.onend = function() {
+@@ -1837,8 +1844,35 @@ function connectionListener(socket) {
+ // The following callback is issued after the headers have been read on a
+ // new message. In this callback we setup the response object and pass it
+ // to the user.
++
++ socket._drain_paused = false;
++ function socketOnDrain() {
++ // If we previously paused, then start reading again.
++ if (socket._drain_paused) {
++ socket._drain_paused = false;
++ socket.parser.resume();
++ socket.resume();
++ }
++ }
++ socket.on('drain', socketOnDrain);
++
+ parser.onIncoming = function(req, shouldKeepAlive) {
+ incoming.push(req);
++
++ // If the writable end isn't consuming, then stop reading
++ // so that we don't become overwhelmed by a flood of
++ // pipelined requests that may never be resolved.
++
++ if (!socket._drain_paused && socket._handle) {
++ var needPause = socket._handle.writeQueueSize > 0;
++ if (needPause) {
++ socket._drain_paused = true;
++ // We also need to pause the parser, but don't do that until after
++ // the call to execute, because we may still be processing the last
++ // chunk.
++ socket.pause();
++ }
++ }
+
+ var res = new ServerResponse(req);
+ debug('server response shouldKeepAlive: ' + shouldKeepAlive);
Index: patches/patch-test_simple_test-http-pipeline-flood_js
===================================================================
RCS file: patches/patch-test_simple_test-http-pipeline-flood_js
diff -N patches/patch-test_simple_test-http-pipeline-flood_js
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-test_simple_test-http-pipeline-flood_js 24 Oct 2013
17:30:41 -0000
@@ -0,0 +1,119 @@
+$OpenBSD$
+--- test/simple/test-http-pipeline-flood.js.orig Thu Oct 24 11:28:58 2013
++++ test/simple/test-http-pipeline-flood.js Thu Oct 24 11:28:58 2013
+@@ -0,0 +1,115 @@
++// Copyright Joyent, Inc. and other Node contributors.
++//
++// Permission is hereby granted, free of charge, to any person obtaining a
++// copy of this software and associated documentation files (the
++// "Software"), to deal in the Software without restriction, including
++// without limitation the rights to use, copy, modify, merge, publish,
++// distribute, sublicense, and/or sell copies of the Software, and to permit
++// persons to whom the Software is furnished to do so, subject to the
++// following conditions:
++//
++// The above copyright notice and this permission notice shall be included
++// in all copies or substantial portions of the Software.
++//
++// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
++// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
++// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++// USE OR OTHER DEALINGS IN THE SOFTWARE.
++
++var common = require('../common');
++var assert = require('assert');
++
++switch (process.argv[2]) {
++ case undefined:
++ return parent();
++ case 'child':
++ return child();
++ default:
++ throw new Error('wtf');
++}
++
++function parent() {
++ var http = require('http');
++ var bigResponse = new Buffer(10240)
++ bigResponse.fill('x');
++ var gotTimeout = false;
++ var childClosed = false;
++ var requests = 0;
++ var connections = 0;
++
++ var server = http.createServer(function(req, res) {
++ requests++;
++ res.setHeader('content-length', bigResponse.length);
++ res.end(bigResponse);
++ });
++
++ server.on('connection', function(conn) {
++ connections++;
++ // kill the connection after a bit, verifying that the
++ // flood of requests was eventually halted.
++ console.log('got connection');
++ setTimeout(function() {
++ gotTimeout = true;
++ conn.destroy();
++ }, 200);
++ });
++
++
++ server.listen(common.PORT, function() {
++ var spawn = require('child_process').spawn;
++ var args = [__filename, 'child'];
++ var child = spawn(process.execPath, args, { stdio: 'inherit' });
++ child.on('exit', function(code) {
++ assert(!code);
++ childClosed = true;
++ server.close();
++ });
++ });
++
++ process.on('exit', function() {
++ assert(gotTimeout);
++ assert(childClosed);
++ assert.equal(connections, 1);
++ // 1213 works out to be the number of requests we end up processing
++ // before the outgoing connection backs up and requires a drain.
++ // however, to avoid being unnecessarily tied to a specific magic number,
++ // and making the test brittle, just assert that it's "a lot", which we
++ // can safely assume is more than 500.
++ assert(requests >= 500);
++ console.log('ok');
++ });
++}
++
++function child() {
++ var net = require('net');
++
++ var gotEpipe = false;
++ var conn = net.connect({ port: common.PORT });
++
++ var req = 'GET / HTTP/1.1\r\nHost: localhost:' +
++ common.PORT + '\r\nAccept: */*\r\n\r\n';
++
++ req = new Array(10241).join(req);
++
++ conn.on('connect', function() {
++ write();
++ });
++
++ conn.on('drain', write);
++
++ conn.on('error', function(er) {
++ gotEpipe = true;
++ });
++
++ process.on('exit', function() {
++ assert(gotEpipe);
++ console.log('ok - child');
++ });
++
++ function write() {
++ while (false !== conn.write(req, 'ascii'));
++ }
++}