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

loader: Destroy VM with incompatible ABI #306

Merged
merged 4 commits into from
May 30, 2019
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
[[#261](https://github.com/ethereum/evmc/issues/261),
[#263](https://github.com/ethereum/evmc/pull/263)]
The `vmtester` tool now builds with MSVC with `/std:c++17`.
- Fixed:
[[#305](https://github.com/ethereum/evmc/issues/305),
[#306](https://github.com/ethereum/evmc/pull/306)]
A loaded VM with incompatible ABI version is not properly destroyed.

## [6.2.2] - 2019-05-16

Expand Down
1 change: 1 addition & 0 deletions lib/loader/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ struct evmc_instance* evmc_load_and_create(const char* filename,

if (!evmc_is_abi_compatible(instance))
{
evmc_destroy(instance);
*error_code = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH,
"EVMC ABI version %d of %s mismatches the expected version %d",
instance->abi_version, filename, EVMC_ABI_VERSION);
Expand Down
91 changes: 80 additions & 11 deletions test/unittests/test_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// Copyright 2018-2019 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.

#include <evmc/evmc.h>
#include <evmc/loader.h>

#include <gtest/gtest.h>

#include <cstring>
#include <unordered_map>
#include <vector>

#if _WIN32
static constexpr bool is_windows = true;
Expand All @@ -15,22 +16,93 @@ static constexpr bool is_windows = false;
#endif

extern "C" {
/// The library path expected by mocked evmc_test_load_library().
extern const char* evmc_test_library_path;

/// The symbol name expected by mocked evmc_test_get_symbol_address().
extern const char* evmc_test_library_symbol;

/// The pointer to function returned by evmc_test_get_symbol_address().
extern evmc_create_fn evmc_test_create_fn;
}

class loader : public ::testing::Test
{
protected:
static int create_count;
static int destroy_count;
static std::unordered_map<std::string, const char*> supported_options;
static std::vector<std::pair<std::string, std::string>> recorded_options;

loader() noexcept
{
create_count = 0;
destroy_count = 0;
supported_options.clear();
recorded_options.clear();
}

void setup(const char* path, const char* symbol, evmc_create_fn fn) noexcept
{
evmc_test_library_path = path;
evmc_test_library_symbol = symbol;
evmc_test_create_fn = fn;
}

static void destroy(evmc_instance*) noexcept { ++destroy_count; }

static evmc_set_option_result set_option(evmc_instance*,
const char* name,
const char* value) noexcept
{
recorded_options.push_back({name, value ? value : "<null>"}); // NOLINT

auto it = supported_options.find(name);
if (it == supported_options.end())
return EVMC_SET_OPTION_INVALID_NAME;
if (it->second == nullptr)
return value == nullptr ? EVMC_SET_OPTION_SUCCESS : EVMC_SET_OPTION_INVALID_VALUE;
if (value == nullptr)
return EVMC_SET_OPTION_INVALID_VALUE;
return std::string{value} == it->second ? EVMC_SET_OPTION_SUCCESS :
EVMC_SET_OPTION_INVALID_VALUE;
}

/// Creates a VM mock with only destroy() method.
static evmc_instance* create_vm_barebone()
{
static auto instance =
evmc_instance{EVMC_ABI_VERSION, "", "", destroy, nullptr, nullptr, nullptr, nullptr};
++create_count;
return &instance;
}

/// Creates a VM mock with ABI version different than in this project.
static evmc_instance* create_vm_with_wrong_abi()
{
constexpr auto wrong_abi_version = 1985;
static_assert(wrong_abi_version != EVMC_ABI_VERSION, "");
static auto instance =
evmc_instance{wrong_abi_version, "", "", destroy, nullptr, nullptr, nullptr, nullptr};
++create_count;
return &instance;
}

/// Creates a VM mock with optional set_option() method.
static evmc_instance* create_vm_with_set_option() noexcept
{
static auto instance =
evmc_instance{EVMC_ABI_VERSION, "", "", destroy, nullptr, nullptr, nullptr, set_option};
++create_count;
return &instance;
}
};

int loader::create_count = 0;
int loader::destroy_count = 0;
std::unordered_map<std::string, const char*> loader::supported_options;
std::vector<std::pair<std::string, std::string>> loader::recorded_options;

static evmc_instance* create_aaa()
{
return (evmc_instance*)0xaaa;
Expand All @@ -46,12 +118,6 @@ static evmc_instance* create_failure()
return nullptr;
}

static evmc_instance* create_abi42()
{
static int abi_version = 42;
return reinterpret_cast<evmc_instance*>(&abi_version);
}

TEST_F(loader, load_nonexistent)
{
constexpr auto path = "nonexistent";
Expand Down Expand Up @@ -210,12 +276,15 @@ TEST_F(loader, load_and_create_failure)

TEST_F(loader, load_and_create_abi_mismatch)
{
setup("abi42.vm", "evmc_create", create_abi42);
setup("abi1985.vm", "evmc_create", create_vm_with_wrong_abi);

evmc_loader_error_code ec;
auto vm = evmc_load_and_create(evmc_test_library_path, &ec);
EXPECT_TRUE(vm == nullptr);
EXPECT_EQ(ec, EVMC_LOADER_ABI_VERSION_MISMATCH);
EXPECT_STREQ(evmc_last_error_msg(),
"EVMC ABI version 42 of abi42.vm mismatches the expected version 6");
const auto expected_error_msg =
"EVMC ABI version 1985 of abi1985.vm mismatches the expected version " +
std::to_string(EVMC_ABI_VERSION);
EXPECT_EQ(evmc_last_error_msg(), expected_error_msg);
EXPECT_EQ(destroy_count, create_count);
}