Skip to content

Fix ADNL inbound peer limiter poisoning - #2483

Open
AndreyMlashkin wants to merge 6 commits into
ton-blockchain:masterfrom
AndreyMlashkin:fix_2373_pr
Open

Fix ADNL inbound peer limiter poisoning#2483
AndreyMlashkin wants to merge 6 commits into
ton-blockchain:masterfrom
AndreyMlashkin:fix_2373_pr

Conversation

@AndreyMlashkin

@AndreyMlashkin AndreyMlashkin commented Jul 19, 2026

Copy link
Copy Markdown

Summary

  • Defer adding inbound peer IDs until packet authentication succeeds.
  • Preserve the per-IP admission limit for already-known peer IDs.
  • Apply the same protection to authenticated channel packets.

The inbound limiter previously recorded packet.from_short() before signature verification. An attacker could therefore fill the 60-peer set for an IP with forged IDs and block legitimate peers. This change moves the set update after authentication while retaining the existing admission check.

Fixes #2373

@AndreyMlashkin
AndreyMlashkin marked this pull request as ready for review July 19, 2026 18:03

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95b271271e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adnl/adnl-peer.cpp Outdated
td::actor::send_closure(actor_id(this), &AdnlPeerPairImpl::send_messages_from_queue);
}
}
td::actor::send_closure(local_actor_, &AdnlLocalId::add_inbound_peer, packet.remote_addr(), packet.from_short());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use the actual peer id for channel limiter entries

When normal traffic is sent over an ADNL channel, the sender does not populate from_short (set_source is only called in the !via_channel path), and AdnlChannelImpl::decrypt explicitly permits packets without a source. This new call therefore usually records the default/empty node id instead of the authenticated channel peer, which consumes one slot in recent_inbound_peers; after any channel traffic from an IP, the limiter can admit only 59 real peer IDs and may reject the 60th legitimate peer behind the same IP. Use the peer pair's known peer id for channel packets, or require/populate a source before recording it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 6789902. Channel packets now register the peer pair known peer_id_short_ instead of packet.from_short(), which may be unset for channel traffic. test-adnl passes.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 678990284f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adnl/adnl-local-id.cpp
}
}

td::actor::send_closure(peer_table_, &AdnlPeerTable::receive_decrypted_packet, short_id_, std::move(packet),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep limiting unknown peers before peer-table allocation

When the set is below UNIQUE_PEERS_PER_IP_LIMIT, this now forwards every decrypted packet without recording its source until after AdnlPeerPairImpl::receive_packet verifies the signature. However AdnlPeerTableImpl::receive_decrypted_packet creates PeerInfo/a peer pair for an unknown full from before that signature check, so one IP can stream packets with fresh full source keys and bad signatures; none are added to recent_inbound_peers, the unique-peer limiter never fills, and the peer table still allocates actors until idle GC. Please keep some pre-auth admission record for unknown full sources or move authentication ahead of peer creation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 148d431. Unknown full-source packets are now signature-checked before PeerInfo/AdnlPeerPair allocation, so forged sources cannot create actors before authentication. test-adnl passes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shahramzarey 3

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 148d431d4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adnl/adnl-peer.cpp Outdated
return;
}

td::actor::send_closure(local_actor_, &AdnlLocalId::add_inbound_peer, packet.remote_addr(), packet.from_short());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce the unique-peer cap before delivery

When the set is just below UNIQUE_PEERS_PER_IP_LIMIT and multiple authenticated non-channel peers from the same IP arrive in a burst, this send_closure only queues the accounting back to the local actor and receive_packet_checked delivers the packet immediately; all packets that passed the earlier receive_coro check before these callbacks run can be accepted, and later add_inbound_peer just drops the excess once the set is full. This regresses the old synchronous cap, so reserve/record the peer, or ask the local actor for admission, before delivering.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks, this is still outstanding. The limiter update is currently asynchronous, so a burst can pass the pre-check before the accounting callbacks run. I’ll add admission reservation before packet delivery.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Should be fixed now

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0b0a680f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adnl/adnl-local-id.cpp
}

void AdnlLocalId::add_inbound_peer(td::IPAddress addr, AdnlNodeIdShort peer, td::Promise<td::Unit> promise) {
auto &rate_limiter = inbound_rate_limiter_[remove_port(addr)];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Schedule cleanup for channel-created limiter entries

When add_inbound_peer is first reached from the channel path for an address that has never gone through receive_coro, this operator[] creates a new inbound_rate_limiter_ entry, but this path only arms cleanup_recent_inbound_peers_at_; cleanup_rate_limiter_at_ is armed only in receive_coro, and alarm() only erases empty/full map entries from that cleanup branch. After the 60-second peer set is cleared, the per-IP entry therefore remains indefinitely, so a peer with a valid channel can send packets from many source IPs and leak limiter entries. Arm the rate-limiter cleanup here too.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 49b2a3d. add_inbound_peer now arms cleanup_rate_limiter_at_, including for channel traffic that bypasses receive_coro. Channel-created entries are therefore removed after their peer set is cleared and the limiter becomes eligible for cleanup. test-adnl passes.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49b2a3dd21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adnl/adnl-peer-table.cpp Outdated
Comment on lines +151 to +155
auto S = R.move_as_ok()->check_signature(packet.to_sign().as_slice(), packet.signature().as_slice());
if (S.is_error()) {
VLOG(adnl, INFO) << this << ": dropping IN message [" << packet.from_short() << "->" << dst
<< "]: bad signature: " << S;
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reserve peer admission before actor allocation

When an IP sends a burst of fresh full-source packets while recent_inbound_peers is still below 60, this new pre-auth verifies each signature but does not reserve the peer before the packet falls through to peers_.try_emplace and get_peer_pair; the cap is only enforced later from AdnlPeerPairImpl::receive_packet via add_inbound_peer (adnl-peer.cpp lines 278-286). Fresh evidence after the latest fix is that valid signed keys still allocate PeerInfo/peer-pair actors before admission, so an attacker that controls arbitrary keypairs can create many actors in one burst even though most packets are dropped after allocation. Please perform the unique-peer admission/reservation before creating the peer entry.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 2216a32. When an inbound packet would require creating either a new PeerInfo or a peer-pair for the destination local ID, its signature is verified and the peer is admitted by AdnlLocalId before peers_.try_emplace or get_peer_pair is reached. Rejected burst peers therefore no longer allocate actors. test-adnl passes.

@shahramz13097-sudo shahramz13097-sudo mentioned this pull request Jul 23, 2026
Closed
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.

adnl: recent_inbound_peers inserted before signature verification (hardening)

2 participants