Skip to content

Commit 38c4716

Browse files
authored
chore: Remove more direct uses of free, replacing them with unique ptrs (#542)
Remove all but three direct uses of `JS_free`, replacing them with either `mozilla::UniquePtr`, or `std::vector`. The resulting pattern is that direct calls to `JS_free` are no longer needed, and functions that take ownership of memory now need to use calls to `.release()` on the unique pointers. I also noticed that I missed a call-site to `fastly_http_req_uri_get` when converting over to the host_api, so I've updated that as well.
1 parent 0b6fd83 commit 38c4716

File tree

5 files changed

+47
-50
lines changed

5 files changed

+47
-50
lines changed

runtime/js-compute-runtime/builtins/crypto-algorithm.cpp

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "openssl/rsa.h"
22
#include "openssl/sha.h"
33
#include <iostream>
4+
#include <optional>
45
#include <span>
6+
#include <vector>
57

68
#include "crypto-algorithm.h"
79
#include "crypto-key-rsa-components.h"
@@ -52,49 +54,47 @@ const EVP_MD *createDigestAlgorithm(JSContext *cx, JS::HandleObject key) {
5254
}
5355
// This implements https://w3c.github.io/webcrypto/#sha-operations for all
5456
// the SHA algorithms that we support.
55-
std::optional<std::span<uint8_t>> rawDigest(JSContext *cx, std::span<uint8_t> data,
56-
const EVP_MD *algorithm, size_t buffer_size) {
57+
std::optional<std::vector<uint8_t>> rawDigest(JSContext *cx, std::span<uint8_t> data,
58+
const EVP_MD *algorithm, size_t buffer_size) {
5759
unsigned int size;
58-
auto buf = static_cast<unsigned char *>(JS_malloc(cx, buffer_size));
59-
if (!buf) {
60-
JS_ReportOutOfMemory(cx);
61-
return std::nullopt;
62-
}
63-
if (!EVP_Digest(data.data(), data.size(), buf, &size, algorithm, NULL)) {
60+
std::vector<uint8_t> buf(buffer_size, 0);
61+
if (!EVP_Digest(data.data(), data.size(), buf.data(), &size, algorithm, NULL)) {
6462
// 2. If performing the operation results in an error, then throw an OperationError.
6563
// TODO: Change to an OperationError DOMException
6664
JS_ReportErrorUTF8(cx, "SubtleCrypto.digest: failed to create digest");
67-
JS_free(cx, buf);
6865
return std::nullopt;
6966
}
70-
return std::span<uint8_t>(buf, size);
67+
return {std::move(buf)};
7168
};
7269

7370
// This implements https://w3c.github.io/webcrypto/#sha-operations for all
7471
// the SHA algorithms that we support.
7572
JSObject *digest(JSContext *cx, std::span<uint8_t> data, const EVP_MD *algorithm,
7673
size_t buffer_size) {
7774
unsigned int size;
78-
auto buf = static_cast<unsigned char *>(JS_malloc(cx, buffer_size));
75+
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> buf{
76+
static_cast<uint8_t *>(JS_malloc(cx, buffer_size))};
7977
if (!buf) {
8078
JS_ReportOutOfMemory(cx);
8179
return nullptr;
8280
}
83-
if (!EVP_Digest(data.data(), data.size(), buf, &size, algorithm, NULL)) {
81+
if (!EVP_Digest(data.data(), data.size(), buf.get(), &size, algorithm, NULL)) {
8482
// 2. If performing the operation results in an error, then throw an OperationError.
8583
// TODO: Change to an OperationError DOMException
8684
JS_ReportErrorUTF8(cx, "SubtleCrypto.digest: failed to create digest");
87-
JS_free(cx, buf);
8885
return nullptr;
8986
}
9087
// 3. Return a new ArrayBuffer containing result.
9188
JS::RootedObject array_buffer(cx);
92-
array_buffer.set(JS::NewArrayBufferWithContents(cx, size, buf));
89+
array_buffer.set(JS::NewArrayBufferWithContents(cx, size, buf.get()));
9390
if (!array_buffer) {
94-
JS_free(cx, buf);
9591
JS_ReportOutOfMemory(cx);
9692
return nullptr;
9793
}
94+
95+
// `array_buffer` now owns `buf`
96+
static_cast<void>(buf.release());
97+
9898
return array_buffer;
9999
};
100100

@@ -603,13 +603,12 @@ JSObject *CryptoAlgorithmRSASSA_PKCS1_v1_5_Sign_Verify::sign(JSContext *cx, JS::
603603
return nullptr;
604604
}
605605

606-
auto digestOption = ::builtins::rawDigest(cx, data, algorithm, EVP_MD_size(algorithm));
607-
if (!digestOption.has_value()) {
606+
auto digest = ::builtins::rawDigest(cx, data, algorithm, EVP_MD_size(algorithm));
607+
if (!digest.has_value()) {
608608
// TODO Rename error to OperationError
609609
JS_ReportErrorLatin1(cx, "OperationError");
610610
return nullptr;
611611
}
612-
auto digest = digestOption.value();
613612

614613
// 2. Perform the signature generation operation defined in Section 8.2 of [RFC3447] with the
615614
// key represented by the [[handle]] internal slot of key as the signer's private key and the
@@ -643,24 +642,23 @@ JSObject *CryptoAlgorithmRSASSA_PKCS1_v1_5_Sign_Verify::sign(JSContext *cx, JS::
643642
}
644643

645644
size_t signature_length;
646-
if (EVP_PKEY_sign(ctx, nullptr, &signature_length, digest.data(), digest.size()) <= 0) {
645+
if (EVP_PKEY_sign(ctx, nullptr, &signature_length, digest->data(), digest->size()) <= 0) {
647646
// TODO Rename error to OperationError
648647
JS_ReportErrorLatin1(cx, "OperationError");
649648
return nullptr;
650649
}
651650

652651
// 4. Let signature be the value S that results from performing the operation.
653-
uint8_t *signature = reinterpret_cast<uint8_t *>(JS_malloc(cx, signature_length));
654-
if (EVP_PKEY_sign(ctx, signature, &signature_length, digest.data(), digest.size()) <= 0) {
652+
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> signature{static_cast<uint8_t *>(JS_malloc(cx, signature_length))};
653+
if (EVP_PKEY_sign(ctx, signature.get(), &signature_length, digest->data(), digest->size()) <= 0) {
655654
// TODO Rename error to OperationError
656655
JS_ReportErrorLatin1(cx, "OperationError");
657-
JS_free(cx, signature);
658656
return nullptr;
659657
}
660658

661659
// 5. Return a new ArrayBuffer associated with the relevant global object of this [HTML], and
662660
// containing the bytes of signature.
663-
JS::RootedObject buffer(cx, JS::NewArrayBufferWithContents(cx, signature_length, signature));
661+
JS::RootedObject buffer(cx, JS::NewArrayBufferWithContents(cx, signature_length, signature.get()));
664662
if (!buffer) {
665663
// We can be here is the array buffer was too large -- if that was the case then a
666664
// JSMSG_BAD_ARRAY_LENGTH will have been created. No other failure scenarios in this path will
@@ -669,9 +667,12 @@ JSObject *CryptoAlgorithmRSASSA_PKCS1_v1_5_Sign_Verify::sign(JSContext *cx, JS::
669667
// TODO Rename error to InternalError
670668
JS_ReportErrorLatin1(cx, "InternalError");
671669
}
672-
JS_free(cx, signature);
673670
return nullptr;
674671
}
672+
673+
// `signature` is now owned by `buffer`
674+
static_cast<void>(signature.release());
675+
675676
return buffer;
676677
}
677678

@@ -1033,4 +1034,4 @@ JSObject *CryptoAlgorithmSHA512::digest(JSContext *cx, std::span<uint8_t> data)
10331034
return ::builtins::digest(cx, data, EVP_sha512(), SHA512_DIGEST_LENGTH);
10341035
}
10351036

1036-
} // namespace builtins
1037+
} // namespace builtins

runtime/js-compute-runtime/builtins/crypto-key.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,12 @@ JSObject *CryptoKey::createRSA(JSContext *cx, CryptoAlgorithmRSASSA_PKCS1_v1_5_I
495495
return nullptr;
496496
}
497497

498-
uint8_t *p = reinterpret_cast<uint8_t *>(calloc(keyData->exponent.size(), sizeof(uint8_t)));
498+
auto p = mozilla::MakeUnique<uint8_t[]>(keyData->exponent.size());
499499
auto exp = keyData->exponent;
500-
std::copy(exp.begin(), exp.end(), p);
500+
std::copy(exp.begin(), exp.end(), p.get());
501501

502-
JS::RootedObject buffer(cx, JS::NewArrayBufferWithContents(cx, keyData->exponent.size(), p));
502+
JS::RootedObject buffer(cx,
503+
JS::NewArrayBufferWithContents(cx, keyData->exponent.size(), p.get()));
503504
// `buffer` takes ownership of `p` if the call to NewArrayBufferWithContents was successful
504505
// if the call was not successful, we need to free `p` before exiting from the function.
505506
if (!buffer) {
@@ -510,10 +511,12 @@ JSObject *CryptoKey::createRSA(JSContext *cx, CryptoAlgorithmRSASSA_PKCS1_v1_5_I
510511
// TODO Rename error to InternalError
511512
JS_ReportErrorLatin1(cx, "InternalError");
512513
}
513-
JS_free(cx, p);
514514
return nullptr;
515515
}
516516

517+
// At this point, `buffer` owns the memory managed by `p`.
518+
static_cast<void>(p.release());
519+
517520
// Set the publicExponent attribute of algorithm to the BigInteger representation of the RSA
518521
// public exponent.
519522
JS::RootedObject byte_array(cx,

runtime/js-compute-runtime/builtins/request-response.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,11 @@ bool RequestOrResponse::parse_body(JSContext *cx, JS::HandleObject self, JS::Uni
380380
JS::RootedValue result(cx);
381381

382382
if constexpr (result_type == RequestOrResponse::BodyReadResult::ArrayBuffer) {
383-
auto *rawBuf = buf.release();
384-
JS::RootedObject array_buffer(cx, JS::NewArrayBufferWithContents(cx, len, rawBuf));
383+
JS::RootedObject array_buffer(cx, JS::NewArrayBufferWithContents(cx, len, buf.get()));
385384
if (!array_buffer) {
386-
JS_free(cx, rawBuf);
387385
return RejectPromiseWithPendingError(cx, result_promise);
388386
}
387+
static_cast<void>(buf.release());
389388
result.setObject(*array_buffer);
390389
} else {
391390
JS::RootedString text(cx, JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(buf.get(), len)));
@@ -505,7 +504,7 @@ bool RequestOrResponse::content_stream_read_then_handler(JSContext *cx, JS::Hand
505504
}
506505
}
507506

508-
auto buf = static_cast<char *>(JS_malloc(cx, buf_size + 1));
507+
JS::UniqueChars buf{static_cast<char *>(JS_malloc(cx, buf_size + 1))};
509508
if (!buf) {
510509
JS_ReportOutOfMemory(cx);
511510
return false;
@@ -516,7 +515,6 @@ bool RequestOrResponse::content_stream_read_then_handler(JSContext *cx, JS::Hand
516515
for (uint32_t index = 0; index < contentsLength; index++) {
517516
JS::RootedValue val(cx);
518517
if (!JS_GetElement(cx, contents, index, &val)) {
519-
JS_free(cx, buf);
520518
return false;
521519
}
522520
{
@@ -529,7 +527,7 @@ bool RequestOrResponse::content_stream_read_then_handler(JSContext *cx, JS::Hand
529527
if (length) {
530528
static_assert(CHAR_BIT == 8, "Strange char");
531529
auto bytes = reinterpret_cast<char *>(JS_GetUint8ArrayData(array, &is_shared, nogc));
532-
memcpy(buf + offset, bytes, length);
530+
memcpy(buf.get() + offset, bytes, length);
533531
offset += length;
534532
}
535533
}
@@ -538,20 +536,17 @@ bool RequestOrResponse::content_stream_read_then_handler(JSContext *cx, JS::Hand
538536
#ifdef DEBUG
539537
bool foundBodyParser;
540538
if (!JS_HasElement(cx, catch_handler, 2, &foundBodyParser)) {
541-
JS_free(cx, buf);
542539
return false;
543540
}
544541
MOZ_ASSERT(foundBodyParser);
545542
#endif
546543
// Now we can call parse_body on the result
547544
JS::RootedValue body_parser(cx);
548545
if (!JS_GetElement(cx, catch_handler, 2, &body_parser)) {
549-
JS_free(cx, buf);
550546
return false;
551547
}
552548
auto parse_body = (ParseBodyCB *)body_parser.toPrivate();
553-
JS::UniqueChars body(buf);
554-
return parse_body(cx, self, std::move(body), offset);
549+
return parse_body(cx, self, std::move(buf), offset);
555550
}
556551

557552
JS::RootedValue val(cx);

runtime/js-compute-runtime/builtins/shared/text-encoder.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ bool TextEncoder::encode(JSContext *cx, unsigned argc, JS::Value *vp) {
2929

3030
size_t chars_len;
3131
JS::UniqueChars chars = ::encode(cx, args[0], &chars_len);
32-
33-
auto *rawChars = chars.release();
34-
JS::RootedObject buffer(cx, JS::NewArrayBufferWithContents(cx, chars_len, rawChars));
32+
JS::RootedObject buffer(cx, JS::NewArrayBufferWithContents(cx, chars_len, chars.get()));
3533
if (!buffer) {
36-
JS_free(cx, rawChars);
3734
return false;
3835
}
3936

37+
// `buffer` now owns `chars`
38+
static_cast<void>(chars.release());
39+
4040
JS::RootedObject byte_array(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, chars_len));
4141
if (!byte_array) {
4242
return false;

runtime/js-compute-runtime/js-compute-builtins.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -788,17 +788,15 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
788788
if (!backend) {
789789
auto handle = builtins::Request::request_handle(request);
790790

791-
fastly_world_string_t uri_str;
792-
fastly_error_t err;
793-
if (fastly_http_req_uri_get(handle.handle, &uri_str, &err)) {
791+
auto res = handle.get_uri();
792+
if (auto *err = res.to_err()) {
793+
HANDLE_ERROR(cx, *err);
794+
} else {
794795
JS_ReportErrorLatin1(cx,
795796
"No backend specified for request with url %s. "
796797
"Must provide a `backend` property on the `init` object "
797798
"passed to either `new Request()` or `fetch`",
798-
uri_str.ptr);
799-
JS_free(cx, uri_str.ptr);
800-
} else {
801-
HANDLE_ERROR(cx, err);
799+
res.unwrap().begin());
802800
}
803801
return ReturnPromiseRejectedWithPendingError(cx, args);
804802
}

0 commit comments

Comments
 (0)