Skip to content

Commit b3f2e3a

Browse files
committed
Backport unaligned matching of strings (OTP < 26)
This commit backports dffbd53 into release-0.6 the following fix that has been already applied to main: Fix unaligned matching of strings for code compiled with OTP < 26 OTP 25 compiler generates bs_match_string, and AtomVM wouldn't support some unaligned matching that worked when compiled by OTP 26+. Also bump s390x and arm64 CI to use bookworm as cc from bullseye crashes on our code. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 2ac2041 commit b3f2e3a

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

.github/workflows/build-and-test-other.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,14 @@ jobs:
102102

103103
- arch: "arm64v8"
104104
platform: "arm64/v8"
105-
tag: "bullseye"
105+
tag: "bookworm"
106106
cflags: "-O2"
107107
cmake_opts: "-DAVM_WARNINGS_ARE_ERRORS=ON"
108108

109109
# Required for testing big endian archs
110110
- arch: "s390x"
111111
platform: "s390x"
112-
tag: "bullseye"
112+
tag: "bookworm"
113113
cflags: "-O2"
114114
cmake_opts: "-DAVM_WARNINGS_ARE_ERRORS=ON"
115115

@@ -139,7 +139,8 @@ jobs:
139139
-e CFLAGS="${{ matrix.cflags }}" -e CXXFLAGS="${{ matrix.cflags }}" \
140140
${{ matrix.arch }}/debian:${{ matrix.tag }} /bin/bash -c '
141141
([ -n "${{ matrix.sources }}" ] && echo "${{ matrix.sources }}" > /etc/apt/sources.list || true) &&
142-
cat /etc/apt/sources.list &&
142+
cat /etc/apt/sources.list || true &&
143+
cat /etc/apt/sources.list.d/* || true &&
143144
if test -n "${{ matrix.install_deps }}"; then
144145
echo
145146
${{ matrix.install_deps }}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ integers
4040
- Fixed support for setting esp32 boot_path in NVS.
4141
- Fixed race conditions in network:start/stop.
4242
- Fixed crash calling network:sta_rssi(), when network not up.
43+
- Fix matching of binaries on unaligned boundaries for code compiled with older versions of OTP
4344

4445
## [0.6.5] - 2024-10-15
4546

src/libAtomVM/opcodesswitch.h

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4709,34 +4709,57 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
47094709
#ifdef IMPL_EXECUTE_LOOP
47104710
VERIFY_IS_MATCH_STATE(src, "bs_match_string");
47114711

4712-
if (bits % 8 != 0) {
4713-
TRACE("bs_match_string: Unsupported bits size (must be evenly divisible by 8). bits=%u\n", (unsigned) bits);
4714-
RAISE_ERROR(UNSUPPORTED_ATOM);
4715-
}
4716-
avm_int_t bytes = bits / 8;
47174712
avm_int_t bs_offset = term_get_match_state_offset(src);
47184713
term bs_bin = term_get_match_state_binary(src);
47194714

4720-
if (bs_offset % 8 != 0) {
4721-
TRACE("bs_match_string: Unsupported offset (must be evenly divisible by 8). bs_offset=%li\n", bs_offset);
4722-
RAISE_ERROR(UNSUPPORTED_ATOM);
4723-
}
4724-
avm_int_t byte_offset = bs_offset / 8;
4725-
4726-
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4727-
47284715
size_t remaining = 0;
47294716
const uint8_t *str = module_get_str(mod, offset, &remaining);
47304717
if (IS_NULL_PTR(str)) {
47314718
TRACE("bs_match_string: Bad offset in strings table.\n");
47324719
RAISE_ERROR(BADARG_ATOM);
47334720
}
4734-
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4735-
TRACE("bs_match_string: failed to match\n");
4736-
JUMP_TO_ADDRESS(mod->labels[fail]);
4721+
4722+
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4723+
4724+
if (bits % 8 == 0 && bs_offset % 8 == 0) {
4725+
avm_int_t bytes = bits / 8;
4726+
avm_int_t byte_offset = bs_offset / 8;
4727+
4728+
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4729+
TRACE("bs_match_string: failed to match\n");
4730+
JUMP_TO_ADDRESS(mod->labels[fail]);
4731+
}
47374732
} else {
4738-
term_set_match_state_offset(src, bs_offset + bits);
4733+
// Compare unaligned bits
4734+
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
4735+
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
4736+
uint8_t str_bit_offset = 7;
4737+
size_t remaining_bits = bits;
4738+
while (remaining_bits > 0) {
4739+
uint8_t str_ch = *str;
4740+
uint8_t bin_ch = *bs_str;
4741+
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
4742+
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
4743+
if (str_ch_bit ^ bin_ch_bit) {
4744+
TRACE("bs_match_string: failed to match\n");
4745+
JUMP_TO_ADDRESS(mod->labels[fail]);
4746+
}
4747+
if (str_bit_offset) {
4748+
str_bit_offset--;
4749+
} else {
4750+
str_bit_offset = 7;
4751+
str++;
4752+
}
4753+
if (bin_bit_offset) {
4754+
bin_bit_offset--;
4755+
} else {
4756+
bin_bit_offset = 7;
4757+
bs_str++;
4758+
}
4759+
remaining_bits--;
4760+
}
47394761
}
4762+
term_set_match_state_offset(src, bs_offset + bits);
47404763
#endif
47414764
break;
47424765
}

tests/erlang_tests/test_bs.erl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ start() ->
8686

8787
test_put_match_string(<<"foo">>, <<"bar">>),
8888
test_skip_bits(),
89+
ok = test_bs_match_string_unaligned(),
8990

9091
test_match_case_type(),
9192

@@ -329,6 +330,14 @@ skip_bits(Len, Bin) ->
329330
<<_First:Len, Rest/binary>> = Bin,
330331
Rest.
331332

333+
test_bs_match_string_unaligned() ->
334+
<<0:1, _:3, 42:7, _:5, 42>> = id(<<0:3, 42, 0:5, 42>>),
335+
<<0:1, _:3, 42:12, _:8, 42>> = id(<<0, 42, 0, 42>>),
336+
ok = expect_error(
337+
fun() -> <<0:1, _:4, 42:12, 0:7>> = id(<<0:5, 42, 0:3>>) end, {badmatch, <<1, 80>>}
338+
),
339+
ok.
340+
332341
test_match_case_type() ->
333342
foo = match_case_type([foo, bar]),
334343
$a = match_case_type(<<"abc">>),

0 commit comments

Comments
 (0)