Skip to content

Commit

Permalink
src: fix mismatched delete[] in src/node_file.cc
Browse files Browse the repository at this point in the history
Fix a bad delete of a pointer that was allocated with placement new.
Casting the pointer was not the right solution because there was at
least one non-placement new constructor call.

This commit rewrites FSReqWrap to be more explicit about ownership of
the auxiliary data and removes a number of egregious const_casts.
The ASYNC_DEST_CALL macro also gets significantly slimmed down.

PR-URL: #1092
Reviewed-By: Fedor Indutny <fedor@indutny.com>
  • Loading branch information
bnoordhuis committed Mar 7, 2015
1 parent 0f7c8eb commit 648fc63
Showing 1 changed file with 61 additions and 40 deletions.
101 changes: 61 additions & 40 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,40 +49,72 @@ using v8::Value;

class FSReqWrap: public ReqWrap<uv_fs_t> {
public:
void* operator new(size_t size) { return new char[size]; }
void* operator new(size_t size, char* storage) { return storage; }
enum Ownership { COPY, MOVE };

inline static FSReqWrap* New(Environment* env,
Local<Object> req,
const char* syscall,
const char* data = nullptr,
Ownership ownership = COPY);

inline void Dispose();

void ReleaseEarly() {
if (data_ != inline_data()) {
delete[] data_;
data_ = nullptr;
}
}

const char* syscall() const { return syscall_; }
const char* data() const { return data_; }

private:
FSReqWrap(Environment* env,
Local<Object> req,
const char* syscall,
char* data = nullptr)
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
syscall_(syscall),
data_(data),
dest_len_(0) {
const char* data)
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
syscall_(syscall),
data_(data) {
Wrap(object(), this);
}

void ReleaseEarly() {
if (data_ == nullptr)
return;
delete[] data_;
data_ = nullptr;
}
~FSReqWrap() { ReleaseEarly(); }

inline const char* syscall() const { return syscall_; }
inline const char* dest() const { return dest_; }
inline unsigned int dest_len() const { return dest_len_; }
inline void dest_len(unsigned int dest_len) { dest_len_ = dest_len; }
void* operator new(size_t size) = delete;
void* operator new(size_t size, char* storage) { return storage; }
char* inline_data() { return reinterpret_cast<char*>(this + 1); }

private:
const char* syscall_;
char* data_;
unsigned int dest_len_;
char dest_[1];
const char* data_;

DISALLOW_COPY_AND_ASSIGN(FSReqWrap);
};


FSReqWrap* FSReqWrap::New(Environment* env,
Local<Object> req,
const char* syscall,
const char* data,
Ownership ownership) {
const bool copy = (data != nullptr && ownership == COPY);
const size_t size = copy ? 1 + strlen(data) : 0;
FSReqWrap* that;
char* const storage = new char[sizeof(*that) + size];
that = new(storage) FSReqWrap(env, req, syscall, data);
if (copy)
that->data_ = static_cast<char*>(memcpy(that->inline_data(), data, size));
return that;
}


void FSReqWrap::Dispose() {
this->~FSReqWrap();
delete[] reinterpret_cast<char*>(this);
}


static void NewFSReqWrap(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
}
Expand Down Expand Up @@ -111,13 +143,12 @@ static void After(uv_fs_t *req) {

if (req->result < 0) {
// An error happened.
const char* dest = req_wrap->dest_len() > 0 ? req_wrap->dest() : nullptr;
argv[0] = UVException(env->isolate(),
req->result,
req_wrap->syscall(),
nullptr,
req->path,
dest);
req_wrap->data());
} else {
// error value is empty or null for non-error.
argv[0] = Null(env->isolate());
Expand Down Expand Up @@ -212,7 +243,7 @@ static void After(uv_fs_t *req) {
req_wrap->MakeCallback(env->oncomplete_string(), argc, argv);

uv_fs_req_cleanup(&req_wrap->req_);
delete req_wrap;
req_wrap->Dispose();
}

// This struct is only used on sync fs calls.
Expand All @@ -225,20 +256,10 @@ struct fs_req_wrap {
};


#define ASYNC_DEST_CALL(func, req, dest_path, ...) \
#define ASYNC_DEST_CALL(func, req, dest, ...) \
Environment* env = Environment::GetCurrent(args); \
FSReqWrap* req_wrap; \
char* dest_str = (dest_path); \
int dest_len = dest_str == nullptr ? 0 : strlen(dest_str); \
char* storage = new char[sizeof(*req_wrap) + dest_len]; \
CHECK(req->IsObject()); \
req_wrap = new(storage) FSReqWrap(env, req.As<Object>(), #func); \
req_wrap->dest_len(dest_len); \
if (dest_str != nullptr) { \
memcpy(const_cast<char*>(req_wrap->dest()), \
dest_str, \
dest_len + 1); \
} \
FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), #func, dest); \
int err = uv_fs_ ## func(env->event_loop(), \
&req_wrap->req_, \
__VA_ARGS__, \
Expand Down Expand Up @@ -811,7 +832,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
char* buf = nullptr;
int64_t pos;
size_t len;
bool must_free = false;
FSReqWrap::Ownership ownership = FSReqWrap::COPY;

// will assign buf and len if string was external
if (!StringBytes::GetExternalParts(env->isolate(),
Expand All @@ -824,7 +845,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
// StorageSize may return too large a char, so correct the actual length
// by the write size
len = StringBytes::Write(env->isolate(), buf, len, args[1], enc);
must_free = true;
ownership = FSReqWrap::MOVE;
}
pos = GET_OFFSET(args[2]);
req = args[4];
Expand All @@ -833,13 +854,13 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {

if (!req->IsObject()) {
SYNC_CALL(write, nullptr, fd, &uvbuf, 1, pos)
if (must_free)
if (ownership == FSReqWrap::MOVE)
delete[] buf;
return args.GetReturnValue().Set(SYNC_RESULT);
}

FSReqWrap* req_wrap =
new FSReqWrap(env, req.As<Object>(), "write", must_free ? buf : nullptr);
FSReqWrap::New(env, req.As<Object>(), "write", buf, ownership);
int err = uv_fs_write(env->event_loop(),
&req_wrap->req_,
fd,
Expand Down

0 comments on commit 648fc63

Please sign in to comment.