Skip to content

chore(deps): update dependency protobuf to ~> 0.16 [security] - #39

Merged
toshi0806 merged 2 commits into
mainfrom
renovate/hex-protobuf-vulnerability
Aug 11, 2026
Merged

chore(deps): update dependency protobuf to ~> 0.16 [security]#39
toshi0806 merged 2 commits into
mainfrom
renovate/hex-protobuf-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Type Update Change
protobuf (source) prod minor ~> 0.15~> 0.16

Protobuf: Unbounded recursion depth in embedded-message decoding

CVE-2026-54451 / GHSA-rv48-qqj5-crxg

More information

Details

Summary

Unbounded recursion depth in Protobuf.Decoder (Hex package protobuf, versions >= 0.8.0, < 0.16.1) lets an unauthenticated attacker crash any service that decodes untrusted protobuf messages whose schema contains a self-referential or cyclic message type. A small request body (a few KB to a few MB) that nests an embedded field hundreds of thousands to millions of levels deep forces the BEAM to recurse once per level, exhausting memory and pinning a scheduler. A handful of such requests can take the node offline (a request-amplification denial of service).

Details

Protobuf.Decoder.value_for_field/3 handles embedded message fields in its embedded?: true branch at lib/protobuf/decoder.ex:218-243. For an embedded field it calls decode(bin, type) recursively, which re-enters build_message → handle_value → value_for_field. The recursive call is not in tail position (its result is consumed by the surrounding decode after it returns), so every nesting level retains a live frame on the process stack and heap.

There is no recursion-depth counter anywhere in the decoder. For any schema with a self-referential message type (e.g. message Tree { Tree child = 1; }, a common shape for comment threads, org charts, file trees, and ASTs) or any cycle of message types, the attacker controls the nesting depth entirely through the input bytes. Each additional level costs only a 1-byte field tag plus a varint length prefix, so depth grows roughly inversely with payload size: a tiny body buys an enormous recursion depth.

Reference protobuf implementations (Google's C++, Java, etc.) cap recursion at 100 specifically to prevent this. The Elixir decoder enforces no comparable bound, so the recursion continues until the process exhausts memory, blows the stack, or starves the scheduler doing GC over the deep structure.

The fix threads a depth counter through decode / build_message / handle_value / value_for_field (or holds it in the process dictionary for the duration of the top-level decode) and raises Protobuf.DecodeError once it exceeds a configurable limit, defaulting to 100 to match the reference implementations.

PoC
  1. Define a self-referential schema: defmodule Tree do use Protobuf, syntax: :proto3; field(:child, 1, type: Tree) end.
  2. Build a wire-format body inner-to-outer: at each of depth levels prepend <<0x0A, length_varint(inner_size), inner>> (tag 0x0A = field 1, wire type 2). Use an iolist with a running byte-size to keep generation O(depth).
  3. POST the body (a few MB at depth = 1_000_000) as application/x-protobuf to any endpoint that calls Tree.decode/1.
  4. The non-tail decode(bin, type) in value_for_field/3 re-enters once per nesting level, accumulating a frame per level. The decode burns seconds of CPU and hundreds of MB on the victim node; a few concurrent requests exhaust it.
Impact

Unauthenticated, network-reachable request-amplification denial of service against any service that decodes attacker-influenced protobuf bytes into a self-referential or cyclic message type. A single small request can consume seconds of CPU and hundreds of MB of memory on the victim; a few concurrent requests can take the node offline.

Resources

Severity

  • CVSS Score: 8.2 / 10 (High)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

References

This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).


Release Notes

elixir-protobuf/protobuf (protobuf)

v0.16.1

Compare Source

Security
  • Fix an unbounded-recursion denial of service when decoding deeply-nested embedded messages
    (GHSA-rv48-qqj5-crxg).
    Decoding now caps embedded-message nesting depth at 100 (matching the reference Protobuf
    implementations), raising Protobuf.DecodeError when the limit is exceeded. The limit can be
    overridden per call via the :max_nesting_depth option to Protobuf.decode/3.

v0.16.0

Compare Source

Enhancements
  • Add full_name/0 callback to Protobuf messages for retrieving the full Protobuf name.

    # For a message defined as:
    # package my.package;
    # message MyMessage { ... }
    
    MyPackage.MyMessage.full_name()
    #=> "my.package.MyMessage"
  • Add support for string arguments in the value/1 callback of enums.

    # For an enum defined as:
    # enum Status {
    #   UNKNOWN = 0;
    #   ACTIVE = 1;
    #   INACTIVE = 2;
    # }
    
    MyPackage.Status.value("ACTIVE")
    #=> 1
    MyPackage.Status.value("INACTIVE")
    #=> 2
  • Add Protobuf.Any.pack/1.

  • Add Protobuf.field_presence/2.

  • Add deprecation warning when casting a non-struct enumerable to a Protobuf struct.


Configuration

📅 Schedule: (in timezone Asia/Tokyo)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate

renovate Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

⚠️ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: mix.lock
Command failed: install-tool elixir v1.20.3

Renovate's artifact update failed on this branch, so mix.lock stayed
pinned to protobuf 0.15.0 while mix.exs was bumped to ~> 0.16. The lock
was both inconsistent with mix.exs and still on a version covered by
GHSA-rv48-qqj5-crxg (>= 0.8.0, < 0.16.1).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

セキュリティ修正(CVE-2026-54451)に対応する依存更新として妥当です。1点、バージョン整合性について確認をお願いします。

Comment thread mix.exs
# Required dependencies
{:gen_stage, "~> 1.2"},
{:protobuf, "~> 0.15"},
{:protobuf, "~> 0.16"},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [MEDIUM] mix.exsでは ~> 0.16 を指定していますが、mix.lockでは実際に 0.17.0 が解決されています。~> 0.16>= 0.16.0 and < 1.0.0 を許容するためロック自体は矛盾しませんが、このPRの目的(CVE-2026-54451対策で必要な最小バージョンは 0.16.1)に対して 0.17.0 が入っている点を意図通りか確認してください。もしメジャー更新(0.17系)に破壊的変更が含まれる場合は、テストが通ることを確認のうえマージすることを推奨します。

@toshi0806 toshi0806 Aug 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘のとおり mix.exs~> 0.16 のまま、mix.lock は 0.17.0 です。意図通りなので、根拠を残して据え置きます。

なぜ 0.17.0 なのか

そもそもこのブランチは Renovate の artifact 更新が失敗しており(本 PR の Renovate コメント参照。原因は Command failed: install-tool elixir v1.20.3)、mix.lockprotobuf 0.15.0 のままでした。つまり merge しても advisory (>= 0.8.0, < 0.16.1) の範囲内に留まり、脆弱性が解消されない状態でした。そこで mix deps.update protobuf で lock を再生成したところ、~> 0.16 を満たす最新版として 0.17.0 が解決されました。

0.16.1 に固定する選択肢もありますが、それには制約を ~> 0.16.1(= >= 0.16.1 and < 0.17.0)に狭める必要があり、以後の 0.17 系更新を塞ぐことになるため採りませんでした。

破壊的変更の確認

ご指摘の「テストが通ることを確認のうえマージ」は実施済みです。

  • mix test: 120 passed (3 doctests / 117 tests)、Encoder は行カバレッジ 100%
  • CI: OTP 27.3.4.4 / Elixir 1.17.3 と OTP 29.0.2 / Elixir 1.20.1 の両マトリクスで pass
  • mix compile --force --warnings-as-errors: 本体に警告なし
  • mix credo --strict: no issues
  • mix hex.audit: 警告なし

生成コード lib/dnstap.pb.ex も再生成なしでそのまま通っており、本ライブラリが使う範囲での API 互換性は確認できています。

advisory は CVE-2026-54451 / GHSA-rv48-qqj5-crxg(CVSS 8.2 High)で、ご指摘の CVE 番号で正しいです。修正内容は embedded message のネスト深さを 100 で打ち切るもので、0.17.0 にも含まれています。

@toshi0806
toshi0806 merged commit 3cc5f5c into main Aug 11, 2026
6 checks passed
@toshi0806
toshi0806 deleted the renovate/hex-protobuf-vulnerability branch August 11, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant