aboutsummaryrefslogtreecommitdiff
path: root/src/common/zstd_compression.cpp
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-11-03 01:39:54 +0100
committercirakg <cirakg@cirakg.xyz>2026-11-03 01:39:54 +0100
commit0b927d171c6d918a675c25f57b2bc0121b7f889d (patch)
tree3f3e8ffd7106f05865ee1217f6cbaa68670e229a /src/common/zstd_compression.cpp
parent70934ece3d2253c7579cf8d71b13e3a0547070e2 (diff)
network: close each bsd fd once and drop unwanted or malformed room packets
Two threads closing the same fd both copied the descriptor and both called Close(), so a host socket could be closed twice, and the second closesocket could hit an unrelated socket that had reused the host fd. CloseImpl now frees the slot under file_descriptors_mutex first and only that thread closes the socket; the other gets EBADF. OnProxyPacketReceived decompressed every room packet when any fd had a socket, including native sockets and proxy sockets on other ports. Sockets now report through AcceptsProxyPacket whether they want a packet, and only those are collected, so an unwanted packet is not decompressed. DecompressDataZSTD passed the frame's declared size straight to the vector constructor, so a malformed or unsized frame from a room member threw on the room thread. The new TryDecompressDataZSTD rejects invalid, unsized and oversized frames, and bsd drops such packets with a warning. DecompressDataZSTD now returns an empty vector for them instead of throwing. Assisted-by: Claude Opus 5.5 (investigation and implementation)
Diffstat (limited to 'src/common/zstd_compression.cpp')
-rw-r--r--src/common/zstd_compression.cpp20
1 files changed, 15 insertions, 5 deletions
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 7665482ee6..9bb8d7b919 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -35,16 +35,26 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
}
std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) {
- const std::size_t decompressed_size =
+ return TryDecompressDataZSTD(compressed, std::vector<u8>{}.max_size())
+ .value_or(std::vector<u8>{});
+}
+
+std::optional<std::vector<u8>> TryDecompressDataZSTD(std::span<const u8> compressed,
+ std::size_t max_size) {
+ // The size comes from the frame header, so check it before allocating
+ const unsigned long long decompressed_size =
ZSTD_getFrameContentSize(compressed.data(), compressed.size());
- std::vector<u8> decompressed(decompressed_size);
+ if (decompressed_size == ZSTD_CONTENTSIZE_ERROR ||
+ decompressed_size == ZSTD_CONTENTSIZE_UNKNOWN || decompressed_size > max_size) {
+ return std::nullopt;
+ }
+ std::vector<u8> decompressed(static_cast<std::size_t>(decompressed_size));
const std::size_t uncompressed_result_size = ZSTD_decompress(
decompressed.data(), decompressed.size(), compressed.data(), compressed.size());
- if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) {
- // Decompression failed
- return {};
+ if (ZSTD_isError(uncompressed_result_size) || uncompressed_result_size != decompressed_size) {
+ return std::nullopt;
}
return decompressed;
}