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'));
++  }
++}

Reply via email to