Skip to content

Commit

Permalink
erts: Fix crash in ETS compressed tables
Browse files Browse the repository at this point in the history
The compression was a bit too good, converting ErlSubBits to
ErlHeapBits whenever that resulted in a smaller result, breaking
an assumption that ETS compressed tables made:

    size_object(original) == size_object(decompressed)

This commit fixes it by not being as clever, naively encoding
ErlSubBits and the likes as-is regardless of size.
  • Loading branch information
jhogberg committed Jul 23, 2024
1 parent 601a012 commit 9639403
Showing 1 changed file with 22 additions and 28 deletions.
50 changes: 22 additions & 28 deletions erts/emulator/beam/external.c
Original file line number Diff line number Diff line change
Expand Up @@ -3853,21 +3853,19 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,

ASSERT(!use_iov);

/* Use [BIT_]BINARY_INTERNAL_REF, copying the actual BinRef
* and/or ErlSubBits whenever that is smaller than the data
* itself. */
if (wire_size >= sizeof(BinRef)) {
if ((encoding == BINARY_EXT) &&
(base == (byte*)refc_binary->orig_bytes) &&
(size == refc_binary->orig_size * 8) &&
(offset == 0)) {
encoding = BINARY_INTERNAL_REF;
copy_payload = 0;
} else if (wire_size >= (sizeof(ErlSubBits) +
sizeof(BinRef))) {
encoding = BITSTRING_INTERNAL_REF;
copy_payload = 0;
}
/* Always use [BIT_]BINARY_INTERNAL_REF: this may lead to a
* larger result than copying the payload, but ensures that
* the decoded object is exactly the same as the encoded
* one, simplifying the decompression logic in ETS. */
if ((encoding == BINARY_EXT) &&
(base == (byte*)refc_binary->orig_bytes) &&
(size == refc_binary->orig_size * 8) &&
(offset == 0)) {
encoding = BINARY_INTERNAL_REF;
copy_payload = 0;
} else {
encoding = BITSTRING_INTERNAL_REF;
copy_payload = 0;
}
}

Expand Down Expand Up @@ -5606,19 +5604,15 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,

ASSERT(vlen < 0);

/* Use [BIT_]BINARY_INTERNAL_REF, copying the actual BinRef
* and/or ErlSubBits whenever that is smaller than the data
* itself. */
if (wire_size >= sizeof(BinRef)) {
if ((encoding == BINARY_EXT) &&
(base == (byte*)refc_binary->orig_bytes) &&
(size == refc_binary->orig_size * 8) &&
(offset == 0)) {
encoding = BINARY_INTERNAL_REF;
} else if (wire_size >= (sizeof(ErlSubBits) +
sizeof(BinRef))) {
encoding = BITSTRING_INTERNAL_REF;
}
/* Always use [BIT_]BINARY_INTERNAL_REF: see matching comment
* in enc_term_int. */
if ((encoding == BINARY_EXT) &&
(base == (byte*)refc_binary->orig_bytes) &&
(size == refc_binary->orig_size * 8) &&
(offset == 0)) {
encoding = BINARY_INTERNAL_REF;
} else {
encoding = BITSTRING_INTERNAL_REF;
}
}

Expand Down

0 comments on commit 9639403

Please sign in to comment.