Browse Source

src: normalize malloc, realloc

malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
that the standard allows them to either return a unique pointer or a
nullptr for zero-sized allocation requests.  Normalize by always using
a nullptr.

- Introduce node::malloc, node::realloc and node::calloc that should
  be used throught our source.
- Update all existing node source files to use the new functions
  instead of the native allocation functions.

Fixes: https://github.com/nodejs/node/issues/7549
PR-URL: https://github.com/nodejs/node/pull/7564
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>

 Conflicts:
	src/node.cc
v6.x
Michael Dawson 8 years ago
committed by Jeremiah Senkpiel
parent
commit
d1d1433b02
  1. 3
      src/cares_wrap.cc
  2. 4
      src/node.cc
  3. 12
      src/node_buffer.cc
  4. 20
      src/node_crypto.cc
  5. 3
      src/node_internals.h
  6. 4
      src/stream_wrap.cc
  7. 8
      src/string_bytes.cc
  8. 2
      src/tls_wrap.cc
  9. 4
      src/udp_wrap.cc
  10. 26
      src/util-inl.h
  11. 13
      src/util.h

3
src/cares_wrap.cc

@ -142,7 +142,8 @@ static void ares_poll_close_cb(uv_handle_t* watcher) {
/* Allocates and returns a new node_ares_task */ /* Allocates and returns a new node_ares_task */
static node_ares_task* ares_task_create(Environment* env, ares_socket_t sock) { static node_ares_task* ares_task_create(Environment* env, ares_socket_t sock) {
node_ares_task* task = static_cast<node_ares_task*>(malloc(sizeof(*task))); node_ares_task* task =
static_cast<node_ares_task*>(node::Malloc(sizeof(*task)));
if (task == nullptr) { if (task == nullptr) {
/* Out of memory. */ /* Out of memory. */

4
src/node.cc

@ -1023,9 +1023,9 @@ void* ArrayBufferAllocator::Allocate(size_t size) {
if (env_ == nullptr || if (env_ == nullptr ||
!env_->array_buffer_allocator_info()->no_zero_fill() || !env_->array_buffer_allocator_info()->no_zero_fill() ||
zero_fill_all_buffers) zero_fill_all_buffers)
return calloc(size, 1); return node::Calloc(size, 1);
env_->array_buffer_allocator_info()->reset_fill_flag(); env_->array_buffer_allocator_info()->reset_fill_flag();
return malloc(size); return node::Malloc(size);
} }
static bool DomainHasErrorHandler(const Environment* env, static bool DomainHasErrorHandler(const Environment* env,

12
src/node_buffer.cc

@ -50,7 +50,7 @@
size_t length = end - start; size_t length = end - start;
#define BUFFER_MALLOC(length) \ #define BUFFER_MALLOC(length) \
zero_fill_all_buffers ? calloc(length, 1) : malloc(length) zero_fill_all_buffers ? node::Calloc(length, 1) : node::Malloc(length)
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define BSWAP_INTRINSIC_2(x) __builtin_bswap16(x) #define BSWAP_INTRINSIC_2(x) __builtin_bswap16(x)
@ -265,10 +265,6 @@ MaybeLocal<Object> New(Isolate* isolate,
size_t actual = 0; size_t actual = 0;
char* data = nullptr; char* data = nullptr;
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
// that the standard allows them to either return a unique pointer or a
// nullptr for zero-sized allocation requests. Normalize by always using
// a nullptr.
if (length > 0) { if (length > 0) {
data = static_cast<char*>(BUFFER_MALLOC(length)); data = static_cast<char*>(BUFFER_MALLOC(length));
@ -282,7 +278,7 @@ MaybeLocal<Object> New(Isolate* isolate,
free(data); free(data);
data = nullptr; data = nullptr;
} else if (actual < length) { } else if (actual < length) {
data = static_cast<char*>(realloc(data, actual)); data = static_cast<char*>(node::Realloc(data, actual));
CHECK_NE(data, nullptr); CHECK_NE(data, nullptr);
} }
} }
@ -361,7 +357,7 @@ MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
void* new_data; void* new_data;
if (length > 0) { if (length > 0) {
CHECK_NE(data, nullptr); CHECK_NE(data, nullptr);
new_data = malloc(length); new_data = node::Malloc(length);
if (new_data == nullptr) if (new_data == nullptr)
return Local<Object>(); return Local<Object>();
memcpy(new_data, data, length); memcpy(new_data, data, length);
@ -1083,7 +1079,7 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) {
offset, offset,
is_forward); is_forward);
} else if (enc == LATIN1) { } else if (enc == LATIN1) {
uint8_t* needle_data = static_cast<uint8_t*>(malloc(needle_length)); uint8_t* needle_data = static_cast<uint8_t*>(node::Malloc(needle_length));
if (needle_data == nullptr) { if (needle_data == nullptr) {
return args.GetReturnValue().Set(-1); return args.GetReturnValue().Set(-1);
} }

20
src/node_crypto.cc

@ -2279,7 +2279,7 @@ int SSLWrap<Base>::TLSExtStatusCallback(SSL* s, void* arg) {
size_t len = Buffer::Length(obj); size_t len = Buffer::Length(obj);
// OpenSSL takes control of the pointer after accepting it // OpenSSL takes control of the pointer after accepting it
char* data = reinterpret_cast<char*>(malloc(len)); char* data = reinterpret_cast<char*>(node::Malloc(len));
CHECK_NE(data, nullptr); CHECK_NE(data, nullptr);
memcpy(data, resp, len); memcpy(data, resp, len);
@ -3330,7 +3330,7 @@ bool CipherBase::GetAuthTag(char** out, unsigned int* out_len) const {
if (initialised_ || kind_ != kCipher || !auth_tag_) if (initialised_ || kind_ != kCipher || !auth_tag_)
return false; return false;
*out_len = auth_tag_len_; *out_len = auth_tag_len_;
*out = static_cast<char*>(malloc(auth_tag_len_)); *out = static_cast<char*>(node::Malloc(auth_tag_len_));
CHECK_NE(*out, nullptr); CHECK_NE(*out, nullptr);
memcpy(*out, auth_tag_, auth_tag_len_); memcpy(*out, auth_tag_, auth_tag_len_);
return true; return true;
@ -4906,7 +4906,7 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
// NOTE: field_size is in bits // NOTE: field_size is in bits
int field_size = EC_GROUP_get_degree(ecdh->group_); int field_size = EC_GROUP_get_degree(ecdh->group_);
size_t out_len = (field_size + 7) / 8; size_t out_len = (field_size + 7) / 8;
char* out = static_cast<char*>(malloc(out_len)); char* out = static_cast<char*>(node::Malloc(out_len));
CHECK_NE(out, nullptr); CHECK_NE(out, nullptr);
int r = ECDH_compute_key(out, out_len, pub, ecdh->key_, nullptr); int r = ECDH_compute_key(out, out_len, pub, ecdh->key_, nullptr);
@ -4942,7 +4942,7 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
if (size == 0) if (size == 0)
return env->ThrowError("Failed to get public key length"); return env->ThrowError("Failed to get public key length");
unsigned char* out = static_cast<unsigned char*>(malloc(size)); unsigned char* out = static_cast<unsigned char*>(node::Malloc(size));
CHECK_NE(out, nullptr); CHECK_NE(out, nullptr);
int r = EC_POINT_point2oct(ecdh->group_, pub, form, out, size, nullptr); int r = EC_POINT_point2oct(ecdh->group_, pub, form, out, size, nullptr);
@ -4968,7 +4968,7 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
return env->ThrowError("Failed to get ECDH private key"); return env->ThrowError("Failed to get ECDH private key");
int size = BN_num_bytes(b); int size = BN_num_bytes(b);
unsigned char* out = static_cast<unsigned char*>(malloc(size)); unsigned char* out = static_cast<unsigned char*>(node::Malloc(size));
CHECK_NE(out, nullptr); CHECK_NE(out, nullptr);
if (size != BN_bn2bin(b, out)) { if (size != BN_bn2bin(b, out)) {
@ -5099,7 +5099,7 @@ class PBKDF2Request : public AsyncWrap {
saltlen_(saltlen), saltlen_(saltlen),
salt_(salt), salt_(salt),
keylen_(keylen), keylen_(keylen),
key_(static_cast<char*>(malloc(keylen))), key_(static_cast<char*>(node::Malloc(keylen))),
iter_(iter) { iter_(iter) {
if (key() == nullptr) if (key() == nullptr)
FatalError("node::PBKDF2Request()", "Out of Memory"); FatalError("node::PBKDF2Request()", "Out of Memory");
@ -5262,7 +5262,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) {
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Salt"); THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Salt");
pass = static_cast<char*>(malloc(passlen)); pass = static_cast<char*>(node::Malloc(passlen));
if (pass == nullptr) { if (pass == nullptr) {
FatalError("node::PBKDF2()", "Out of Memory"); FatalError("node::PBKDF2()", "Out of Memory");
} }
@ -5274,7 +5274,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) {
goto err; goto err;
} }
salt = static_cast<char*>(malloc(saltlen)); salt = static_cast<char*>(node::Malloc(saltlen));
if (salt == nullptr) { if (salt == nullptr) {
FatalError("node::PBKDF2()", "Out of Memory"); FatalError("node::PBKDF2()", "Out of Memory");
} }
@ -5367,7 +5367,7 @@ class RandomBytesRequest : public AsyncWrap {
: AsyncWrap(env, object, AsyncWrap::PROVIDER_CRYPTO), : AsyncWrap(env, object, AsyncWrap::PROVIDER_CRYPTO),
error_(0), error_(0),
size_(size), size_(size),
data_(static_cast<char*>(malloc(size))) { data_(static_cast<char*>(node::Malloc(size))) {
if (data() == nullptr) if (data() == nullptr)
FatalError("node::RandomBytesRequest()", "Out of Memory"); FatalError("node::RandomBytesRequest()", "Out of Memory");
Wrap(object, this); Wrap(object, this);
@ -5596,7 +5596,7 @@ void GetCurves(const FunctionCallbackInfo<Value>& args) {
if (num_curves) { if (num_curves) {
alloc_size = sizeof(*curves) * num_curves; alloc_size = sizeof(*curves) * num_curves;
curves = static_cast<EC_builtin_curve*>(malloc(alloc_size)); curves = static_cast<EC_builtin_curve*>(node::Malloc(alloc_size));
CHECK_NE(curves, nullptr); CHECK_NE(curves, nullptr);

3
src/node_internals.h

@ -198,7 +198,8 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
inline void set_env(Environment* env) { env_ = env; } inline void set_env(Environment* env) { env_ = env; }
virtual void* Allocate(size_t size); // Defined in src/node.cc virtual void* Allocate(size_t size); // Defined in src/node.cc
virtual void* AllocateUninitialized(size_t size) { return malloc(size); } virtual void* AllocateUninitialized(size_t size)
{ return node::Malloc(size); }
virtual void Free(void* data, size_t) { free(data); } virtual void Free(void* data, size_t) { free(data); }
private: private:

4
src/stream_wrap.cc

@ -148,7 +148,7 @@ void StreamWrap::OnAlloc(uv_handle_t* handle,
void StreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) { void StreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) {
buf->base = static_cast<char*>(malloc(size)); buf->base = static_cast<char*>(node::Malloc(size));
buf->len = size; buf->len = size;
if (buf->base == nullptr && size > 0) { if (buf->base == nullptr && size > 0) {
@ -204,7 +204,7 @@ void StreamWrap::OnReadImpl(ssize_t nread,
return; return;
} }
char* base = static_cast<char*>(realloc(buf->base, nread)); char* base = static_cast<char*>(node::Realloc(buf->base, nread));
CHECK_LE(static_cast<size_t>(nread), buf->len); CHECK_LE(static_cast<size_t>(nread), buf->len);
if (pending == UV_TCP) { if (pending == UV_TCP) {

8
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*>(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>();
} }
@ -624,7 +624,7 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
case ASCII: case ASCII:
if (contains_non_ascii(buf, buflen)) { if (contains_non_ascii(buf, buflen)) {
char* out = static_cast<char*>(malloc(buflen)); char* out = static_cast<char*>(node::Malloc(buflen));
if (out == nullptr) { if (out == nullptr) {
return Local<String>(); return Local<String>();
} }
@ -659,7 +659,7 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
case BASE64: { case BASE64: {
size_t dlen = base64_encoded_size(buflen); size_t dlen = base64_encoded_size(buflen);
char* dst = static_cast<char*>(malloc(dlen)); char* dst = static_cast<char*>(node::Malloc(dlen));
if (dst == nullptr) { if (dst == nullptr) {
return Local<String>(); return Local<String>();
} }
@ -678,7 +678,7 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
case HEX: { case HEX: {
size_t dlen = buflen * 2; size_t dlen = buflen * 2;
char* dst = static_cast<char*>(malloc(dlen)); char* dst = static_cast<char*>(node::Malloc(dlen));
if (dst == nullptr) { if (dst == nullptr) {
return Local<String>(); return Local<String>();
} }

2
src/tls_wrap.cc

@ -661,7 +661,7 @@ void TLSWrap::OnReadImpl(ssize_t nread,
void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) { void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) {
buf->base = static_cast<char*>(malloc(suggested_size)); buf->base = static_cast<char*>(node::Malloc(suggested_size));
CHECK_NE(buf->base, nullptr); CHECK_NE(buf->base, nullptr);
buf->len = suggested_size; buf->len = suggested_size;
} }

4
src/udp_wrap.cc

@ -384,7 +384,7 @@ void UDPWrap::OnSend(uv_udp_send_t* req, int status) {
void UDPWrap::OnAlloc(uv_handle_t* handle, void UDPWrap::OnAlloc(uv_handle_t* handle,
size_t suggested_size, size_t suggested_size,
uv_buf_t* buf) { uv_buf_t* buf) {
buf->base = static_cast<char*>(malloc(suggested_size)); buf->base = static_cast<char*>(node::Malloc(suggested_size));
buf->len = suggested_size; buf->len = suggested_size;
if (buf->base == nullptr && suggested_size > 0) { if (buf->base == nullptr && suggested_size > 0) {
@ -426,7 +426,7 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
return; return;
} }
char* base = static_cast<char*>(realloc(buf->base, nread)); char* base = static_cast<char*>(node::Realloc(buf->base, nread));
argv[2] = Buffer::New(env, base, nread).ToLocalChecked(); argv[2] = Buffer::New(env, base, nread).ToLocalChecked();
argv[3] = AddressToJS(env, addr); argv[3] = AddressToJS(env, addr);
wrap->MakeCallback(env->onmessage_string(), arraysize(argv), argv); wrap->MakeCallback(env->onmessage_string(), arraysize(argv), argv);

26
src/util-inl.h

@ -229,6 +229,32 @@ bool StringEqualNoCaseN(const char* a, const char* b, size_t length) {
return true; return true;
} }
// These should be used in our code as opposed to the native
// versions as they abstract out some platform and or
// compiler version specific functionality.
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
// that the standard allows them to either return a unique pointer or a
// nullptr for zero-sized allocation requests. Normalize by always using
// a nullptr.
void* Realloc(void* pointer, size_t size) {
if (size == 0) {
free(pointer);
return nullptr;
}
return realloc(pointer, size);
}
// As per spec realloc behaves like malloc if passed nullptr.
void* Malloc(size_t size) {
return Realloc(nullptr, size);
}
void* Calloc(size_t n, size_t size) {
if ((n == 0) || (size == 0)) return nullptr;
CHECK_GE(n * size, n); // Overflow guard.
return calloc(n, size);
}
} // namespace node } // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

13
src/util.h

@ -24,6 +24,17 @@
namespace node { namespace node {
// These should be used in our code as opposed to the native
// versions as they abstract out some platform and or
// compiler version specific functionality
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
// that the standard allows them to either return a unique pointer or a
// nullptr for zero-sized allocation requests. Normalize by always using
// a nullptr.
inline void* Realloc(void* pointer, size_t size);
inline void* Malloc(size_t size);
inline void* Calloc(size_t n, size_t size);
#ifdef __GNUC__ #ifdef __GNUC__
#define NO_RETURN __attribute__((noreturn)) #define NO_RETURN __attribute__((noreturn))
#else #else
@ -300,7 +311,7 @@ class MaybeStackBuffer {
// Guard against overflow. // Guard against overflow.
CHECK_LE(storage, sizeof(T) * storage); CHECK_LE(storage, sizeof(T) * storage);
buf_ = static_cast<T*>(malloc(sizeof(T) * storage)); buf_ = static_cast<T*>(Malloc(sizeof(T) * storage));
CHECK_NE(buf_, nullptr); CHECK_NE(buf_, nullptr);
} }

Loading…
Cancel
Save