Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0.4] consolidated security fixes for 1.0.4 #1113

Merged
merged 1 commit into from
Jan 22, 2025
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
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.
Loading