Browse Source

src: add Malloc() size param + overflow detection

Adds an optional second parameter to `node::Malloc()` and
an optional third parameter to `node::Realloc()` giving the
size/number of items to be allocated, in the style of `calloc(3)`.

Use a proper overflow check using division;
the previous `CHECK_GE(n * size, n);` would not detect all cases
of overflow (e.g. `size == SIZE_MAX / 2 && n == 3`).

PR-URL: https://github.com/nodejs/node/pull/8482
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com>
v6
Anna Henningsen 8 years ago
parent
commit
eb927fac38
No known key found for this signature in database GPG Key ID: D8B9F5AEAE84E4CF
  1. 5
      src/node_crypto.cc
  2. 2
      src/string_bytes.cc
  3. 24
      src/util-inl.h
  4. 11
      src/util.h

5
src/node_crypto.cc

@ -5592,11 +5592,10 @@ void GetCurves(const FunctionCallbackInfo<Value>& args) {
const size_t num_curves = EC_get_builtin_curves(nullptr, 0); const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
Local<Array> arr = Array::New(env->isolate(), num_curves); Local<Array> arr = Array::New(env->isolate(), num_curves);
EC_builtin_curve* curves; EC_builtin_curve* curves;
size_t alloc_size;
if (num_curves) { if (num_curves) {
alloc_size = sizeof(*curves) * num_curves; curves = static_cast<EC_builtin_curve*>(node::Malloc(sizeof(*curves),
curves = static_cast<EC_builtin_curve*>(node::Malloc(alloc_size)); num_curves));
CHECK_NE(curves, nullptr); CHECK_NE(curves, nullptr);

2
src/string_bytes.cc

@ -54,7 +54,7 @@ class ExternString: public ResourceType {
return scope.Escape(String::Empty(isolate)); return scope.Escape(String::Empty(isolate));
TypeName* new_data = TypeName* new_data =
static_cast<TypeName*>(node::Malloc(length * sizeof(*new_data))); static_cast<TypeName*>(node::Malloc(length, sizeof(*new_data)));
if (new_data == nullptr) { if (new_data == nullptr) {
return Local<String>(); return Local<String>();
} }

24
src/util-inl.h

@ -229,6 +229,14 @@ bool StringEqualNoCaseN(const char* a, const char* b, size_t length) {
return true; return true;
} }
inline size_t MultiplyWithOverflowCheck(size_t a, size_t b) {
size_t ret = a * b;
if (a != 0)
CHECK_EQ(b, ret / a);
return ret;
}
// These should be used in our code as opposed to the native // These should be used in our code as opposed to the native
// versions as they abstract out some platform and or // versions as they abstract out some platform and or
// compiler version specific functionality. // compiler version specific functionality.
@ -236,24 +244,28 @@ bool StringEqualNoCaseN(const char* a, const char* b, size_t length) {
// that the standard allows them to either return a unique pointer or a // that the standard allows them to either return a unique pointer or a
// nullptr for zero-sized allocation requests. Normalize by always using // nullptr for zero-sized allocation requests. Normalize by always using
// a nullptr. // a nullptr.
void* Realloc(void* pointer, size_t size) { void* Realloc(void* pointer, size_t n, size_t size) {
if (size == 0) { size_t full_size = MultiplyWithOverflowCheck(size, n);
if (full_size == 0) {
free(pointer); free(pointer);
return nullptr; return nullptr;
} }
return realloc(pointer, size);
return realloc(pointer, full_size);
} }
// As per spec realloc behaves like malloc if passed nullptr. // As per spec realloc behaves like malloc if passed nullptr.
void* Malloc(size_t size) { void* Malloc(size_t n, size_t size) {
if (n == 0) n = 1;
if (size == 0) size = 1; if (size == 0) size = 1;
return Realloc(nullptr, size); return Realloc(nullptr, n, size);
} }
void* Calloc(size_t n, size_t size) { void* Calloc(size_t n, size_t size) {
if (n == 0) n = 1; if (n == 0) n = 1;
if (size == 0) size = 1; if (size == 0) size = 1;
CHECK_GE(n * size, n); // Overflow guard. MultiplyWithOverflowCheck(size, n);
return calloc(n, size); return calloc(n, size);
} }

11
src/util.h

@ -22,9 +22,9 @@ namespace node {
// that the standard allows them to either return a unique pointer or a // that the standard allows them to either return a unique pointer or a
// nullptr for zero-sized allocation requests. Normalize by always using // nullptr for zero-sized allocation requests. Normalize by always using
// a nullptr. // a nullptr.
inline void* Realloc(void* pointer, size_t size); inline void* Realloc(void* pointer, size_t n, size_t size = 1);
inline void* Malloc(size_t size); inline void* Malloc(size_t n, size_t size = 1);
inline void* Calloc(size_t n, size_t size); inline void* Calloc(size_t n, size_t size = 1);
#ifdef __GNUC__ #ifdef __GNUC__
#define NO_RETURN __attribute__((noreturn)) #define NO_RETURN __attribute__((noreturn))
@ -285,10 +285,7 @@ class MaybeStackBuffer {
if (storage <= kStackStorageSize) { if (storage <= kStackStorageSize) {
buf_ = buf_st_; buf_ = buf_st_;
} else { } else {
// Guard against overflow. buf_ = static_cast<T*>(Malloc(sizeof(T), storage));
CHECK_LE(storage, sizeof(T) * storage);
buf_ = static_cast<T*>(Malloc(sizeof(T) * storage));
CHECK_NE(buf_, nullptr); CHECK_NE(buf_, nullptr);
} }

Loading…
Cancel
Save