Skip to content

Commit

Permalink
Merge pull request #1495 from bettio/backport-unaligned-strings
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bettio committed Feb 11, 2025
2 parents a60a679 + b3f2e3a commit f30ff07
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 20 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/build-and-test-other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ jobs:

- arch: "arm64v8"
platform: "arm64/v8"
tag: "bullseye"
tag: "bookworm"
cflags: "-O2"
cmake_opts: "-DAVM_WARNINGS_ARE_ERRORS=ON"

# Required for testing big endian archs
- arch: "s390x"
platform: "s390x"
tag: "bullseye"
tag: "bookworm"
cflags: "-O2"
cmake_opts: "-DAVM_WARNINGS_ARE_ERRORS=ON"

Expand Down Expand Up @@ -139,7 +139,8 @@ jobs:
-e CFLAGS="${{ matrix.cflags }}" -e CXXFLAGS="${{ matrix.cflags }}" \
${{ matrix.arch }}/debian:${{ matrix.tag }} /bin/bash -c '
([ -n "${{ matrix.sources }}" ] && echo "${{ matrix.sources }}" > /etc/apt/sources.list || true) &&
cat /etc/apt/sources.list &&
cat /etc/apt/sources.list || true &&
cat /etc/apt/sources.list.d/* || true &&
if test -n "${{ matrix.install_deps }}"; then
echo
${{ matrix.install_deps }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ integers
- Fixed crash calling network:sta_rssi(), when network not up.
- Fix error handling when calling `min` and `max` with code compiled before OTP-26: there was a
bug when handling errors from BIFs used as NIFs (when called with `CALL_EXT` and similar opcodes)`
- Fix matching of binaries on unaligned boundaries for code compiled with older versions of OTP

## [0.6.5] - 2024-10-15

Expand Down
57 changes: 40 additions & 17 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -4721,34 +4721,57 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
#ifdef IMPL_EXECUTE_LOOP
VERIFY_IS_MATCH_STATE(src, "bs_match_string");

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

if (bs_offset % 8 != 0) {
TRACE("bs_match_string: Unsupported offset (must be evenly divisible by 8). bs_offset=%li\n", bs_offset);
RAISE_ERROR(UNSUPPORTED_ATOM);
}
avm_int_t byte_offset = bs_offset / 8;

TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);

size_t remaining = 0;
const uint8_t *str = module_get_str(mod, offset, &remaining);
if (IS_NULL_PTR(str)) {
TRACE("bs_match_string: Bad offset in strings table.\n");
RAISE_ERROR(BADARG_ATOM);
}
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
TRACE("bs_match_string: failed to match\n");
JUMP_TO_ADDRESS(mod->labels[fail]);

TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);

if (bits % 8 == 0 && bs_offset % 8 == 0) {
avm_int_t bytes = bits / 8;
avm_int_t byte_offset = bs_offset / 8;

if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
TRACE("bs_match_string: failed to match\n");
JUMP_TO_ADDRESS(mod->labels[fail]);
}
} else {
term_set_match_state_offset(src, bs_offset + bits);
// Compare unaligned bits
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
uint8_t str_bit_offset = 7;
size_t remaining_bits = bits;
while (remaining_bits > 0) {
uint8_t str_ch = *str;
uint8_t bin_ch = *bs_str;
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
if (str_ch_bit ^ bin_ch_bit) {
TRACE("bs_match_string: failed to match\n");
JUMP_TO_ADDRESS(mod->labels[fail]);
}
if (str_bit_offset) {
str_bit_offset--;
} else {
str_bit_offset = 7;
str++;
}
if (bin_bit_offset) {
bin_bit_offset--;
} else {
bin_bit_offset = 7;
bs_str++;
}
remaining_bits--;
}
}
term_set_match_state_offset(src, bs_offset + bits);
#endif
break;
}
Expand Down
9 changes: 9 additions & 0 deletions tests/erlang_tests/test_bs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ start() ->

test_put_match_string(<<"foo">>, <<"bar">>),
test_skip_bits(),
ok = test_bs_match_string_unaligned(),

test_match_case_type(),

Expand Down Expand Up @@ -329,6 +330,14 @@ skip_bits(Len, Bin) ->
<<_First:Len, Rest/binary>> = Bin,
Rest.

test_bs_match_string_unaligned() ->
<<0:1, _:3, 42:7, _:5, 42>> = id(<<0:3, 42, 0:5, 42>>),
<<0:1, _:3, 42:12, _:8, 42>> = id(<<0, 42, 0, 42>>),
ok = expect_error(
fun() -> <<0:1, _:4, 42:12, 0:7>> = id(<<0:5, 42, 0:3>>) end, {badmatch, <<1, 80>>}
),
ok.

test_match_case_type() ->
foo = match_case_type([foo, bar]),
$a = match_case_type(<<"abc">>),
Expand Down

0 comments on commit f30ff07

Please sign in to comment.