Skip to content

Commit 95da7e5

Browse files
linh2931spoonincode
authored andcommitted
security updates
1 parent 1934f75 commit 95da7e5

File tree

7 files changed

+112
-23
lines changed

7 files changed

+112
-23
lines changed

plugins/producer_plugin/producer_plugin.cpp

+21-19
Original file line numberDiff line numberDiff line change
@@ -1483,29 +1483,31 @@ void producer_plugin_impl::plugin_initialize(const boost::program_options::varia
14831483
plugin_config_exception,
14841484
"read-only-read-window-time-us (${read}) must be at least greater than ${min} us",
14851485
("read", _ro_read_window_time_us)("min", _ro_read_window_minimum_time_us));
1486-
_ro_read_window_effective_time_us = _ro_read_window_time_us - _ro_read_window_minimum_time_us;
1487-
1486+
_ro_read_window_effective_time_us = _ro_read_window_time_us;
14881487
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",
14891488
("ww", _ro_write_window_time_us)("rw", _ro_read_window_time_us)("w", _ro_read_window_effective_time_us));
1490-
}
1491-
app().executor().init_read_threads(_ro_thread_pool_size);
1489+
// Make sure _ro_max_trx_time_us is always set.
1490+
// Make sure a read-only transaction can finish within the read
1491+
// window if scheduled at the very beginning of the window.
1492+
if (_max_transaction_time_ms.load() > 0) {
1493+
_ro_max_trx_time_us = fc::milliseconds(_max_transaction_time_ms.load());
1494+
} else {
1495+
// max-transaction-time can be set to negative for unlimited time
1496+
_ro_max_trx_time_us = fc::microseconds::maximum();
1497+
}
1498+
// Factor _ro_read_window_minimum_time_us into _ro_max_trx_time_us
1499+
// such that a transaction which runs less than or equal to _ro_max_trx_time_us
1500+
// can fit in effective read-only window
1501+
assert(_ro_read_window_effective_time_us > _ro_read_window_minimum_time_us);
1502+
if (_ro_max_trx_time_us > _ro_read_window_effective_time_us - _ro_read_window_minimum_time_us) {
1503+
_ro_max_trx_time_us = _ro_read_window_effective_time_us - _ro_read_window_minimum_time_us;
1504+
}
1505+
ilog("Read-only max transaction time ${rot}us set to fit in the effective read-only window ${row}us.",
1506+
("rot", _ro_max_trx_time_us)("row", _ro_read_window_effective_time_us));
1507+
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));
14921508

1493-
// Make sure _ro_max_trx_time_us is always set.
1494-
// Make sure a read-only transaction can finish within the read
1495-
// window if scheduled at the very beginning of the window.
1496-
// Add _ro_read_window_minimum_time_us for safety margin.
1497-
if (_max_transaction_time_ms.load() > 0) {
1498-
_ro_max_trx_time_us = fc::milliseconds(_max_transaction_time_ms.load());
1499-
} else {
1500-
// max-transaction-time can be set to negative for unlimited time
1501-
_ro_max_trx_time_us = fc::microseconds::maximum();
1502-
}
1503-
if (_ro_max_trx_time_us > _ro_read_window_effective_time_us) {
1504-
_ro_max_trx_time_us = _ro_read_window_effective_time_us;
1509+
app().executor().init_read_threads(_ro_thread_pool_size);
15051510
}
1506-
ilog("Read-only max transaction time ${rot}us set to fit in the effective read-only window ${row}us.",
1507-
("rot", _ro_max_trx_time_us)("row", _ro_read_window_effective_time_us));
1508-
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));
15091511

15101512
_incoming_block_sync_provider = app().get_method<incoming::methods::block_sync>().register_provider(
15111513
[this](const signed_block_ptr& block, const block_id_type& block_id, const std::optional<block_handle>& obt) {

tests/CMakeLists.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ add_test(NAME compute_transaction_test COMMAND tests/compute_transaction_test.py
206206
set_property(TEST compute_transaction_test PROPERTY LABELS nonparallelizable_tests)
207207
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})
208208
set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests)
209-
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})
209+
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})
210210
set_property(TEST read-only-trx-parallel-test PROPERTY LABELS nonparallelizable_tests)
211211
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})
212212
set_property(TEST read-only-trx-basic-if-test PROPERTY LABELS nonparallelizable_tests)
213-
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})
213+
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})
214214
set_property(TEST read-only-trx-parallel-if-test PROPERTY LABELS nonparallelizable_tests)
215-
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})
215+
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})
216216
set_property(TEST read-only-trx-parallel-if-eos-vm-oc-test PROPERTY LABELS nonparallelizable_tests)
217217
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})
218218
set_property(TEST read-only-trx-parallel-no-oc-if-test PROPERTY LABELS nonparallelizable_tests)

tests/read_only_trx_test.py

+61-1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ def sendReadOnlyPayloadless():
210210
def sendReadOnlySlowPayloadless():
211211
return sendTransaction('payloadless', action='doitslow', data={}, auth=[], opts='--read')
212212

213+
def sendReadOnlyForeverPayloadless():
214+
return sendTransaction('payloadless', action='doitforever', data={}, auth=[], opts='--read')
215+
213216
# Send read-only trxs from mutltiple threads to bump load
214217
def sendReadOnlyTrxOnThread(startId, numTrxs):
215218
Print("start sendReadOnlyTrxOnThread")
@@ -283,7 +286,7 @@ def runReadOnlyTrxAndRpcInParallel(resource, command, fieldIn=None, expectedValu
283286
def mixedOpsTest(opt=None):
284287
Print("mixedOpsTest -- opt = ", opt)
285288

286-
numRuns = 200
289+
numRuns = 100
287290
readOnlyThread = threading.Thread(target = sendReadOnlyTrxOnThread, args = (0, numRuns ))
288291
readOnlyThread.start()
289292
sendTrxThread = threading.Thread(target = sendTrxsOnThread, args = (numRuns, numRuns, opt))
@@ -373,6 +376,60 @@ def runEverythingParallel():
373376
for thr in threadList:
374377
thr.join()
375378

379+
def fastTransactions():
380+
Print("fastTransactions")
381+
for i in range(1000):
382+
result = sendReadOnlyPayloadless()
383+
assert(result[0])
384+
385+
def slowTransactions():
386+
Print("slowTransactions")
387+
for i in range(100): # run fewer number than regular Payloadless so total running time is close
388+
result = sendReadOnlySlowPayloadless()
389+
assert(result[0])
390+
391+
def foreverTransactions():
392+
Print("foreverTransactions")
393+
for i in range(5): # run fewer number than slowPayloadless so total running time is close
394+
result = sendReadOnlyForeverPayloadless()
395+
assert(result[0] == False) # should fail
396+
397+
def timeoutTest():
398+
Print("timeoutTest")
399+
400+
# Send a forever readonly transaction. It should timeout
401+
Print("Sending a forever read only transaction")
402+
results = sendReadOnlyForeverPayloadless()
403+
# Results look like
404+
'''
405+
( False,
406+
{'processed':
407+
{
408+
...
409+
'except': {'code': 3080004, 'name': 'tx_cpu_usage_exceeded', 'message': 'Transaction exceeded the current CPU usage limit imposed on the transaction' ...}
410+
...
411+
}
412+
}
413+
)
414+
'''
415+
assert(results[0] == False)
416+
assert('except' in results[1]['processed'])
417+
assert(results[1]['processed']['except']['code'] == 3080004)
418+
assert(results[1]['processed']['except']['name'] == "tx_cpu_usage_exceeded")
419+
420+
# Send multiple different speeds of read only transactions simutaneously
421+
# to trigger forever transactions are exhausted in read window but RETRYING
422+
# at next round.
423+
threadList = []
424+
threadList.append(threading.Thread(target = fastTransactions))
425+
threadList.append(threading.Thread(target = slowTransactions))
426+
threadList.append(threading.Thread(target = foreverTransactions))
427+
Print("Sending different speeds of read only transactions simutaneously")
428+
for thr in threadList:
429+
thr.start()
430+
for thr in threadList:
431+
thr.join()
432+
376433
try:
377434
startCluster()
378435
deployTestContracts()
@@ -387,6 +444,9 @@ def runEverythingParallel():
387444
mixedOpsTest()
388445
runEverythingParallel()
389446

447+
# should be running under multiple threads but no need to run multiple times
448+
timeoutTest()
449+
390450
testSuccessful = True
391451
finally:
392452
TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails)

unittests/test-contracts/payloadless/payloadless.abi

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
"base": "",
99
"fields": []
1010
},
11+
{
12+
"name": "doitforever",
13+
"base": "",
14+
"fields": []
15+
},
1116
{
1217
"name": "doitslow",
1318
"base": "",
@@ -20,6 +25,11 @@
2025
"type": "doit",
2126
"ricardian_contract": ""
2227
},
28+
{
29+
"name": "doitforever",
30+
"type": "doitforever",
31+
"ricardian_contract": ""
32+
},
2333
{
2434
"name": "doitslow",
2535
"type": "doitslow",

unittests/test-contracts/payloadless/payloadless.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,17 @@ void payloadless::doitslow() {
5050
}
5151
}
5252

53+
void payloadless::doitforever() {
54+
print("Im a payloadless forever action");
55+
constexpr size_t max_cpu_prime = std::numeric_limits<size_t>::max();
56+
57+
while (true) {
58+
for (size_t p = 2; p <= max_cpu_prime; p += 1) {
59+
if (is_prime(p) && is_mersenne_prime(p)) {
60+
// We need to keep an eye on this to make sure it doesn't get optimized out. So far so good.
61+
//eosio::print_f(" %u", p);
62+
}
63+
}
64+
}
65+
}
66+

unittests/test-contracts/payloadless/payloadless.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ class [[eosio::contract]] payloadless : public eosio::contract {
1111

1212
[[eosio::action]]
1313
void doitslow();
14+
15+
[[eosio::action]]
16+
void doitforever();
1417
};
Binary file not shown.

0 commit comments

Comments
 (0)