Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions libraries/chain/qc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ void qc_t::verify_basic(const finalizer_policies_t& policies) const {
EOS_ASSERT(policies.pending_finalizer_policy, invalid_qc,
"qc ${bn} contains pending policy signature for nonexistent pending finalizer policy", ("bn", block_num));

// verify that every finalizer included in both policies voted the same
verify_dual_finalizers_votes(policies, active_policy_sig, *pending_policy_sig, block_num);

// Validate the pending signature's vote format and weights BEFORE comparing dual finalizers.
// verify_dual_finalizers_votes() -> vote_same_at() indexes the pending vote bitset, so its
// size must be validated first; otherwise a malformed pending bitset could reach unchecked
// indexing instead of being rejected here.
pending_policy_sig->verify_vote_format(policies.pending_finalizer_policy);
pending_policy_sig->verify_weights(policies.pending_finalizer_policy);

// verify that every finalizer included in both policies voted the same
verify_dual_finalizers_votes(policies, active_policy_sig, *pending_policy_sig, block_num);
} else {
EOS_ASSERT(!policies.pending_finalizer_policy, invalid_qc,
"qc ${bn} does not contain pending policy signature for pending finalizer policy", ("bn", block_num));
Expand All @@ -84,8 +88,13 @@ void qc_t::verify_basic(const finalizer_policies_t& policies) const {

// returns true iff the other and I voted in the same way.
bool qc_sig_t::vote_same_at(const qc_sig_t& other, uint32_t my_vote_index, uint32_t other_vote_index) const {
assert(!strong_votes || my_vote_index < strong_votes->size());
assert(!weak_votes || my_vote_index < weak_votes->size());
// Runtime bounds checks for BOTH signatures' vote indices. The previous assert()s are compiled
// out under NDEBUG and only guarded `my_vote_index`; validate `other`'s index too so a
// malformed/undersized bitset is rejected rather than reaching unchecked dynamic_bitset access.
EOS_ASSERT(!strong_votes || my_vote_index < strong_votes->size(), invalid_qc, "qc active strong_votes index out of range");
EOS_ASSERT(!weak_votes || my_vote_index < weak_votes->size(), invalid_qc, "qc active weak_votes index out of range");
EOS_ASSERT(!other.strong_votes || other_vote_index < other.strong_votes->size(), invalid_qc, "qc pending strong_votes index out of range");
EOS_ASSERT(!other.weak_votes || other_vote_index < other.weak_votes->size(), invalid_qc, "qc pending weak_votes index out of range");

// We have already verified the same index has not voted both strong
// and weak for a given qc_sig_t (I or other).
Expand Down
11 changes: 8 additions & 3 deletions libraries/chain/webassembly/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,10 @@ namespace eosio::chain::webassembly {
}

int32_t interface::bls_g1_weighted_sum(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const {
if(n == 0 || points.size() != n*96 || scalars.size() != n*32 || result.size() != 96)
// Compute the expected span lengths in 64-bit. `n` is uint32 and the products were previously
// computed in 32-bit, which can overflow for large `n` and let malformed inputs pass this
// length check before allocation. 64-bit math keeps the comparison exact.
if(n == 0 || points.size() != static_cast<uint64_t>(n)*96u || scalars.size() != static_cast<uint64_t>(n)*32u || result.size() != 96)
return return_code::failure;

// Use much efficient scale for the special case of n == 1.
Expand Down Expand Up @@ -320,7 +323,8 @@ namespace eosio::chain::webassembly {
}

int32_t interface::bls_g2_weighted_sum(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const {
if(n == 0 || points.size() != n*192 || scalars.size() != n*32 || result.size() != 192)
// 64-bit length math to avoid the 32-bit n*192 / n*32 wraparound (see bls_g1_weighted_sum).
if(n == 0 || points.size() != static_cast<uint64_t>(n)*192u || scalars.size() != static_cast<uint64_t>(n)*32u || result.size() != 192)
return return_code::failure;

// Use much efficient scale for the special case of n == 1.
Expand Down Expand Up @@ -355,7 +359,8 @@ namespace eosio::chain::webassembly {
}

int32_t interface::bls_pairing(span<const char> g1_points, span<const char> g2_points, const uint32_t n, span<char> result) const {
if(n == 0 || g1_points.size() != n*96 || g2_points.size() != n*192 || result.size() != 576)
// 64-bit length math to avoid the 32-bit n*96 / n*192 wraparound (see bls_g1_weighted_sum).
if(n == 0 || g1_points.size() != static_cast<uint64_t>(n)*96u || g2_points.size() != static_cast<uint64_t>(n)*192u || result.size() != 576)
return return_code::failure;
std::vector<std::tuple<bls12_381::g1, bls12_381::g2>> v;
v.reserve(n);
Expand Down
Loading