Skip to content

Commit f30ff07

Browse files
committed
Merge pull request #1495 from bettio/backport-unaligned-strings
Backport unaligned matching of strings (OTP < 26) Just a backport of #1492 These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents a60a679 + b3f2e3a commit f30ff07

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
@@ -42,6 +42,7 @@ integers
4242
- Fixed crash calling network:sta_rssi(), when network not up.
4343
- Fix error handling when calling `min` and `max` with code compiled before OTP-26: there was a
4444
bug when handling errors from BIFs used as NIFs (when called with `CALL_EXT` and similar opcodes)`
45+
- Fix matching of binaries on unaligned boundaries for code compiled with older versions of OTP
4546

4647
## [0.6.5] - 2024-10-15
4748

src/libAtomVM/opcodesswitch.h

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

4724-
if (bits % 8 != 0) {
4725-
TRACE("bs_match_string: Unsupported bits size (must be evenly divisible by 8). bits=%u\n", (unsigned) bits);
4726-
RAISE_ERROR(UNSUPPORTED_ATOM);
4727-
}
4728-
avm_int_t bytes = bits / 8;
47294724
avm_int_t bs_offset = term_get_match_state_offset(src);
47304725
term bs_bin = term_get_match_state_binary(src);
47314726

4732-
if (bs_offset % 8 != 0) {
4733-
TRACE("bs_match_string: Unsupported offset (must be evenly divisible by 8). bs_offset=%li\n", bs_offset);
4734-
RAISE_ERROR(UNSUPPORTED_ATOM);
4735-
}
4736-
avm_int_t byte_offset = bs_offset / 8;
4737-
4738-
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4739-
47404727
size_t remaining = 0;
47414728
const uint8_t *str = module_get_str(mod, offset, &remaining);
47424729
if (IS_NULL_PTR(str)) {
47434730
TRACE("bs_match_string: Bad offset in strings table.\n");
47444731
RAISE_ERROR(BADARG_ATOM);
47454732
}
4746-
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4747-
TRACE("bs_match_string: failed to match\n");
4748-
JUMP_TO_ADDRESS(mod->labels[fail]);
4733+
4734+
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4735+
4736+
if (bits % 8 == 0 && bs_offset % 8 == 0) {
4737+
avm_int_t bytes = bits / 8;
4738+
avm_int_t byte_offset = bs_offset / 8;
4739+
4740+
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4741+
TRACE("bs_match_string: failed to match\n");
4742+
JUMP_TO_ADDRESS(mod->labels[fail]);
4743+
}
47494744
} else {
4750-
term_set_match_state_offset(src, bs_offset + bits);
4745+
// Compare unaligned bits
4746+
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
4747+
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
4748+
uint8_t str_bit_offset = 7;
4749+
size_t remaining_bits = bits;
4750+
while (remaining_bits > 0) {
4751+
uint8_t str_ch = *str;
4752+
uint8_t bin_ch = *bs_str;
4753+
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
4754+
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
4755+
if (str_ch_bit ^ bin_ch_bit) {
4756+
TRACE("bs_match_string: failed to match\n");
4757+
JUMP_TO_ADDRESS(mod->labels[fail]);
4758+
}
4759+
if (str_bit_offset) {
4760+
str_bit_offset--;
4761+
} else {
4762+
str_bit_offset = 7;
4763+
str++;
4764+
}
4765+
if (bin_bit_offset) {
4766+
bin_bit_offset--;
4767+
} else {
4768+
bin_bit_offset = 7;
4769+
bs_str++;
4770+
}
4771+
remaining_bits--;
4772+
}
47514773
}
4774+
term_set_match_state_offset(src, bs_offset + bits);
47524775
#endif
47534776
break;
47544777
}

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)