Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
cbbe95e
net: introduce `Socket#connecting` property
indutny Apr 26, 2016
19fb134
doc: use Buffer.from() instead of new Buffer()
JacksonTian Apr 25, 2016
36207c6
doc: correctly document the behavior of ee.once().
lance Apr 25, 2016
11e7cc5
build: don't compile with -B
bnoordhuis Apr 26, 2016
7b9ae70
benchmark: Fix crash in net benchmarks
Apr 27, 2016
8956432
build: exclude tap files from tarballs
mscdex Apr 22, 2016
f6d7279
doc: update build instructions for OS X
Trott Apr 20, 2016
fdde369
crypto: fix error in deprecation message
Trott Apr 22, 2016
6d1606e
test,tools: adjust function argument alignment
Trott Apr 26, 2016
3f608b1
tools: lint for function argument alignment
Trott Apr 19, 2016
66903f6
tools: add tests for the doctool
iankronquist Apr 3, 2016
089362f
test,tools: limit lint tolerance of gc global
Trott Apr 21, 2016
7153f96
build: unbreak -prof, disable PIE on OS X
bnoordhuis Apr 28, 2016
e62c42b
tools: remove default parameters from lint rule
Trott Apr 27, 2016
fa9d82d
src: unify implementations of Utf8Value etc.
addaleax Apr 23, 2016
ab84d69
doc: better example & synopsis
Fishrock123 Apr 12, 2016
150dd36
doc: no Node.js(1)
Fishrock123 Apr 12, 2016
fc0fbf1
doc: subdivide TOC, add auxiliary links
Fishrock123 Apr 12, 2016
9f23cb2
tools: fix exit code when linting from CI
mscdex Apr 27, 2016
2c92a1f
events: pass the original listener added by once
davidtaikocha Apr 29, 2016
1b9fdba
process: add process.cpuUsage() - implementation, doc, tests
Apr 5, 2016
b3f75ec
doc: expand documentation for process.exit()
jasnell Apr 27, 2016
76c9ab5
assert: allow circular references
Trott Apr 27, 2016
fd37d54
doc: added note warning about change to console.endTime()
ben-page Apr 28, 2016
8eb87ee
doc: Remove extra space in REPL example
Apr 28, 2016
bfcde97
doc: fix incorrect syntax in examples
evanlucas Apr 29, 2016
4068d64
doc: fix EventEmitter#eventNames() example
ChALkeR Apr 27, 2016
0d19853
build: use shorthand lint target from test
jbergstroem Apr 27, 2016
e6c8da4
doc: make `writable.setDefaultEncoding()` return `this`
estliberitas Apr 30, 2016
4ae39f9
doc: remove all scrollbar styling
claudiorodriguez Apr 29, 2016
7034ebe
doc: use `Node.js` in synopsis document
Trott Apr 29, 2016
3d5b732
doc: clarifies http.serverResponse implementation
AllenSH12 Apr 6, 2016
13e6608
doc: extra clarification of historySize option
vsemozhetbyt Apr 26, 2016
ab7055b
doc: fix fs.realpath man pg links
phette23 Apr 28, 2016
969f96a
doc: fix a typo in __dirname section
toyota790 Apr 29, 2016
3e028a1
doc: remove obsolete comment in isError() example
cjihrig Apr 30, 2016
e5ce53a
doc: fix sentence fragment in fs doc
Trott Apr 30, 2016
457d12a
test: add failing url parse tests as known_issue
jasnell Mar 24, 2016
5fe5fa2
test: make addon testing part of `make test`
bnoordhuis Apr 15, 2016
2d503b1
debugger: display array contents in repl
cjihrig Apr 28, 2016
7641f9a
test: refactor large event emitter tests
cjihrig Apr 28, 2016
7c30f15
doc: ensure consistent grammar in node.1 file
justshiv Apr 27, 2016
08e0884
tools: add -F flag for fixing lint issues
Trott Apr 29, 2016
1330496
cluster: remove use of bind() in destroy()
yorkie May 1, 2016
d674493
doc: add missing underscore for markdown italics
May 2, 2016
0bca959
util: fix inspecting of proxy objects
jasnell Apr 29, 2016
9a29b50
doc: fix the lint of an example in cluster.md
yorkie May 2, 2016
4f728df
doc: linkify remaining references to fs.Stats object
Apr 30, 2016
33f24c8
doc: adds 'close' events to fs.ReadStream and fs.WriteStream
May 1, 2016
91ab769
util: truncate inspect array and typed array
jasnell Apr 21, 2016
65b6574
deps: backport IsValid changes from 4e8736d in V8
targos May 3, 2016
e6ab3ec
deps: upgrade openssl sources to 1.0.2h
May 3, 2016
3af28d3
deps: copy all openssl header files to include dir
May 3, 2016
d159462
deps: fix openssl assembly error on ia32 win32
indutny Jan 8, 2014
3fe6812
deps: fix asm build error of openssl in x86_win32
Feb 13, 2015
534f03c
openssl: fix keypress requirement in apps on win32
Feb 17, 2015
a5a2944
deps: add -no_rand_screen to openssl s_client
May 27, 2015
54f8600
deps: update openssl asm and asm_obsolete files
May 3, 2016
65030c7
test: fix alpn tests for openssl1.0.2h
May 3, 2016
cd7b76e
2016-05-05, Version 6.1.0 (Current)
Fishrock123 May 5, 2016
8aa8f18
Working on v6.1.1
Fishrock123 May 5, 2016
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
Prev Previous commit
Next Next commit
src: unify implementations of Utf8Value etc.
Unify the common code of `Utf8Value`, `TwoByteValue`, `BufferValue`
and `StringBytes::InlineDecoder` into one class. Always make the
result zero-terminated for the first three.

This fixes two problems in passing:

* When the conversion of the input value to String fails,
  make the buffer zero-terminated anyway. Previously, this would
  have resulted in possibly reading uninitialized data in multiple
  places in the code. An instance of that problem can be reproduced
  by running e.g.
  `valgrind node -e 'net.isIP({ toString() { throw Error() } })'`.
* Previously, `BufferValue` copied one byte too much from the source,
  possibly resulting in an out-of-bounds memory access.
  This can be reproduced by running e.g.
  `valgrind node -e \
    'fs.openSync(Buffer.from("node".repeat(8192)), "r")'`.

Further minor changes:
* This lifts the `out()` method of `StringBytes::InlineDecoder`
  to the common class so that it can be used when using the
  overloaded `operator*` does not seem appropiate.
* Hopefully clearer variable names.
* Add checks to make sure the length of the data does not exceed
  the allocated storage size, including the possible null terminator.

PR-URL: #6357
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
  • Loading branch information
addaleax authored and Fishrock123 committed May 4, 2016
commit fa9d82d1206b0d018113b74f373435fbbbb5e6ae
44 changes: 15 additions & 29 deletions src/string_bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,14 @@
#include "node.h"
#include "env.h"
#include "env-inl.h"
#include "util.h"

namespace node {

class StringBytes {
public:
class InlineDecoder {
class InlineDecoder : public MaybeStackBuffer<char> {
public:
InlineDecoder() : out_(nullptr) {
}

~InlineDecoder() {
if (out_ != out_st_)
delete[] out_;
out_ = nullptr;
}

inline bool Decode(Environment* env,
v8::Local<v8::String> string,
v8::Local<v8::Value> encoding,
Expand All @@ -33,28 +25,22 @@ class StringBytes {
return false;
}

size_t buflen = StringBytes::StorageSize(env->isolate(), string, enc);
if (buflen > sizeof(out_st_))
out_ = new char[buflen];
else
out_ = out_st_;
size_ = StringBytes::Write(env->isolate(),
out_,
buflen,
string,
enc);
const size_t storage = StringBytes::StorageSize(env->isolate(),
string,
enc);
AllocateSufficientStorage(storage);
const size_t length = StringBytes::Write(env->isolate(),
out(),
storage,
string,
enc);

// No zero terminator is included when using this method.
SetLength(length);
return true;
}

inline const char* out() const { return out_; }
inline size_t size() const { return size_; }

private:
static const int kStorageSize = 1024;

char out_st_[kStorageSize];
char* out_;
size_t size_;
inline size_t size() const { return length(); }
};

// Does the string match the encoding? Quick but non-exhaustive.
Expand Down
75 changes: 34 additions & 41 deletions src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,76 +10,69 @@ using v8::Local;
using v8::String;
using v8::Value;

static int MakeUtf8String(Isolate* isolate,
Local<Value> value,
char** dst,
const size_t size) {
template <typename T>
static void MakeUtf8String(Isolate* isolate,
Local<Value> value,
T* target) {
Local<String> string = value->ToString(isolate);
if (string.IsEmpty())
return 0;
size_t len = StringBytes::StorageSize(isolate, string, UTF8) + 1;
if (len > size) {
*dst = static_cast<char*>(malloc(len));
CHECK_NE(*dst, nullptr);
}
return;

const size_t storage = StringBytes::StorageSize(isolate, string, UTF8) + 1;
target->AllocateSufficientStorage(storage);
const int flags =
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
const int length = string->WriteUtf8(*dst, len, 0, flags);
(*dst)[length] = '\0';
return length;
const int length = string->WriteUtf8(target->out(), storage, 0, flags);
target->SetLengthAndZeroTerminate(length);
}

Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value)
: length_(0), str_(str_st_) {
Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value) {
if (value.IsEmpty())
return;
length_ = MakeUtf8String(isolate, value, &str_, sizeof(str_st_));

MakeUtf8String(isolate, value, this);
}


TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value)
: length_(0), str_(str_st_) {
if (value.IsEmpty())
TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value) {
if (value.IsEmpty()) {
return;
}

Local<String> string = value->ToString(isolate);
if (string.IsEmpty())
return;

// Allocate enough space to include the null terminator
size_t len =
StringBytes::StorageSize(isolate, string, UCS2) +
sizeof(uint16_t);
if (len > sizeof(str_st_)) {
str_ = static_cast<uint16_t*>(malloc(len));
CHECK_NE(str_, nullptr);
}
const size_t storage = string->Length() + 1;
AllocateSufficientStorage(storage);

const int flags =
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
length_ = string->Write(str_, 0, len, flags);
str_[length_] = '\0';
const int length = string->Write(out(), 0, storage, flags);
SetLengthAndZeroTerminate(length);
}

BufferValue::BufferValue(Isolate* isolate, Local<Value> value)
: str_(str_st_), fail_(true) {
BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
// Slightly different take on Utf8Value. If value is a String,
// it will return a Utf8 encoded string. If value is a Buffer,
// it will copy the data out of the Buffer as is.
if (value.IsEmpty())
if (value.IsEmpty()) {
// Dereferencing this object will return nullptr.
Invalidate();
return;
}

if (value->IsString()) {
MakeUtf8String(isolate, value, &str_, sizeof(str_st_));
fail_ = false;
MakeUtf8String(isolate, value, this);
} else if (Buffer::HasInstance(value)) {
size_t len = Buffer::Length(value) + 1;
if (len > sizeof(str_st_)) {
str_ = static_cast<char*>(malloc(len));
CHECK_NE(str_, nullptr);
}
memcpy(str_, Buffer::Data(value), len);
str_[len - 1] = '\0';
fail_ = false;
const size_t len = Buffer::Length(value);
// Leave place for the terminating '\0' byte.
AllocateSufficientStorage(len + 1);
memcpy(out(), Buffer::Data(value), len);
SetLengthAndZeroTerminate(len);
} else {
Invalidate();
}
}

Expand Down
123 changes: 74 additions & 49 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,77 +178,102 @@ inline TypeName* Unwrap(v8::Local<v8::Object> object);

inline void SwapBytes(uint16_t* dst, const uint16_t* src, size_t buflen);

class Utf8Value {
// Allocates an array of member type T. For up to kStackStorageSize items,
// the stack is used, otherwise malloc().
template <typename T, size_t kStackStorageSize = 1024>
class MaybeStackBuffer {
public:
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
const T* out() const {
return buf_;
}

~Utf8Value() {
if (str_ != str_st_)
free(str_);
T* out() {
return buf_;
}

char* operator*() {
return str_;
};
// operator* for compatibility with `v8::String::(Utf8)Value`
T* operator*() {
return buf_;
}

const char* operator*() const {
return str_;
};
const T* operator*() const {
return buf_;
}

size_t length() const {
return length_;
};

private:
size_t length_;
char* str_;
char str_st_[1024];
};
}

class TwoByteValue {
public:
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
// Call to make sure enough space for `storage` entries is available.
// There can only be 1 call to AllocateSufficientStorage or Invalidate
// per instance.
void AllocateSufficientStorage(size_t storage) {
if (storage <= kStackStorageSize) {
buf_ = buf_st_;
} else {
// Guard against overflow.
CHECK_LE(storage, sizeof(T) * storage);

buf_ = static_cast<T*>(malloc(sizeof(T) * storage));
CHECK_NE(buf_, nullptr);
}

// Remember how much was allocated to check against that in SetLength().
length_ = storage;
}

~TwoByteValue() {
if (str_ != str_st_)
free(str_);
void SetLength(size_t length) {
// length_ stores how much memory was allocated.
CHECK_LE(length, length_);
length_ = length;
}

uint16_t* operator*() {
return str_;
};
void SetLengthAndZeroTerminate(size_t length) {
// length_ stores how much memory was allocated.
CHECK_LE(length + 1, length_);
SetLength(length);

const uint16_t* operator*() const {
return str_;
};
// T() is 0 for integer types, nullptr for pointers, etc.
buf_[length] = T();
}

size_t length() const {
return length_;
};
// Make derefencing this object return nullptr.
// Calling this is mutually exclusive with calling
// AllocateSufficientStorage.
void Invalidate() {
CHECK_EQ(buf_, buf_st_);
length_ = 0;
buf_ = nullptr;
}

MaybeStackBuffer() : length_(0), buf_(buf_st_) {
// Default to a zero-length, null-terminated buffer.
buf_[0] = T();
}

~MaybeStackBuffer() {
if (buf_ != buf_st_)
free(buf_);
}
private:
size_t length_;
uint16_t* str_;
uint16_t str_st_[1024];
T* buf_;
T buf_st_[kStackStorageSize];
};

class BufferValue {
class Utf8Value : public MaybeStackBuffer<char> {
public:
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);

~BufferValue() {
if (str_ != str_st_)
free(str_);
}
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
};

const char* operator*() const {
return fail_ ? nullptr : str_;
};
class TwoByteValue : public MaybeStackBuffer<uint16_t> {
public:
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
};

private:
char* str_;
char str_st_[1024];
bool fail_;
class BufferValue : public MaybeStackBuffer<char> {
public:
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
};

} // namespace node
Expand Down