mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
vm: bugfixes
This commit is contained in:
parent
27aaa11524
commit
ba76f1404e
30 changed files with 396 additions and 178 deletions
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
@ -94,10 +94,8 @@ td::Result<Ref<DataCell>> CellSerializationInfo::create_data_cell(td::Slice cell
|
|||
for (int k = 0; k < refs_cnt; k++) {
|
||||
cb.store_ref(std::move(refs[k]));
|
||||
}
|
||||
auto res = cb.finalize_novm(special);
|
||||
if (res.is_null()) {
|
||||
return td::Status::Error("CellBuilder::finalize failed");
|
||||
}
|
||||
TRY_RESULT(res, cb.finalize_novm_nothrow(special));
|
||||
CHECK(!res.is_null());
|
||||
if (res->is_special() != special) {
|
||||
return td::Status::Error("is_special mismatch");
|
||||
}
|
||||
|
@ -654,10 +652,10 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
|
|||
ptr += 6;
|
||||
sz -= 6;
|
||||
if (sz < ref_byte_size) {
|
||||
return -(int)roots_offset;
|
||||
return -static_cast<int>(roots_offset);
|
||||
}
|
||||
cell_count = (int)read_ref(ptr);
|
||||
if (cell_count < 0) {
|
||||
if (cell_count <= 0) {
|
||||
cell_count = -1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -671,7 +669,7 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
|
|||
}
|
||||
index_offset = roots_offset;
|
||||
if (magic == boc_generic) {
|
||||
index_offset += root_count * ref_byte_size;
|
||||
index_offset += (long long)root_count * ref_byte_size;
|
||||
has_roots = true;
|
||||
} else {
|
||||
if (root_count != 1) {
|
||||
|
@ -690,12 +688,18 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
|
|||
return 0;
|
||||
}
|
||||
if (sz < 3 * ref_byte_size + offset_byte_size) {
|
||||
return -(int)roots_offset;
|
||||
return -static_cast<int>(roots_offset);
|
||||
}
|
||||
data_size = read_offset(ptr + 3 * ref_byte_size);
|
||||
if (data_size > ((unsigned long long)cell_count << 10)) {
|
||||
return 0;
|
||||
}
|
||||
if (data_size > (1ull << 40)) {
|
||||
return 0; // bag of cells with more than 1TiB data is unlikely
|
||||
}
|
||||
if (data_size < cell_count * (2ull + ref_byte_size) - ref_byte_size) {
|
||||
return 0; // invalid header, too many cells for this amount of data bytes
|
||||
}
|
||||
valid = true;
|
||||
total_size = data_offset + data_size + (has_crc32c ? 4 : 0);
|
||||
return total_size;
|
||||
|
@ -747,14 +751,13 @@ td::Result<td::Ref<vm::DataCell>> BagOfCells::deserialize_cell(int idx, td::Slic
|
|||
return cell_info.create_data_cell(cell_slice, refs);
|
||||
}
|
||||
|
||||
td::Result<long long> BagOfCells::deserialize(const td::Slice& data) {
|
||||
td::Result<long long> BagOfCells::deserialize(const td::Slice& data, int max_roots) {
|
||||
clear();
|
||||
long long size_est = info.parse_serialized_header(data);
|
||||
//LOG(INFO) << "estimated size " << size_est << ", true size " << data.size();
|
||||
if (size_est == 0) {
|
||||
return td::Status::Error(PSLICE() << "cannot deserialize bag-of-cells: invalid header, error " << size_est);
|
||||
}
|
||||
|
||||
if (size_est < 0) {
|
||||
//LOG(ERROR) << "cannot deserialize bag-of-cells: not enough bytes (" << data.size() << " present, " << -size_est
|
||||
//<< " required)";
|
||||
|
@ -767,6 +770,9 @@ td::Result<long long> BagOfCells::deserialize(const td::Slice& data) {
|
|||
return -size_est;
|
||||
}
|
||||
//LOG(INFO) << "estimated size " << size_est << ", true size " << data.size();
|
||||
if (info.root_count > max_roots) {
|
||||
return td::Status::Error("Bag-of-cells has more root cells than expected");
|
||||
}
|
||||
if (info.has_crc32c) {
|
||||
unsigned crc_computed = td::crc32c(td::Slice{data.ubegin(), data.uend() - 4});
|
||||
unsigned crc_stored = td::as<unsigned>(data.uend() - 4);
|
||||
|
@ -906,7 +912,7 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
|
|||
return Ref<Cell>();
|
||||
}
|
||||
BagOfCells boc;
|
||||
auto res = boc.deserialize(data);
|
||||
auto res = boc.deserialize(data, 1);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
|
@ -923,12 +929,12 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
|
|||
return std::move(root);
|
||||
}
|
||||
|
||||
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data) {
|
||||
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data, int max_roots) {
|
||||
if (data.empty()) {
|
||||
return std::vector<Ref<Cell>>{};
|
||||
}
|
||||
BagOfCells boc;
|
||||
auto res = boc.deserialize(data);
|
||||
auto res = boc.deserialize(data, max_roots);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include <set>
|
||||
|
@ -26,12 +26,6 @@
|
|||
namespace vm {
|
||||
using td::Ref;
|
||||
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
|
||||
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
|
||||
|
||||
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data);
|
||||
td::Result<td::BufferSlice> std_boc_serialize_multi(std::vector<Ref<Cell>> root, int mode = 0);
|
||||
|
||||
class NewCellStorageStat {
|
||||
public:
|
||||
NewCellStorageStat() {
|
||||
|
@ -159,7 +153,7 @@ struct CellSerializationInfo {
|
|||
|
||||
class BagOfCells {
|
||||
public:
|
||||
enum { hash_bytes = vm::Cell::hash_bytes };
|
||||
enum { hash_bytes = vm::Cell::hash_bytes, default_max_roots = 16384 };
|
||||
enum Mode { WithIndex = 1, WithCRC32C = 2, WithTopHash = 4, WithIntHashes = 8, WithCacheBits = 16, max = 31 };
|
||||
enum { max_cell_whs = 64 };
|
||||
using Hash = Cell::Hash;
|
||||
|
@ -259,9 +253,10 @@ class BagOfCells {
|
|||
std::size_t serialize_to(unsigned char* buffer, std::size_t buff_size, int mode = 0);
|
||||
std::string extract_string() const;
|
||||
|
||||
td::Result<long long> deserialize(const td::Slice& data);
|
||||
td::Result<long long> deserialize(const unsigned char* buffer, std::size_t buff_size) {
|
||||
return deserialize(td::Slice{buffer, buff_size});
|
||||
td::Result<long long> deserialize(const td::Slice& data, int max_roots = default_max_roots);
|
||||
td::Result<long long> deserialize(const unsigned char* buffer, std::size_t buff_size,
|
||||
int max_roots = default_max_roots) {
|
||||
return deserialize(td::Slice{buffer, buff_size}, max_roots);
|
||||
}
|
||||
int get_root_count() const {
|
||||
return root_count;
|
||||
|
@ -311,4 +306,11 @@ class BagOfCells {
|
|||
std::vector<td::uint8>* cell_should_cache);
|
||||
};
|
||||
|
||||
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
|
||||
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
|
||||
|
||||
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data,
|
||||
int max_roots = BagOfCells::default_max_roots);
|
||||
td::Result<td::BufferSlice> std_boc_serialize_multi(std::vector<Ref<Cell>> root, int mode = 0);
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
@ -67,9 +67,14 @@ Ref<DataCell> CellBuilder::finalize_copy(bool special) const {
|
|||
return cell;
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::finalize_novm(bool special) {
|
||||
td::Result<Ref<DataCell>> CellBuilder::finalize_novm_nothrow(bool special) {
|
||||
auto res = DataCell::create(data, size(), td::mutable_span(refs.data(), size_refs()), special);
|
||||
bits = refs_cnt = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
Ref<DataCell> CellBuilder::finalize_novm(bool special) {
|
||||
auto res = finalize_novm_nothrow(special);
|
||||
if (res.is_error()) {
|
||||
LOG(DEBUG) << res.error();
|
||||
throw CellWriteError{};
|
||||
|
@ -570,11 +575,11 @@ CellBuilder* CellBuilder::make_copy() const {
|
|||
return c;
|
||||
}
|
||||
|
||||
CellSlice CellBuilder::as_cellslice() const & {
|
||||
CellSlice CellBuilder::as_cellslice() const& {
|
||||
return CellSlice{finalize_copy()};
|
||||
}
|
||||
|
||||
Ref<CellSlice> CellBuilder::as_cellslice_ref() const & {
|
||||
Ref<CellSlice> CellBuilder::as_cellslice_ref() const& {
|
||||
return Ref<CellSlice>{true, finalize_copy()};
|
||||
}
|
||||
|
||||
|
|
|
@ -177,12 +177,13 @@ class CellBuilder : public td::CntObject {
|
|||
Ref<DataCell> finalize_copy(bool special = false) const;
|
||||
Ref<DataCell> finalize(bool special = false);
|
||||
Ref<DataCell> finalize_novm(bool special = false);
|
||||
td::Result<Ref<DataCell>> finalize_novm_nothrow(bool special = false);
|
||||
bool finalize_to(Ref<Cell>& res, bool special = false) {
|
||||
return (res = finalize(special)).not_null();
|
||||
}
|
||||
CellSlice as_cellslice() const &;
|
||||
CellSlice as_cellslice() const&;
|
||||
CellSlice as_cellslice() &&;
|
||||
Ref<CellSlice> as_cellslice_ref() const &;
|
||||
Ref<CellSlice> as_cellslice_ref() const&;
|
||||
Ref<CellSlice> as_cellslice_ref() &&;
|
||||
static td::int64 get_total_cell_builders() {
|
||||
return get_thread_safe_counter().sum();
|
||||
|
|
|
@ -492,6 +492,7 @@ int exec_setcontargs_common(VmState* st, int copy, int more) {
|
|||
} else {
|
||||
cdata->stack.write().move_from_stack(stack, copy);
|
||||
}
|
||||
st->consume_stack_gas(cdata->stack);
|
||||
if (cdata->nargs >= 0) {
|
||||
cdata->nargs -= copy;
|
||||
}
|
||||
|
@ -557,6 +558,7 @@ int exec_return_args_common(VmState* st, int count) {
|
|||
cdata->stack.write().move_from_stack(alt_stk.write(), copy);
|
||||
alt_stk.clear();
|
||||
}
|
||||
st->consume_stack_gas(cdata->stack);
|
||||
if (cdata->nargs >= 0) {
|
||||
cdata->nargs -= copy;
|
||||
}
|
||||
|
@ -587,6 +589,7 @@ int exec_bless_args_common(VmState* st, int copy, int more) {
|
|||
stack.check_underflow(copy + 1);
|
||||
auto cs = stack.pop_cellslice();
|
||||
auto new_stk = stack.split_top(copy);
|
||||
st->consume_stack_gas(new_stk);
|
||||
stack.push_cont(Ref<OrdCont>{true, std::move(cs), st->get_cp(), std::move(new_stk), more});
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -176,8 +176,10 @@ int VmState::call(Ref<Continuation> cont, int pass_args, int ret_args) {
|
|||
if (skip > 0) {
|
||||
get_stack().pop_many(skip);
|
||||
}
|
||||
consume_stack_gas(new_stk);
|
||||
} else if (copy >= 0) {
|
||||
new_stk = get_stack().split_top(copy, skip);
|
||||
consume_stack_gas(new_stk);
|
||||
} else {
|
||||
new_stk = std::move(stack);
|
||||
stack.clear();
|
||||
|
@ -196,7 +198,13 @@ int VmState::call(Ref<Continuation> cont, int pass_args, int ret_args) {
|
|||
throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"};
|
||||
}
|
||||
// create new stack from the top `pass_args` elements of the current stack
|
||||
Ref<Stack> new_stk = (pass_args >= 0 ? get_stack().split_top(pass_args) : std::move(stack));
|
||||
Ref<Stack> new_stk;
|
||||
if (pass_args >= 0) {
|
||||
new_stk = get_stack().split_top(pass_args);
|
||||
consume_stack_gas(new_stk);
|
||||
} else {
|
||||
new_stk = std::move(stack);
|
||||
}
|
||||
// create return continuation using the remainder of the current stack
|
||||
Ref<OrdCont> ret = Ref<OrdCont>{true, std::move(code), cp, std::move(stack), ret_args};
|
||||
ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0]));
|
||||
|
@ -251,10 +259,12 @@ int VmState::jump(Ref<Continuation> cont, int pass_args) {
|
|||
new_stk = cont_data->stack;
|
||||
}
|
||||
new_stk.write().move_from_stack(get_stack(), copy);
|
||||
consume_stack_gas(new_stk);
|
||||
set_stack(std::move(new_stk));
|
||||
} else {
|
||||
if (copy >= 0) {
|
||||
if (copy >= 0 && copy < stack->depth()) {
|
||||
get_stack().drop_bottom(stack->depth() - copy);
|
||||
consume_stack_gas(copy);
|
||||
}
|
||||
}
|
||||
return jump_to(std::move(cont));
|
||||
|
@ -264,8 +274,10 @@ int VmState::jump(Ref<Continuation> cont, int pass_args) {
|
|||
int depth = get_stack().depth();
|
||||
if (pass_args > depth) {
|
||||
throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"};
|
||||
} else if (pass_args < depth) {
|
||||
get_stack().drop_bottom(depth - pass_args);
|
||||
consume_stack_gas(pass_args);
|
||||
}
|
||||
get_stack().drop_bottom(depth - pass_args);
|
||||
}
|
||||
return jump_to(std::move(cont));
|
||||
}
|
||||
|
@ -303,6 +315,7 @@ Ref<OrdCont> VmState::extract_cc(int save_cr, int stack_copy, int cc_args) {
|
|||
} else if (stack_copy > 0) {
|
||||
stack->check_underflow(stack_copy);
|
||||
new_stk = get_stack().split_top(stack_copy);
|
||||
consume_stack_gas(new_stk);
|
||||
} else {
|
||||
new_stk = Ref<Stack>{true};
|
||||
}
|
||||
|
@ -332,7 +345,7 @@ int VmState::throw_exception(int excno) {
|
|||
stack_ref.push_smallint(0);
|
||||
stack_ref.push_smallint(excno);
|
||||
code.clear();
|
||||
consume_gas(exception_gas_price);
|
||||
gas.consume_chk(exception_gas_price);
|
||||
return jump(get_c2());
|
||||
}
|
||||
|
||||
|
@ -342,7 +355,7 @@ int VmState::throw_exception(int excno, StackEntry&& arg) {
|
|||
stack_ref.push(std::move(arg));
|
||||
stack_ref.push_smallint(excno);
|
||||
code.clear();
|
||||
consume_gas(exception_gas_price);
|
||||
gas.consume_chk(exception_gas_price);
|
||||
return jump(get_c2());
|
||||
}
|
||||
|
||||
|
@ -403,7 +416,6 @@ int VmState::run() {
|
|||
int res;
|
||||
Guard guard(this);
|
||||
do {
|
||||
// LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells();
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
|
@ -419,12 +431,10 @@ int VmState::run() {
|
|||
} catch (const VmError& vme) {
|
||||
VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg();
|
||||
try {
|
||||
// LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells();
|
||||
++steps;
|
||||
res = throw_exception(vme.get_errno());
|
||||
} catch (const VmError& vme2) {
|
||||
VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg();
|
||||
// LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells();
|
||||
return ~vme2.get_errno();
|
||||
}
|
||||
}
|
||||
|
@ -437,7 +447,6 @@ int VmState::run() {
|
|||
return vmoog.get_errno(); // no ~ for unhandled exceptions (to make their faking impossible)
|
||||
}
|
||||
} while (!res);
|
||||
// LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells();
|
||||
if ((res | 1) == -1 && !try_commit()) {
|
||||
VM_LOG(this) << "automatic commit failed (new data or action cells too deep)";
|
||||
get_stack().clear();
|
||||
|
|
|
@ -106,6 +106,8 @@ class VmState final : public VmStateInterface {
|
|||
tuple_entry_gas_price = 1,
|
||||
implicit_jmpref_gas_price = 10,
|
||||
implicit_ret_gas_price = 5,
|
||||
free_stack_depth = 32,
|
||||
stack_entry_gas_price = 1,
|
||||
max_data_depth = 512
|
||||
};
|
||||
VmState();
|
||||
|
@ -141,11 +143,19 @@ class VmState final : public VmStateInterface {
|
|||
void consume_tuple_gas(unsigned tuple_len) {
|
||||
consume_gas(tuple_len * tuple_entry_gas_price);
|
||||
}
|
||||
void consume_tuple_gas(const Ref<vm::Tuple>& tup) {
|
||||
void consume_tuple_gas(const Ref<Tuple>& tup) {
|
||||
if (tup.not_null()) {
|
||||
consume_tuple_gas((unsigned)tup->size());
|
||||
}
|
||||
}
|
||||
void consume_stack_gas(unsigned stack_depth) {
|
||||
consume_gas((std::max(stack_depth, (unsigned)free_stack_depth) - free_stack_depth) * stack_entry_gas_price);
|
||||
}
|
||||
void consume_stack_gas(const Ref<Stack>& stk) {
|
||||
if (stk.not_null()) {
|
||||
consume_stack_gas((unsigned)stk->depth());
|
||||
}
|
||||
}
|
||||
GasLimits get_gas_limits() const {
|
||||
return gas;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue