Skip to content

Commit

Permalink
Merge pull request #1113 from AntelopeIO/104_security
Browse files Browse the repository at this point in the history
[1.0.4] consolidated security fixes for 1.0.4
  • Loading branch information
spoonincode authored Jan 22, 2025
2 parents 1934f75 + 95da7e5 commit 5a0b421
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 23 deletions.
40 changes: 21 additions & 19 deletions plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1483,29 +1483,31 @@ void producer_plugin_impl::plugin_initialize(const boost::program_options::varia
plugin_config_exception,
"read-only-read-window-time-us (${read}) must be at least greater than ${min} us",
("read", _ro_read_window_time_us)("min", _ro_read_window_minimum_time_us));
_ro_read_window_effective_time_us = _ro_read_window_time_us - _ro_read_window_minimum_time_us;

_ro_read_window_effective_time_us = _ro_read_window_time_us;
ilog("read-only-write-window-time-us: ${ww} us, read-only-read-window-time-us: ${rw} us, effective read window time to be used: ${w} us",
("ww", _ro_write_window_time_us)("rw", _ro_read_window_time_us)("w", _ro_read_window_effective_time_us));
}
app().executor().init_read_threads(_ro_thread_pool_size);
// Make sure _ro_max_trx_time_us is always set.
// Make sure a read-only transaction can finish within the read
// window if scheduled at the very beginning of the window.
if (_max_transaction_time_ms.load() > 0) {
_ro_max_trx_time_us = fc::milliseconds(_max_transaction_time_ms.load());
} else {
// max-transaction-time can be set to negative for unlimited time
_ro_max_trx_time_us = fc::microseconds::maximum();
}
// Factor _ro_read_window_minimum_time_us into _ro_max_trx_time_us
// such that a transaction which runs less than or equal to _ro_max_trx_time_us
// can fit in effective read-only window
assert(_ro_read_window_effective_time_us > _ro_read_window_minimum_time_us);
if (_ro_max_trx_time_us > _ro_read_window_effective_time_us - _ro_read_window_minimum_time_us) {
_ro_max_trx_time_us = _ro_read_window_effective_time_us - _ro_read_window_minimum_time_us;
}
ilog("Read-only max transaction time ${rot}us set to fit in the effective read-only window ${row}us.",
("rot", _ro_max_trx_time_us)("row", _ro_read_window_effective_time_us));
ilog("read-only-threads ${s}, max read-only trx time to be enforced: ${t} us", ("s", _ro_thread_pool_size)("t", _ro_max_trx_time_us));

// Make sure _ro_max_trx_time_us is always set.
// Make sure a read-only transaction can finish within the read
// window if scheduled at the very beginning of the window.
// Add _ro_read_window_minimum_time_us for safety margin.
if (_max_transaction_time_ms.load() > 0) {
_ro_max_trx_time_us = fc::milliseconds(_max_transaction_time_ms.load());
} else {
// max-transaction-time can be set to negative for unlimited time
_ro_max_trx_time_us = fc::microseconds::maximum();
}
if (_ro_max_trx_time_us > _ro_read_window_effective_time_us) {
_ro_max_trx_time_us = _ro_read_window_effective_time_us;
app().executor().init_read_threads(_ro_thread_pool_size);
}
ilog("Read-only max transaction time ${rot}us set to fit in the effective read-only window ${row}us.",
("rot", _ro_max_trx_time_us)("row", _ro_read_window_effective_time_us));
ilog("read-only-threads ${s}, max read-only trx time to be enforced: ${t} us", ("s", _ro_thread_pool_size)("t", _ro_max_trx_time_us));

_incoming_block_sync_provider = app().get_method<incoming::methods::block_sync>().register_provider(
[this](const signed_block_ptr& block, const block_id_type& block_id, const std::optional<block_handle>& obt) {
Expand Down
6 changes: 3 additions & 3 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,13 @@ add_test(NAME compute_transaction_test COMMAND tests/compute_transaction_test.py
set_property(TEST compute_transaction_test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-basic-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 1 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 2 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-parallel-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-basic-if-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 1 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-basic-if-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-parallel-if-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 3 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME read-only-trx-parallel-if-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 2 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-parallel-if-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-parallel-if-eos-vm-oc-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --eos-vm-oc-enable all --read-only-threads 16 --num-test-runs 3 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME read-only-trx-parallel-if-eos-vm-oc-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --eos-vm-oc-enable all --read-only-threads 16 --num-test-runs 2 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-parallel-if-eos-vm-oc-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-parallel-no-oc-if-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --eos-vm-oc-enable none --read-only-threads 6 --num-test-runs 2 --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-parallel-no-oc-if-test PROPERTY LABELS nonparallelizable_tests)
Expand Down
62 changes: 61 additions & 1 deletion tests/read_only_trx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ def sendReadOnlyPayloadless():
def sendReadOnlySlowPayloadless():
return sendTransaction('payloadless', action='doitslow', data={}, auth=[], opts='--read')

def sendReadOnlyForeverPayloadless():
return sendTransaction('payloadless', action='doitforever', data={}, auth=[], opts='--read')

# Send read-only trxs from mutltiple threads to bump load
def sendReadOnlyTrxOnThread(startId, numTrxs):
Print("start sendReadOnlyTrxOnThread")
Expand Down Expand Up @@ -283,7 +286,7 @@ def runReadOnlyTrxAndRpcInParallel(resource, command, fieldIn=None, expectedValu
def mixedOpsTest(opt=None):
Print("mixedOpsTest -- opt = ", opt)

numRuns = 200
numRuns = 100
readOnlyThread = threading.Thread(target = sendReadOnlyTrxOnThread, args = (0, numRuns ))
readOnlyThread.start()
sendTrxThread = threading.Thread(target = sendTrxsOnThread, args = (numRuns, numRuns, opt))
Expand Down Expand Up @@ -373,6 +376,60 @@ def runEverythingParallel():
for thr in threadList:
thr.join()

def fastTransactions():
Print("fastTransactions")
for i in range(1000):
result = sendReadOnlyPayloadless()
assert(result[0])

def slowTransactions():
Print("slowTransactions")
for i in range(100): # run fewer number than regular Payloadless so total running time is close
result = sendReadOnlySlowPayloadless()
assert(result[0])

def foreverTransactions():
Print("foreverTransactions")
for i in range(5): # run fewer number than slowPayloadless so total running time is close
result = sendReadOnlyForeverPayloadless()
assert(result[0] == False) # should fail

def timeoutTest():
Print("timeoutTest")

# Send a forever readonly transaction. It should timeout
Print("Sending a forever read only transaction")
results = sendReadOnlyForeverPayloadless()
# Results look like
'''
( False,
{'processed':
{
...
'except': {'code': 3080004, 'name': 'tx_cpu_usage_exceeded', 'message': 'Transaction exceeded the current CPU usage limit imposed on the transaction' ...}
...
}
}
)
'''
assert(results[0] == False)
assert('except' in results[1]['processed'])
assert(results[1]['processed']['except']['code'] == 3080004)
assert(results[1]['processed']['except']['name'] == "tx_cpu_usage_exceeded")

# Send multiple different speeds of read only transactions simutaneously
# to trigger forever transactions are exhausted in read window but RETRYING
# at next round.
threadList = []
threadList.append(threading.Thread(target = fastTransactions))
threadList.append(threading.Thread(target = slowTransactions))
threadList.append(threading.Thread(target = foreverTransactions))
Print("Sending different speeds of read only transactions simutaneously")
for thr in threadList:
thr.start()
for thr in threadList:
thr.join()

try:
startCluster()
deployTestContracts()
Expand All @@ -387,6 +444,9 @@ def runEverythingParallel():
mixedOpsTest()
runEverythingParallel()

# should be running under multiple threads but no need to run multiple times
timeoutTest()

testSuccessful = True
finally:
TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails)
Expand Down
10 changes: 10 additions & 0 deletions unittests/test-contracts/payloadless/payloadless.abi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
"base": "",
"fields": []
},
{
"name": "doitforever",
"base": "",
"fields": []
},
{
"name": "doitslow",
"base": "",
Expand All @@ -20,6 +25,11 @@
"type": "doit",
"ricardian_contract": ""
},
{
"name": "doitforever",
"type": "doitforever",
"ricardian_contract": ""
},
{
"name": "doitslow",
"type": "doitslow",
Expand Down
14 changes: 14 additions & 0 deletions unittests/test-contracts/payloadless/payloadless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,17 @@ void payloadless::doitslow() {
}
}

void payloadless::doitforever() {
print("Im a payloadless forever action");
constexpr size_t max_cpu_prime = std::numeric_limits<size_t>::max();

while (true) {
for (size_t p = 2; p <= max_cpu_prime; p += 1) {
if (is_prime(p) && is_mersenne_prime(p)) {
// We need to keep an eye on this to make sure it doesn't get optimized out. So far so good.
//eosio::print_f(" %u", p);
}
}
}
}

3 changes: 3 additions & 0 deletions unittests/test-contracts/payloadless/payloadless.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ class [[eosio::contract]] payloadless : public eosio::contract {

[[eosio::action]]
void doitslow();

[[eosio::action]]
void doitforever();
};
Binary file modified unittests/test-contracts/payloadless/payloadless.wasm
Binary file not shown.

0 comments on commit 5a0b421

Please sign in to comment.