Skip to content

Commit 648fc63

Browse files
committed
src: fix mismatched delete[] in src/node_file.cc
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 <[email protected]>
1 parent 0f7c8eb commit 648fc63

File tree

1 file changed

+61
-40
lines changed

1 file changed

+61
-40
lines changed

src/node_file.cc

+61-40
Original file line numberDiff line numberDiff line change
@@ -49,40 +49,72 @@ using v8::Value;
4949

5050
class FSReqWrap: public ReqWrap<uv_fs_t> {
5151
public:
52-
void* operator new(size_t size) { return new char[size]; }
53-
void* operator new(size_t size, char* storage) { return storage; }
52+
enum Ownership { COPY, MOVE };
53+
54+
inline static FSReqWrap* New(Environment* env,
55+
Local<Object> req,
56+
const char* syscall,
57+
const char* data = nullptr,
58+
Ownership ownership = COPY);
59+
60+
inline void Dispose();
5461

62+
void ReleaseEarly() {
63+
if (data_ != inline_data()) {
64+
delete[] data_;
65+
data_ = nullptr;
66+
}
67+
}
68+
69+
const char* syscall() const { return syscall_; }
70+
const char* data() const { return data_; }
71+
72+
private:
5573
FSReqWrap(Environment* env,
5674
Local<Object> req,
5775
const char* syscall,
58-
char* data = nullptr)
59-
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
60-
syscall_(syscall),
61-
data_(data),
62-
dest_len_(0) {
76+
const char* data)
77+
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
78+
syscall_(syscall),
79+
data_(data) {
6380
Wrap(object(), this);
6481
}
6582

66-
void ReleaseEarly() {
67-
if (data_ == nullptr)
68-
return;
69-
delete[] data_;
70-
data_ = nullptr;
71-
}
83+
~FSReqWrap() { ReleaseEarly(); }
7284

73-
inline const char* syscall() const { return syscall_; }
74-
inline const char* dest() const { return dest_; }
75-
inline unsigned int dest_len() const { return dest_len_; }
76-
inline void dest_len(unsigned int dest_len) { dest_len_ = dest_len; }
85+
void* operator new(size_t size) = delete;
86+
void* operator new(size_t size, char* storage) { return storage; }
87+
char* inline_data() { return reinterpret_cast<char*>(this + 1); }
7788

78-
private:
7989
const char* syscall_;
80-
char* data_;
81-
unsigned int dest_len_;
82-
char dest_[1];
90+
const char* data_;
91+
92+
DISALLOW_COPY_AND_ASSIGN(FSReqWrap);
8393
};
8494

8595

96+
FSReqWrap* FSReqWrap::New(Environment* env,
97+
Local<Object> req,
98+
const char* syscall,
99+
const char* data,
100+
Ownership ownership) {
101+
const bool copy = (data != nullptr && ownership == COPY);
102+
const size_t size = copy ? 1 + strlen(data) : 0;
103+
FSReqWrap* that;
104+
char* const storage = new char[sizeof(*that) + size];
105+
that = new(storage) FSReqWrap(env, req, syscall, data);
106+
if (copy)
107+
that->data_ = static_cast<char*>(memcpy(that->inline_data(), data, size));
108+
return that;
109+
}
110+
111+
112+
void FSReqWrap::Dispose() {
113+
this->~FSReqWrap();
114+
delete[] reinterpret_cast<char*>(this);
115+
}
116+
117+
86118
static void NewFSReqWrap(const FunctionCallbackInfo<Value>& args) {
87119
CHECK(args.IsConstructCall());
88120
}
@@ -111,13 +143,12 @@ static void After(uv_fs_t *req) {
111143

112144
if (req->result < 0) {
113145
// An error happened.
114-
const char* dest = req_wrap->dest_len() > 0 ? req_wrap->dest() : nullptr;
115146
argv[0] = UVException(env->isolate(),
116147
req->result,
117148
req_wrap->syscall(),
118149
nullptr,
119150
req->path,
120-
dest);
151+
req_wrap->data());
121152
} else {
122153
// error value is empty or null for non-error.
123154
argv[0] = Null(env->isolate());
@@ -212,7 +243,7 @@ static void After(uv_fs_t *req) {
212243
req_wrap->MakeCallback(env->oncomplete_string(), argc, argv);
213244

214245
uv_fs_req_cleanup(&req_wrap->req_);
215-
delete req_wrap;
246+
req_wrap->Dispose();
216247
}
217248

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

227258

228-
#define ASYNC_DEST_CALL(func, req, dest_path, ...) \
259+
#define ASYNC_DEST_CALL(func, req, dest, ...) \
229260
Environment* env = Environment::GetCurrent(args); \
230-
FSReqWrap* req_wrap; \
231-
char* dest_str = (dest_path); \
232-
int dest_len = dest_str == nullptr ? 0 : strlen(dest_str); \
233-
char* storage = new char[sizeof(*req_wrap) + dest_len]; \
234261
CHECK(req->IsObject()); \
235-
req_wrap = new(storage) FSReqWrap(env, req.As<Object>(), #func); \
236-
req_wrap->dest_len(dest_len); \
237-
if (dest_str != nullptr) { \
238-
memcpy(const_cast<char*>(req_wrap->dest()), \
239-
dest_str, \
240-
dest_len + 1); \
241-
} \
262+
FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), #func, dest); \
242263
int err = uv_fs_ ## func(env->event_loop(), \
243264
&req_wrap->req_, \
244265
__VA_ARGS__, \
@@ -811,7 +832,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
811832
char* buf = nullptr;
812833
int64_t pos;
813834
size_t len;
814-
bool must_free = false;
835+
FSReqWrap::Ownership ownership = FSReqWrap::COPY;
815836

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

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

841862
FSReqWrap* req_wrap =
842-
new FSReqWrap(env, req.As<Object>(), "write", must_free ? buf : nullptr);
863+
FSReqWrap::New(env, req.As<Object>(), "write", buf, ownership);
843864
int err = uv_fs_write(env->event_loop(),
844865
&req_wrap->req_,
845866
fd,

0 commit comments

Comments
 (0)