Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

buffer: backport --zero-fill-buffers command line option #5748

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/api/buffer.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ While more efficient, it introduces subtle incompatibilities with the typed
arrays specification. `ArrayBuffer#slice()` makes a copy of the slice while
`Buffer#slice()` creates a view.

## The `--zero-fill-buffers` command line option

Node.js can be started using the `--zero-fill-buffers` command line option to
force all newly allocated `Buffer` and `SlowBuffer` instances created using
either `new Buffer(size)` and `new SlowBuffer(size)` to be *automatically
zero-filled* upon creation. Use of this flag *changes the default behavior* of
these methods and *can have a significant impact* on performance. Use of the
`--zero-fill-buffers` option is recommended only when absolutely necessary to
enforce that newly allocated `Buffer` instances cannot contain potentially
sensitive data.

```
$ node --zero-fill-buffers
> Buffer(5);
<Buffer 00 00 00 00 00>
```

## Class: Buffer

The Buffer class is a global type for dealing with binary data directly.
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ and servers.

--throw-deprecation throw errors on deprecations

--zero-fill-buffers Automatically zero-fills all newly allocated Buffer
and SlowBuffer instances.

--v8-options print v8 command line options

--max-stack-size=val set max v8 stack size (bytes)
Expand Down
6 changes: 6 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ void* ArrayBufferAllocator::Allocate(size_t length) {


void* ArrayBufferAllocator::AllocateUninitialized(size_t length) {
if (zero_fill_all_buffers)
return ArrayBufferAllocator::Allocate(length);
if (length > kMaxLength)
return NULL;
return new char[length];
Expand Down Expand Up @@ -3003,6 +3005,8 @@ static void PrintHelp() {
"function is used\n"
" --trace-deprecation show stack traces on deprecations\n"
" --v8-options print v8 command line options\n"
" --zero-fill-buffers automatically zero-fill all newly allocated\n"
" Buffer and SlowBuffer instances\n"
" --max-stack-size=val set max v8 stack size (bytes)\n"
#if defined(NODE_HAVE_I18N_SUPPORT)
" --icu-data-dir=dir set ICU data load path to dir\n"
Expand Down Expand Up @@ -3137,6 +3141,8 @@ static void ParseArgs(int* argc,
} else if (strcmp(arg, "--v8-options") == 0) {
new_v8_argv[new_v8_argc] = "--help";
new_v8_argc += 1;
} else if (strcmp(arg, "--zero-fill-buffers") == 0) {
zero_fill_all_buffers = true;
#if defined(NODE_HAVE_I18N_SUPPORT)
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
icu_data_dir = arg + 15;
Expand Down
2 changes: 1 addition & 1 deletion src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Local<Object> New(Environment* env, size_t length) {
// approach if v8 provided one.
char* data;
if (length > 0) {
data = static_cast<char*>(malloc(length));
data = static_cast<char*>(BUFFER_MALLOC(length));
if (data == NULL)
FatalError("node::Buffer::New(size_t)", "Out Of Memory");
} else {
Expand Down
6 changes: 5 additions & 1 deletion src/smalloc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
#define ALLOC_ID (0xA10C)

namespace node {

// if true, all Buffer and SlowBuffer instances will automatically zero-fill
bool zero_fill_all_buffers = false;

namespace smalloc {

using v8::Context;
Expand Down Expand Up @@ -329,7 +333,7 @@ void Alloc(Environment* env,
if (length == 0)
return Alloc(env, obj, NULL, length, type);

char* data = static_cast<char*>(malloc(length));
char* data = static_cast<char*>(BUFFER_MALLOC(length));
if (data == NULL) {
FatalError("node::smalloc::Alloc(v8::Handle<v8::Object>, size_t,"
" v8::ExternalArrayType)", "Out Of Memory");
Expand Down
4 changes: 4 additions & 0 deletions src/smalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

namespace node {

#define BUFFER_MALLOC(length) \
zero_fill_all_buffers ? calloc(length, 1) : malloc(length)
extern bool zero_fill_all_buffers;

// Forward declaration
class Environment;

Expand Down
29 changes: 29 additions & 0 deletions test/simple/test-buffer-zero-fill-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Flags: --zero-fill-buffers

// when using --zero-fill-buffers, every Buffer and SlowBuffer
// instance must be zero filled upon creation

require('../common');
var SlowBuffer = require('buffer').SlowBuffer;
var assert = require('assert');

function isZeroFilled(buf) {
for (var n = 0; n < buf.length; n++)
if (buf[n] > 0) return false;
return true;
}

// This can be somewhat unreliable because the
// allocated memory might just already happen to
// contain all zeroes. The test is run multiple
// times to improve the reliability.
for (var i = 0; i < 50; i++) {
var bufs = [
SlowBuffer(20),
Buffer(20),
new SlowBuffer(20)
];
for (var buf of bufs) {
assert(isZeroFilled(buf));
}
}