Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions src/workerd/api/node/zlib-util.c++
Original file line number Diff line number Diff line change
Expand Up @@ -812,10 +812,19 @@ void zstdFreeDCtx(ZSTD_DCtx* dctx) {
} // namespace

ZstdEncoderContext::ZstdEncoderContext(ZlibMode _mode)
: ZstdContext(_mode),
cctx_(kj::disposeWith<zstdFreeCCtx>(ZSTD_createCCtx())) {}
: ZstdContext(_mode) {}

kj::Maybe<CompressionError> ZstdEncoderContext::initialize(uint64_t pledgedSrcSize) {
KJ_DASSERT(allocator_ != nullptr, "Zstd encoder allocator should not be null");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KJ_DASSERT is stripped in release builds. If allocator_ is ever null here (e.g. a future caller forgets setAllocator), this will silently pass through and crash inside the ZSTD allocation callback when it dereferences the null opaque pointer. Use KJ_ASSERT so the invariant is enforced in all build modes:

Suggested change
KJ_DASSERT(allocator_ != nullptr, "Zstd encoder allocator should not be null");
KJ_ASSERT(allocator_ != nullptr, "Zstd encoder allocator should not be null");

if (cctx_.get() == nullptr) {
ZSTD_customMem customMem = {
.customAlloc = CompressionAllocator::AllocForBrotli,
.customFree = CompressionAllocator::FreeForZlib,
.opaque = allocator_,
};
cctx_ = kj::disposeWith<zstdFreeCCtx>(ZSTD_createCCtx_advanced(customMem));
}

if (cctx_.get() == nullptr) {
return CompressionError(
"Could not initialize Zstd instance"_kj, "ERR_ZLIB_INITIALIZATION_FAILED"_kj, -1);
Expand Down Expand Up @@ -878,12 +887,19 @@ kj::Maybe<CompressionError> ZstdEncoderContext::getError() const {
}

ZstdDecoderContext::ZstdDecoderContext(ZlibMode _mode)
: ZstdContext(_mode),
dctx_(kj::disposeWith<zstdFreeDCtx>(ZSTD_createDCtx())) {}
: ZstdContext(_mode) {}

kj::Maybe<CompressionError> ZstdDecoderContext::initialize() {
// dctx_ is created in the constructor. It can only be nullptr if ZSTD_createDCtx()
// failed due to memory allocation failure.
KJ_DASSERT(allocator_ != nullptr, "Zstd decoder allocator should not be null");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the encoder path — KJ_DASSERT is a no-op in release builds:

Suggested change
KJ_DASSERT(allocator_ != nullptr, "Zstd decoder allocator should not be null");
KJ_ASSERT(allocator_ != nullptr, "Zstd decoder allocator should not be null");

if (dctx_.get() == nullptr) {
ZSTD_customMem customMem = {
.customAlloc = CompressionAllocator::AllocForBrotli,
.customFree = CompressionAllocator::FreeForZlib,
.opaque = allocator_,
};
dctx_ = kj::disposeWith<zstdFreeDCtx>(ZSTD_createDCtx_advanced(customMem));
}

if (dctx_.get() == nullptr) {
return CompressionError(
"Could not initialize Zstd instance"_kj, "ERR_ZLIB_INITIALIZATION_FAILED"_kj, -1);
Expand Down Expand Up @@ -957,6 +973,7 @@ bool ZlibUtil::ZstdCompressionStream<CompressionContext>::initialize(jsg::Lock&
jsg::Function<void()> writeCallback,
jsg::Optional<uint64_t> pledgedSrcSize) {
this->initializeStream(kj::mv(writeResult), kj::mv(writeCallback));
this->context()->setAllocator(&this->allocator);

uint64_t srcSize = pledgedSrcSize.orDefault(ZSTD_CONTENTSIZE_UNKNOWN);

Expand Down Expand Up @@ -1168,7 +1185,9 @@ void ZlibUtil::brotliWithCallback(

template <typename Context>
kj::Array<kj::byte> ZlibUtil::zstdSync(jsg::Lock& js, InputSource data, ZstdContext::Options opts) {
CompressionAllocator allocator(js.getExternalMemoryTarget());
Context ctx(Context::Mode);
ctx.setAllocator(&allocator);

auto chunkSize = opts.chunkSize.orDefault(ZLIB_PERFORMANT_CHUNK_SIZE);
auto maxOutputLength = opts.maxOutputLength.orDefault(Z_MAX_CHUNK);
Expand Down
5 changes: 5 additions & 0 deletions src/workerd/api/node/zlib-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ class ZstdContext {
explicit ZstdContext(ZlibMode _mode): mode(_mode) {}
KJ_DISALLOW_COPY(ZstdContext);

void setAllocator(CompressionAllocator* allocator) {
allocator_ = allocator;
}

void setBuffers(kj::ArrayPtr<kj::byte> input, kj::ArrayPtr<kj::byte> output);
void setInputBuffer(kj::ArrayPtr<const kj::byte> input);
void setOutputBuffer(kj::ArrayPtr<kj::byte> output);
Expand All @@ -313,6 +317,7 @@ class ZstdContext {
};

protected:
CompressionAllocator* allocator_ = nullptr;
ZlibMode mode;
ZSTD_inBuffer input_{nullptr, 0, 0};
ZSTD_outBuffer output_{nullptr, 0, 0};
Expand Down
Loading