Skip to content

Commit 5a948f6

Browse files
XadillaXaddaleax
authored andcommitted
dns: fix crash using dns.setServers after resolve4
The callback function in cares_query is synchronous and called before closed. So dns.setServers in the synchronous callback before closed will occur crashing. Fixes: #894 Refs: https://github.com/nodejs/node/blob/v6.9.4/deps/cares/src/ares_process.c#L1332-L1333 Refs: https://github.com/OpenSIPS/opensips/blob/2.3.0/proxy.c PR-URL: #13050 Reviewed-By: Anna Henningsen <[email protected]>
1 parent 47e3d00 commit 5a948f6

File tree

2 files changed

+162
-11
lines changed

2 files changed

+162
-11
lines changed

src/cares_wrap.cc

+150-11
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ inline const char* ToErrorCodeString(int status) {
9898
V(ETIMEOUT)
9999
#undef V
100100
}
101+
101102
return "UNKNOWN_ARES_ERROR";
102103
}
103104

@@ -296,6 +297,94 @@ Local<Array> HostentToNames(Environment* env, struct hostent* host) {
296297
return scope.Escape(names);
297298
}
298299

300+
void safe_free_hostent(struct hostent* host) {
301+
int idx;
302+
303+
if (host->h_addr_list != nullptr) {
304+
idx = 0;
305+
while (host->h_addr_list[idx]) {
306+
free(host->h_addr_list[idx++]);
307+
}
308+
free(host->h_addr_list);
309+
host->h_addr_list = 0;
310+
}
311+
312+
if (host->h_aliases != nullptr) {
313+
idx = 0;
314+
while (host->h_aliases[idx]) {
315+
free(host->h_aliases[idx++]);
316+
}
317+
free(host->h_aliases);
318+
host->h_aliases = 0;
319+
}
320+
321+
if (host->h_name != nullptr) {
322+
free(host->h_name);
323+
}
324+
325+
host->h_addrtype = host->h_length = 0;
326+
}
327+
328+
void cares_wrap_hostent_cpy(struct hostent* dest, struct hostent* src) {
329+
dest->h_addr_list = nullptr;
330+
dest->h_addrtype = 0;
331+
dest->h_aliases = nullptr;
332+
dest->h_length = 0;
333+
dest->h_name = nullptr;
334+
335+
/* copy `h_name` */
336+
size_t name_size = strlen(src->h_name) + 1;
337+
dest->h_name = node::Malloc<char>(name_size);
338+
memcpy(dest->h_name, src->h_name, name_size);
339+
340+
/* copy `h_aliases` */
341+
size_t alias_count;
342+
size_t cur_alias_length;
343+
for (alias_count = 0;
344+
src->h_aliases[alias_count] != nullptr;
345+
alias_count++) {
346+
}
347+
348+
dest->h_aliases = node::Malloc<char*>(alias_count + 1);
349+
for (size_t i = 0; i < alias_count; i++) {
350+
cur_alias_length = strlen(src->h_aliases[i]);
351+
dest->h_aliases[i] = node::Malloc(cur_alias_length + 1);
352+
memcpy(dest->h_aliases[i], src->h_aliases[i], cur_alias_length + 1);
353+
}
354+
dest->h_aliases[alias_count] = nullptr;
355+
356+
/* copy `h_addr_list` */
357+
size_t list_count;
358+
for (list_count = 0;
359+
src->h_addr_list[list_count] != nullptr;
360+
list_count++) {
361+
}
362+
363+
dest->h_addr_list = node::Malloc<char*>(list_count + 1);
364+
for (size_t i = 0; i < list_count; i++) {
365+
dest->h_addr_list[i] = node::Malloc(src->h_length);
366+
memcpy(dest->h_addr_list[i], src->h_addr_list[i], src->h_length);
367+
}
368+
dest->h_addr_list[list_count] = nullptr;
369+
370+
/* work after work */
371+
dest->h_length = src->h_length;
372+
dest->h_addrtype = src->h_addrtype;
373+
}
374+
375+
class QueryWrap;
376+
struct CaresAsyncData {
377+
QueryWrap* wrap;
378+
int status;
379+
bool is_host;
380+
union {
381+
hostent* host;
382+
unsigned char* buf;
383+
} data;
384+
int len;
385+
386+
uv_async_t async_handle;
387+
};
299388

300389
class QueryWrap : public AsyncWrap {
301390
public:
@@ -328,30 +417,80 @@ class QueryWrap : public AsyncWrap {
328417
return static_cast<void*>(this);
329418
}
330419

331-
static void Callback(void *arg, int status, int timeouts,
332-
unsigned char* answer_buf, int answer_len) {
333-
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
420+
static void CaresAsyncClose(uv_handle_t* handle) {
421+
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
422+
auto data = static_cast<struct CaresAsyncData*>(async->data);
423+
delete data->wrap;
424+
delete data;
425+
}
426+
427+
static void CaresAsyncCb(uv_async_t* handle) {
428+
auto data = static_cast<struct CaresAsyncData*>(handle->data);
429+
430+
QueryWrap* wrap = data->wrap;
431+
int status = data->status;
334432

335433
if (status != ARES_SUCCESS) {
336434
wrap->ParseError(status);
435+
} else if (!data->is_host) {
436+
unsigned char* buf = data->data.buf;
437+
wrap->Parse(buf, data->len);
438+
free(buf);
337439
} else {
338-
wrap->Parse(answer_buf, answer_len);
440+
hostent* host = data->data.host;
441+
wrap->Parse(host);
442+
safe_free_hostent(host);
443+
free(host);
339444
}
340445

341-
delete wrap;
446+
uv_close(reinterpret_cast<uv_handle_t*>(handle), CaresAsyncClose);
342447
}
343448

344449
static void Callback(void *arg, int status, int timeouts,
345-
struct hostent* host) {
450+
unsigned char* answer_buf, int answer_len) {
346451
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
347452

348-
if (status != ARES_SUCCESS) {
349-
wrap->ParseError(status);
350-
} else {
351-
wrap->Parse(host);
453+
unsigned char* buf_copy = nullptr;
454+
if (status == ARES_SUCCESS) {
455+
buf_copy = node::Malloc<unsigned char>(answer_len);
456+
memcpy(buf_copy, answer_buf, answer_len);
352457
}
353458

354-
delete wrap;
459+
CaresAsyncData* data = new CaresAsyncData();
460+
data->status = status;
461+
data->wrap = wrap;
462+
data->is_host = false;
463+
data->data.buf = buf_copy;
464+
data->len = answer_len;
465+
466+
uv_async_t* async_handle = &data->async_handle;
467+
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
468+
469+
async_handle->data = data;
470+
uv_async_send(async_handle);
471+
}
472+
473+
static void Callback(void *arg, int status, int timeouts,
474+
struct hostent* host) {
475+
QueryWrap* wrap = static_cast<QueryWrap*>(arg);
476+
477+
struct hostent* host_copy = nullptr;
478+
if (status == ARES_SUCCESS) {
479+
host_copy = node::Malloc<hostent>(1);
480+
cares_wrap_hostent_cpy(host_copy, host);
481+
}
482+
483+
CaresAsyncData* data = new CaresAsyncData();
484+
data->status = status;
485+
data->data.host = host_copy;
486+
data->wrap = wrap;
487+
data->is_host = true;
488+
489+
uv_async_t* async_handle = &data->async_handle;
490+
uv_async_init(wrap->env()->event_loop(), async_handle, CaresAsyncCb);
491+
492+
async_handle->data = data;
493+
uv_async_send(async_handle);
355494
}
356495

357496
void CallOnComplete(Local<Value> answer,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
// We don't care about `err` in the callback function of `dns.resolve4`. We just
4+
// want to test whether `dns.setServers` that is run after `resolve4` will cause
5+
// a crash or not. If it doesn't crash, the test succeeded.
6+
7+
const common = require('../common');
8+
const dns = require('dns');
9+
10+
dns.resolve4('google.com', common.mustCall(function(/* err, nameServers */) {
11+
dns.setServers([ '8.8.8.8' ]);
12+
}));

0 commit comments

Comments
 (0)