Skip to content

Commit a877e7e

Browse files
committed
Merge pull request #15 from basho/bz1283-thread-safety
BUG 1283: erlang_js uses non-thread-safe driver function
2 parents f42a246 + 8122b34 commit a877e7e

File tree

1 file changed

+69
-26
lines changed

1 file changed

+69
-26
lines changed

c_src/spidermonkey_drv.c

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
limitations under the License. */
1515

1616
#include <string.h>
17+
#include <assert.h>
1718
#include <erl_driver.h>
1819

1920
#include "spidermonkey.h"
@@ -32,6 +33,10 @@ typedef struct _spidermonkey_drv_t {
3233
typedef struct _js_call_t {
3334
spidermonkey_drv_t *driver_data;
3435
ErlDrvBinary *args;
36+
ErlDrvTermData return_terms[20];
37+
char return_call_id[32];
38+
int return_term_count;
39+
const char *return_string;
3540
} js_call;
3641

3742
typedef void (*asyncfun)(void *);
@@ -42,6 +47,7 @@ static ErlDrvData start(ErlDrvPort port, char *cmd);
4247
static int init();
4348
static void stop(ErlDrvData handle);
4449
static void process(ErlDrvData handle, ErlIOVec *ev);
50+
static void ready_async(ErlDrvData handle, ErlDrvThreadData async_data);
4551

4652
static ErlDrvEntry spidermonkey_drv_entry = {
4753
init, /* init */
@@ -56,7 +62,7 @@ static ErlDrvEntry spidermonkey_drv_entry = {
5662
NULL, /* control */
5763
NULL, /* timeout */
5864
process, /* process */
59-
NULL, /* ready_async */
65+
ready_async, /* ready_async */
6066
NULL, /* flush */
6167
NULL, /* call */
6268
NULL, /* event */
@@ -67,46 +73,67 @@ static ErlDrvEntry spidermonkey_drv_entry = {
6773
};
6874

6975

70-
void send_output(ErlDrvPort port, ErlDrvTermData *terms, int term_count) {
71-
driver_output_term(port, terms, term_count);
76+
void send_immediate_ok_response(spidermonkey_drv_t *dd, const char *call_id) {
77+
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY, (ErlDrvTermData) call_id, strlen(call_id),
78+
ERL_DRV_ATOM, dd->atom_ok,
79+
ERL_DRV_TUPLE, 2};
80+
driver_output_term(dd->port, terms, sizeof(terms) / sizeof(terms[0]));
7281
}
7382

74-
void send_ok_response(spidermonkey_drv_t *dd, const char *call_id) {
75-
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY, (ErlDrvTermData) call_id, strlen(call_id),
83+
#define COPY_DATA(CD, CID, TERMS) \
84+
do { \
85+
assert(strlen(CID) < sizeof(CD->return_call_id) - 1); \
86+
strcpy(CD->return_call_id, CID); \
87+
assert(sizeof(TERMS) <= sizeof(CD->return_terms)); \
88+
memcpy(CD->return_terms, TERMS, sizeof(TERMS)); \
89+
CD->return_term_count = sizeof(TERMS) / sizeof(TERMS[0]); \
90+
} while (0)
91+
92+
void send_ok_response(spidermonkey_drv_t *dd, js_call *call_data,
93+
const char *call_id) {
94+
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY,
95+
(ErlDrvTermData) call_data->return_call_id,strlen(call_id),
7696
ERL_DRV_ATOM, dd->atom_ok,
7797
ERL_DRV_TUPLE, 2};
78-
send_output(dd->port, terms, sizeof(terms) / sizeof(terms[0]));
98+
COPY_DATA(call_data, call_id, terms);
7999
}
80100

81-
void send_error_string_response(spidermonkey_drv_t *dd, const char *call_id, const char *msg) {
82-
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY, (ErlDrvTermData) call_id, strlen(call_id),
101+
void send_error_string_response(spidermonkey_drv_t *dd, js_call *call_data,
102+
const char *call_id, const char *msg) {
103+
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY,
104+
(ErlDrvTermData) call_data->return_call_id,strlen(call_id),
83105
ERL_DRV_ATOM, dd->atom_error,
84106
ERL_DRV_BUF2BINARY, (ErlDrvTermData) msg, strlen(msg),
85107
ERL_DRV_TUPLE, 3};
86-
send_output(dd->port, terms, sizeof(terms) / sizeof(terms[0]));
108+
COPY_DATA(call_data, call_id, terms);
109+
call_data->return_string = msg;
87110
}
88111

89-
void send_string_response(spidermonkey_drv_t *dd, const char *call_id, const char *result) {
90-
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY, (ErlDrvTermData) call_id, strlen(call_id),
112+
void send_string_response(spidermonkey_drv_t *dd, js_call *call_data,
113+
const char *call_id, const char *result) {
114+
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY,
115+
(ErlDrvTermData) call_data->return_call_id,strlen(call_id),
91116
ERL_DRV_ATOM, dd->atom_ok,
92117
ERL_DRV_BUF2BINARY, (ErlDrvTermData) result, strlen(result),
93118
ERL_DRV_TUPLE, 3};
94-
send_output(dd->port, terms, sizeof(terms) / sizeof(terms[0]));
119+
COPY_DATA(call_data, call_id, terms);
120+
call_data->return_string = result;
95121
}
96122

97-
void unknown_command(spidermonkey_drv_t *dd, const char *call_id) {
98-
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY, (ErlDrvTermData) call_id, strlen(call_id),
123+
void unknown_command(spidermonkey_drv_t *dd, js_call *call_data,
124+
const char *call_id) {
125+
ErlDrvTermData terms[] = {ERL_DRV_BUF2BINARY,
126+
(ErlDrvTermData) call_data->return_call_id,strlen(call_id),
99127
ERL_DRV_ATOM, dd->atom_error,
100128
ERL_DRV_ATOM, dd->atom_unknown_cmd,
101129
ERL_DRV_TUPLE, 3};
102-
send_output(dd->port, terms, sizeof(terms) / sizeof(terms[0]));
130+
COPY_DATA(call_data, call_id, terms);
103131
}
104132

105133
void run_js(void *jsargs) {
106134
js_call *call_data = (js_call *) jsargs;
107135
spidermonkey_drv_t *dd = call_data->driver_data;
108136
ErlDrvBinary *args = call_data->args;
109-
driver_free(call_data);
110137
char *data = args->orig_bytes;
111138
char *command = read_command(&data);
112139
char *call_id = read_string(&data);
@@ -116,39 +143,36 @@ void run_js(void *jsargs) {
116143
char *code = read_string(&data);
117144
result = sm_eval(dd->vm, filename, code, 1);
118145
if ((strncmp(result, "[{\"error\":\"notfound\"}]", 22) == 0) || (strncmp(result, "{\"error\"", 8) == 0)) {
119-
send_error_string_response(dd, call_id, result);
146+
send_error_string_response(dd, call_data, call_id, result);
120147
}
121148
else {
122-
send_string_response(dd, call_id, result);
149+
send_string_response(dd, call_data, call_id, result);
123150
}
124151
driver_free(filename);
125152
driver_free(code);
126-
driver_free(result);
127153
}
128154
else if (strncmp(command, "dj", 2) == 0) {
129155
char *filename = read_string(&data);
130156
char *code = read_string(&data);
131157
result = sm_eval(dd->vm, filename, code, 0);
132158
if (result == NULL) {
133-
send_ok_response(dd, call_id);
159+
send_ok_response(dd, call_data, call_id);
134160
}
135161
else {
136-
send_error_string_response(dd, call_id, result);
137-
driver_free(result);
162+
send_error_string_response(dd, call_data, call_id, result);
138163
}
139164
driver_free(filename);
140165
driver_free(code);
141166
}
142167
else if (strncmp(command, "sd", 2) == 0) {
143168
dd->shutdown = 1;
144-
send_ok_response(dd, call_id);
169+
send_ok_response(dd, call_data, call_id);
145170
}
146171
else {
147-
unknown_command(dd, call_id);
172+
unknown_command(dd, call_data, call_id);
148173
}
149174
driver_free(command);
150175
driver_free(call_id);
151-
driver_binary_dec_refc(args);
152176
}
153177

154178
DRIVER_INIT(spidermonkey_drv) {
@@ -183,6 +207,7 @@ static void stop(ErlDrvData handle) {
183207

184208
static void process(ErlDrvData handle, ErlIOVec *ev) {
185209
spidermonkey_drv_t *dd = (spidermonkey_drv_t *) handle;
210+
186211
char *data = ev->binv[1]->orig_bytes;
187212
char *command = read_command(&data);
188213
if (strncmp(command, "ij", 2) == 0) {
@@ -194,17 +219,35 @@ static void process(ErlDrvData handle, ErlIOVec *ev) {
194219
thread_stack = thread_stack * (1024 * 1024);
195220
int heap_size = read_int32(&data) * (1024 * 1024);
196221
dd->vm = sm_initialize(thread_stack, heap_size);
197-
send_ok_response(dd, call_id);
222+
send_immediate_ok_response(dd, call_id);
198223
driver_free(call_id);
199224
}
200225
else {
201226
js_call *call_data = (js_call *) driver_alloc(sizeof(js_call));
202227
call_data->driver_data = dd;
203228
call_data->args = ev->binv[1];
229+
call_data->return_terms[0] = 0;
230+
call_data->return_term_count = 0;
231+
call_data->return_string = NULL;
204232
driver_binary_inc_refc(call_data->args);
205233
ErlDrvPort port = dd->port;
206234
unsigned long thread_key = (unsigned long) port;
207235
driver_async(dd->port, (unsigned int *) &thread_key, (asyncfun) run_js, (void *) call_data, NULL);
208236
}
209237
driver_free(command);
210238
}
239+
240+
static void
241+
ready_async(ErlDrvData handle, ErlDrvThreadData async_data)
242+
{
243+
spidermonkey_drv_t *dd = (spidermonkey_drv_t *) handle;
244+
js_call *call_data = (js_call *) async_data;
245+
246+
driver_output_term(dd->port,
247+
call_data->return_terms, call_data->return_term_count);
248+
driver_binary_dec_refc(call_data->args);
249+
if (call_data->return_string != NULL) {
250+
driver_free((void *) call_data->return_string);
251+
}
252+
driver_free(call_data);
253+
}

0 commit comments

Comments
 (0)