1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

vm: bugfixes

This commit is contained in:
ton 2020-03-02 17:52:55 +04:00
parent 27aaa11524
commit ba76f1404e
30 changed files with 396 additions and 178 deletions

View file

@ -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();
}

View file

@ -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

View file

@ -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()};
}

View file

@ -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();

View file

@ -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;
}

View file

@ -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();

View file

@ -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;
}