zlib: fix assert on bad input
authorBen Noordhuis <info@bnoordhuis.nl>
Thu, 21 Feb 2013 22:58:55 +0000 (23:58 +0100)
committerBen Noordhuis <info@bnoordhuis.nl>
Thu, 21 Feb 2013 23:08:26 +0000 (00:08 +0100)
The following test case occasionally triggered an assert because
write_in_progress_ didn't get cleared on error:

  $ cat test.js
  require('zlib').gunzip('BAM', console.log);
  setTimeout(gc, 10);

  $ while true; do node --expose-gc test.js || break; done
  { [Error: incorrect header check] errno: -3, code: 'Z_DATA_ERROR' }
  Assertion failed: (!write_in_progress_ && "write in progress"),
  function Clear, file ../src/node_zlib.cc, line 71.
  Abort trap: 6

Steps to avoid that:

* Initialize all primitive member fields in the constructor.
* Clear the write_in_progress_ member field in ZCtx::Error().
* Ref the ZCtx object as soon as write_in_progress_ is set to true.
  Before this commit, it could get GC'ed in the time between setting
  the field and the call to ctx->Ref().

Fixes #4783.

src/node_zlib.cc

index 4a60c87..7c7f966 100644 (file)
@@ -59,7 +59,22 @@ void InitZlib(v8::Handle<v8::Object> target);
 class ZCtx : public ObjectWrap {
  public:
 
-  ZCtx(node_zlib_mode mode) : ObjectWrap(), dictionary_(NULL), mode_(mode) {}
+  ZCtx(node_zlib_mode mode)
+    : ObjectWrap()
+    , init_done_(false)
+    , level_(0)
+    , windowBits_(0)
+    , memLevel_(0)
+    , strategy_(0)
+    , err_(0)
+    , dictionary_(NULL)
+    , dictionary_len_(0)
+    , flush_(0)
+    , chunk_size_(0)
+    , write_in_progress_(false)
+    , mode_(mode)
+  {
+  }
 
 
   ~ZCtx() {
@@ -108,6 +123,7 @@ class ZCtx : public ObjectWrap {
 
     assert(!ctx->write_in_progress_ && "write already in progress");
     ctx->write_in_progress_ = true;
+    ctx->Ref();
 
     unsigned int flush = args[0]->Uint32Value();
     Bytef *in;
@@ -155,8 +171,6 @@ class ZCtx : public ObjectWrap {
                   ZCtx::Process,
                   ZCtx::After);
 
-    ctx->Ref();
-
     return ctx->handle_;
   }
 
@@ -269,6 +283,7 @@ class ZCtx : public ObjectWrap {
     MakeCallback(ctx->handle_, onerror_sym, ARRAY_SIZE(args), args);
 
     // no hope of rescue.
+    ctx->write_in_progress_ = false;
     ctx->Unref();
   }