summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2012-09-14 01:59:44 +0200
committerBen Noordhuis <info@bnoordhuis.nl>2012-09-14 02:37:51 +0200
commit07804c7c9a4c2eb50eb897fa5e41cb8c9e5a1ab5 (patch)
treebf247a70af1333cdccc7c7906ba7836d61e23536
parent1d52968d1dbd04053356d62dc0804bcc68deed8a (diff)
downloadnode-new-07804c7c9a4c2eb50eb897fa5e41cb8c9e5a1ab5.tar.gz
fs: don't segfault on deeply recursive stat()
Check that the calls to Integer::New() and Date::New() succeed and bail out if they don't. V8 returns an empty handle on stack overflow. Trying to set the empty handle as a property on an object results in a NULL pointer dereference in release builds and an assert in debug builds. Fixes #4015.
-rw-r--r--src/node_file.cc83
-rw-r--r--test/fixtures/test-regress-GH-4015.js7
-rw-r--r--test/simple/test-regress-GH-4015.js33
3 files changed, 83 insertions, 40 deletions
diff --git a/src/node_file.cc b/src/node_file.cc
index 7e8badf5c2..89a786b17e 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -301,46 +301,49 @@ Local<Object> BuildStatsObject(const uv_statbuf_t* s) {
Local<Object> stats =
stats_constructor_template->GetFunction()->NewInstance();
- /* ID of device containing file */
- stats->Set(dev_symbol, Integer::New(s->st_dev));
-
- /* inode number */
- stats->Set(ino_symbol, Integer::New(s->st_ino));
-
- /* protection */
- stats->Set(mode_symbol, Integer::New(s->st_mode));
-
- /* number of hard links */
- stats->Set(nlink_symbol, Integer::New(s->st_nlink));
-
- /* user ID of owner */
- stats->Set(uid_symbol, Integer::New(s->st_uid));
-
- /* group ID of owner */
- stats->Set(gid_symbol, Integer::New(s->st_gid));
-
- /* device ID (if special file) */
- stats->Set(rdev_symbol, Integer::New(s->st_rdev));
-
- /* total size, in bytes */
- stats->Set(size_symbol, Number::New(s->st_size));
-
-#ifdef __POSIX__
- /* blocksize for filesystem I/O */
- stats->Set(blksize_symbol, Integer::New(s->st_blksize));
-
- /* number of blocks allocated */
- stats->Set(blocks_symbol, Integer::New(s->st_blocks));
-#endif
-
- /* time of last access */
- stats->Set(atime_symbol, NODE_UNIXTIME_V8(s->st_atime));
-
- /* time of last modification */
- stats->Set(mtime_symbol, NODE_UNIXTIME_V8(s->st_mtime));
-
- /* time of last status change */
- stats->Set(ctime_symbol, NODE_UNIXTIME_V8(s->st_ctime));
+ if (stats.IsEmpty()) return Local<Object>();
+
+ // The code below is very nasty-looking but it prevents a segmentation fault
+ // when people run JS code like the snippet below. It's apparently more
+ // common than you would expect, several people have reported this crash...
+ //
+ // function crash() {
+ // fs.statSync('.');
+ // crash();
+ // }
+ //
+ // We need to check the return value of Integer::New() and Date::New()
+ // and make sure that we bail out when V8 returns an empty handle.
+#define X(name) \
+ { \
+ Local<Value> val = Integer::New(s->st_##name); \
+ if (val.IsEmpty()) return Local<Object>(); \
+ stats->Set(name##_symbol, val); \
+ }
+ X(dev)
+ X(ino)
+ X(mode)
+ X(nlink)
+ X(uid)
+ X(gid)
+ X(rdev)
+ X(size)
+# if defined(__POSIX__)
+ X(blksize)
+ X(blocks)
+# endif
+#undef X
+
+#define X(name) \
+ { \
+ Local<Value> val = NODE_UNIXTIME_V8(s->st_##name); \
+ if (val.IsEmpty()) return Local<Object>(); \
+ stats->Set(name##_symbol, val); \
+ }
+ X(atime)
+ X(mtime)
+ X(ctime)
+#undef X
return scope.Close(stats);
}
diff --git a/test/fixtures/test-regress-GH-4015.js b/test/fixtures/test-regress-GH-4015.js
new file mode 100644
index 0000000000..0d9c05d6d7
--- /dev/null
+++ b/test/fixtures/test-regress-GH-4015.js
@@ -0,0 +1,7 @@
+var fs = require('fs');
+
+function load() {
+ fs.statSync('.');
+ load();
+}
+load();
diff --git a/test/simple/test-regress-GH-4015.js b/test/simple/test-regress-GH-4015.js
new file mode 100644
index 0000000000..b66584f804
--- /dev/null
+++ b/test/simple/test-regress-GH-4015.js
@@ -0,0 +1,33 @@
+// 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');
+var exec = require('child_process').exec;
+
+var cmd = process.execPath
+ + ' '
+ + common.fixturesDir
+ + '/test-regress-GH-4015.js';
+
+exec(cmd, function(err, stdout, stderr) {
+ assert(/RangeError: Maximum call stack size exceeded/.test(stderr));
+});