Skip to content

Conversation

@NelsonVides
Copy link
Contributor

The following snippet reads garbage memory and even often triggers segfaults. It barely ever happens with small Ns, but for around larger than 40 it is almost guaranteed to segfault in my machine.

generate_max_length_names(N) ->
    Label = <<<<$a>> || _ <- lists:seq(1, N)>>,
    <<Label/binary, Label/binary, Label/binary>>.

I found this while trying to write some tests for bencharking dns packet parsing logic. It did not trigger with only two labels, but with three it is enough. These labels trigger or fail to trigger too:

    BadLabel = <<<<$a>> || <<_>> <= <<1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1>> >>,
    GoodLabel = <<1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1>>,
    GoodLabel = iolist_to_binary(lists:duplicate(63, <<$a>>)),

My findings:

When the same binary variable is used multiple times in a single bs_create_bin instruction (e.g., <<Label/binary, Label/binary, Label/binary>>), the private_append optimization was incorrectly applied. This caused memory corruption because:

  1. The first segment uses private_append to destructively extend the binary
  2. The ErlSubBits structure is modified to reflect the new size
  3. Subsequent segments read this modified size, not the original
  4. The runtime copies more bytes than the original contained, reading uninitialized/garbage memory

The fix adds a check to ensure that before setting first_fragment_dies to true (which enables private_append), we verify the first fragment variable doesn't appear in any other segment of the instruction.

The following snippet reads garbage memory and even often triggers
segfaults. It barely ever happens with small Ns, but for around larger
than 40 it is almost guaranteed to segfault in my machine.

```erl
generate_max_length_names(N) ->
    Label = <<<<$a>> || _ <- lists:seq(1, N)>>,
    <<Label/binary, Label/binary, Label/binary>>.
```

I found this while trying to write some tests for bencharking dns packet
parsing logic. It did not trigger with only two labels, but with three
it is enough. These labels trigger or fail to trigger too:

```erl
    BadLabel = <<<<$a>> || <<_>> <= <<1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1>> >>,
    GoodLabel = <<1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1>>,
    GoodLabel = iolist_to_binary(lists:duplicate(63, <<$a>>)),
```

My findings:

When the same binary variable is used multiple times in a single
bs_create_bin instruction (e.g., `<<Label/binary, Label/binary,
Label/binary>>`), the `private_append` optimization was incorrectly
applied. This caused memory corruption because:

1. The first segment uses `private_append` to destructively extend
   the binary
2. The `ErlSubBits` structure is modified to reflect the new size
3. Subsequent segments read this modified size, not the original
4. The runtime copies more bytes than the original contained,
   reading uninitialized/garbage memory

The fix adds a check to ensure that before setting `first_fragment_dies`
to true (which enables `private_append`), we verify the first fragment
variable doesn't appear in any other segment of the instruction.
@github-actions
Copy link
Contributor

github-actions bot commented Jan 24, 2026

CT Test Results

    2 files    333 suites   8m 29s ⏱️
  856 tests   852 ✅ 4 💤 0 ❌
5 657 runs  5 653 ✅ 4 💤 0 ❌

Results for commit 466bd36.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

@bjorng bjorng added the team:VM Assigned to OTP team VM label Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team:VM Assigned to OTP team VM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants