Skip to content

Commit 3812425

Browse files
author
Jake Champion
committed
fix: copy the object-store key parameter into a native object to avoid pointing to a value which could get garbage collected
currently the value being used is a pointer to a JS String's UTF 8 representation and the JS String can become garbage collected, leading to the pointer no longer being valid
1 parent a0882c7 commit 3812425

File tree

1 file changed

+29
-19
lines changed

1 file changed

+29
-19
lines changed

c-dependencies/js-compute-runtime/builtins/object-store.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,42 @@ ObjectStoreHandle object_store_handle(JSObject *obj) {
106106

107107
const unsigned ctor_length = 1;
108108

109-
std::optional<char *> parse_and_validate_key(JSContext *cx, JS::HandleValue val, size_t *key_len) {
109+
std::optional<std::string> parse_and_validate_key(JSContext *cx, JS::HandleValue val) {
110+
size_t key_len;
110111
// Convert the key argument into a String following https://tc39.es/ecma262/#sec-tostring
111-
JS::UniqueChars key = encode(cx, val, key_len);
112-
if (!key) {
112+
JS::UniqueChars keyString = encode(cx, val, &key_len);
113+
if (!keyString) {
113114
return std::nullopt;
114115
}
115116

116117
// If the converted string has a length of 0 then we throw an Error
117118
// because ObjectStore Keys have to be at-least 1 character.
118-
if (*key_len == 0) {
119+
if (key_len == 0) {
119120
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_EMPTY);
120121
return std::nullopt;
121122
}
122123

123124
// If the converted string has a length of more than 1024 then we throw an Error
124125
// because ObjectStore Keys have to be less than 1025 characters.
125-
if (*key_len > 1024) {
126+
if (key_len > 1024) {
126127
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_TOO_LONG);
127128
return std::nullopt;
128129
}
129130

130-
char *key_chars = key.get();
131+
std::string key(keyString.get(), key_len);
132+
auto key_chars = key.c_str();
131133

132134
if (auto res = find_invalid_character_for_object_store_key(key_chars)) {
133-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
134-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, *res);
135+
if (*res == '\n') {
136+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
137+
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "newline");
138+
} else if (*res == '\r') {
139+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
140+
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "carriage return");
141+
} else {
142+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
143+
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, res);
144+
}
135145
return std::nullopt;
136146
}
137147
auto acme_challenge = ".well-known/acme-challenge/";
@@ -145,7 +155,7 @@ std::optional<char *> parse_and_validate_key(JSContext *cx, JS::HandleValue val,
145155
return std::nullopt;
146156
}
147157

148-
return key_chars;
158+
return key;
149159
}
150160

151161
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
@@ -158,14 +168,14 @@ bool get(JSContext *cx, unsigned argc, JS::Value *vp) {
158168
return ReturnPromiseRejectedWithPendingError(cx, args);
159169
}
160170

161-
size_t key_len;
162-
std::optional<char *> key_chars = parse_and_validate_key(cx, args.get(0), &key_len);
171+
JS::RootedValue key(cx, args.get(0));
172+
auto key_chars = parse_and_validate_key(cx, key);
163173
if (!key_chars) {
164174
return ReturnPromiseRejectedWithPendingError(cx, args);
165175
}
166176
BodyHandle body_handle = {INVALID_HANDLE};
167-
int status =
168-
fastly_object_store_get(object_store_handle(self), key_chars.value(), key_len, &body_handle);
177+
int status = fastly_object_store_get(object_store_handle(self), key_chars.value().c_str(),
178+
key_chars.value().length(), &body_handle);
169179
if (!HANDLE_RESULT(cx, status)) {
170180
return false;
171181
}
@@ -198,8 +208,8 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
198208
return ReturnPromiseRejectedWithPendingError(cx, args);
199209
}
200210

201-
size_t key_len;
202-
std::optional<char *> key_chars = parse_and_validate_key(cx, args.get(0), &key_len);
211+
JS::RootedValue key(cx, args.get(0));
212+
auto key_chars = parse_and_validate_key(cx, key);
203213
if (!key_chars) {
204214
return ReturnPromiseRejectedWithPendingError(cx, args);
205215
}
@@ -232,8 +242,8 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
232242
JS::RootedObject source_owner(cx, builtins::NativeStreamSource::owner(stream_source));
233243
BodyHandle body = RequestOrResponse::body_handle(source_owner);
234244

235-
int status =
236-
fastly_object_store_insert(object_store_handle(self), key_chars.value(), key_len, body);
245+
int status = fastly_object_store_insert(object_store_handle(self), key_chars.value().c_str(),
246+
key_chars.value().length(), body);
237247
if (!HANDLE_RESULT(cx, status)) {
238248
return ReturnPromiseRejectedWithPendingError(cx, args);
239249
}
@@ -308,8 +318,8 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
308318
return ReturnPromiseRejectedWithPendingError(cx, args);
309319
}
310320

311-
int status = fastly_object_store_insert(object_store_handle(self), key_chars.value(), key_len,
312-
body_handle);
321+
int status = fastly_object_store_insert(object_store_handle(self), key_chars.value().c_str(),
322+
key_chars.value().length(), body_handle);
313323
// Ensure that we throw an exception for all unexpected host errors.
314324
if (!HANDLE_RESULT(cx, status)) {
315325
return RejectPromiseWithPendingError(cx, result_promise);

0 commit comments

Comments
 (0)