Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Consolidated Security Updates for 2.0.12 - 2.1.x #10273

Merged
merged 3 commits into from
Apr 20, 2021
Merged
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
3 changes: 2 additions & 1 deletion libraries/chain/include/eosio/chain/transaction_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ namespace eosio { namespace chain {
void record_transaction( const transaction_id_type& id, fc::time_point_sec expire );

void validate_cpu_usage_to_bill( int64_t billed_us, int64_t account_cpu_limit, bool check_minimum )const;
void validate_account_cpu_usage( int64_t billed_us, int64_t account_cpu_limit, bool estimate )const;
void validate_account_cpu_usage( int64_t billed_us, int64_t account_cpu_limit )const;
void validate_account_cpu_usage_estimate( int64_t billed_us, int64_t account_cpu_limit )const;

void disallow_transaction_extensions( const char* error_msg )const;

Expand Down
51 changes: 41 additions & 10 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,11 @@ namespace eosio { namespace chain {

if( !explicit_billed_cpu_time ) {
// Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us;
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
if( validate_account_cpu_limit > 0 )
validate_account_cpu_limit -= EOS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
validate_account_cpu_usage( billed_cpu_time_us, validate_account_cpu_limit, true );
validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit );
}

eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) so check_net_usage can be efficient
Expand Down Expand Up @@ -456,34 +456,65 @@ namespace eosio { namespace chain {
);
}

validate_account_cpu_usage( billed_us, account_cpu_limit, false );
validate_account_cpu_usage( billed_us, account_cpu_limit );
}
}

void transaction_context::validate_account_cpu_usage( int64_t billed_us, int64_t account_cpu_limit, bool estimate )const {
void transaction_context::validate_account_cpu_usage( int64_t billed_us, int64_t account_cpu_limit )const {
if( (billed_us > 0) && !control.skip_trx_checks() ) {
const bool cpu_limited_by_account = (account_cpu_limit <= objective_duration_limit.count());

if( !cpu_limited_by_account && (billing_timer_exception_code == block_cpu_usage_exceeded::code_value) ) {
EOS_ASSERT( billed_us <= objective_duration_limit.count(),
block_cpu_usage_exceeded,
"${desc} CPU time (${billed} us) is greater than the billable CPU time left in the block (${billable} us)",
("desc", (estimate ? "estimated" : "billed"))("billed", billed_us)( "billable", objective_duration_limit.count() )
"billed CPU time (${billed} us) is greater than the billable CPU time left in the block (${billable} us)",
("billed", billed_us)( "billable", objective_duration_limit.count() )
);
} else {
if( cpu_limit_due_to_greylist && cpu_limited_by_account ) {
EOS_ASSERT( billed_us <= account_cpu_limit,
greylist_cpu_usage_exceeded,
"${desc} CPU time (${billed} us) is greater than the maximum greylisted billable CPU time for the transaction (${billable} us)",
("desc", (estimate ? "estimated" : "billed"))("billed", billed_us)( "billable", account_cpu_limit )
"billed CPU time (${billed} us) is greater than the maximum greylisted billable CPU time for the transaction (${billable} us)",
("billed", billed_us)( "billable", account_cpu_limit )
);
} else {
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
const int64_t cpu_limit = (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
EOS_ASSERT( billed_us <= cpu_limit,
tx_cpu_usage_exceeded,
"${desc} CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)",
("desc", (estimate ? "estimated" : "billed"))("billed", billed_us)( "billable", cpu_limit )
"billed CPU time (${billed} us) is greater than the maximum billable CPU time for the transaction (${billable} us)",
("billed", billed_us)( "billable", cpu_limit )
);
}
}
}
}

void transaction_context::validate_account_cpu_usage_estimate( int64_t prev_billed_us, int64_t account_cpu_limit )const {
// prev_billed_us can be 0, but so can account_cpu_limit
if( (prev_billed_us >= 0) && !control.skip_trx_checks() ) {
const bool cpu_limited_by_account = (account_cpu_limit <= objective_duration_limit.count());

if( !cpu_limited_by_account && (billing_timer_exception_code == block_cpu_usage_exceeded::code_value) ) {
EOS_ASSERT( prev_billed_us < objective_duration_limit.count(),
block_cpu_usage_exceeded,
"estimated CPU time (${billed} us) is not less than the billable CPU time left in the block (${billable} us)",
("billed", prev_billed_us)( "billable", objective_duration_limit.count() )
);
} else {
if( cpu_limit_due_to_greylist && cpu_limited_by_account ) {
EOS_ASSERT( prev_billed_us < account_cpu_limit,
greylist_cpu_usage_exceeded,
"estimated CPU time (${billed} us) is not less than the maximum greylisted billable CPU time for the transaction (${billable} us)",
("billed", prev_billed_us)( "billable", account_cpu_limit )
);
} else {
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
const int64_t cpu_limit = (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
EOS_ASSERT( prev_billed_us < cpu_limit,
tx_cpu_usage_exceeded,
"estimated CPU time (${billed} us) is not less than the maximum billable CPU time for the transaction (${billable} us)",
("billed", prev_billed_us)( "billable", cpu_limit )
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class subjective_billing {
trx_cache_index _trx_cache_index;
account_subjective_bill_cache _account_subjective_bill_cache;
block_subjective_bill_cache _block_subjective_bill_cache;
std::set<chain::account_name> _disabled_accounts;

private:
uint32_t time_ordinal_for( const fc::time_point& t ) const {
Expand Down Expand Up @@ -115,12 +116,13 @@ class subjective_billing {
public:
void disable() { _disabled = true; }
bool is_disabled() const { return _disabled; }
void disable_account( chain::account_name a ) { _disabled_accounts.emplace( a ); }

/// @param in_pending_block pass true if pt's bill time is accounted for in the pending block
void subjective_bill( const transaction_id_type& id, const fc::time_point& expire, const account_name& first_auth,
const fc::microseconds& elapsed, bool in_pending_block )
{
if( !_disabled ) {
if( !_disabled && !_disabled_accounts.count( first_auth ) ) {
uint32_t bill = std::max<int64_t>( 0, elapsed.count() );
auto p = _trx_cache_index.emplace(
trx_cache_entry{id,
Expand All @@ -138,15 +140,15 @@ class subjective_billing {

void subjective_bill_failure( const account_name& first_auth, const fc::microseconds& elapsed, const fc::time_point& now )
{
if( !_disabled ) {
if( !_disabled && !_disabled_accounts.count( first_auth ) ) {
uint32_t bill = std::max<int64_t>( 0, elapsed.count() );
const auto time_ordinal = time_ordinal_for(now);
_account_subjective_bill_cache[first_auth].expired_accumulator.add(bill, time_ordinal, expired_accumulator_average_window);
}
}

uint32_t get_subjective_bill( const account_name& first_auth, const fc::time_point& now ) const {
if( _disabled ) return 0;
if( _disabled || _disabled_accounts.count( first_auth ) ) return 0;
const auto time_ordinal = time_ordinal_for(now);
const subjective_billing_info* sub_bill_info = nullptr;
auto aitr = _account_subjective_bill_cache.find( first_auth );
Expand Down
Loading