diff --git a/lib/loader/loader.c b/lib/loader/loader.c index b65accf..861d5ab 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include #if defined(EVMC_LOADER_MOCK) @@ -48,6 +50,27 @@ static void strcpy_s(char* dest, size_t destsz, const char* src) static const char* last_error_msg = NULL; +#define LAST_ERROR_MSG_BUFFER_SIZE 511 + +// Buffer for formatted error messages. +// It has one null byte extra to avoid buffer read overflow during concurrent access. +static char last_error_msg_buffer[LAST_ERROR_MSG_BUFFER_SIZE + 1]; + +// TODO: Change that into a macro - will be simpler and we will get compilers warnings for snprintf +// misuse. +static enum evmc_loader_error_code set_error(enum evmc_loader_error_code error_code, + const char* format, + ...) +{ + va_list args; + va_start(args, format); + if (vsnprintf(last_error_msg_buffer, LAST_ERROR_MSG_BUFFER_SIZE, format, args) < + LAST_ERROR_MSG_BUFFER_SIZE) + last_error_msg = last_error_msg_buffer; + va_end(args); + return error_code; +} + evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code) { @@ -57,14 +80,21 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro if (!filename) { - ec = EVMC_LOADER_INVALID_ARGUMENT; + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, "invalid argument: file name cannot be null"); goto exit; } const size_t length = strlen(filename); - if (length == 0 || length > PATH_MAX_LENGTH) + if (length == 0) { - ec = EVMC_LOADER_INVALID_ARGUMENT; + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, "invalid argument: file name cannot be empty"); + goto exit; + } + else if (length > PATH_MAX_LENGTH) + { + ec = set_error(EVMC_LOADER_INVALID_ARGUMENT, + "invalid argument: file name is too long (%d, maximum allowed length is %d)", + (int)length, PATH_MAX_LENGTH); goto exit; } @@ -73,7 +103,10 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro { // Get error message if available. last_error_msg = DLL_GET_ERROR_MSG(); - ec = EVMC_LOADER_CANNOT_OPEN; + if (last_error_msg) + ec = EVMC_LOADER_CANNOT_OPEN; + else + ec = set_error(EVMC_LOADER_CANNOT_OPEN, "cannot open %s", filename); goto exit; } @@ -128,7 +161,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro if (!create_fn) { DLL_CLOSE(handle); - ec = EVMC_LOADER_SYMBOL_NOT_FOUND; + ec = set_error(EVMC_LOADER_SYMBOL_NOT_FOUND, "EVMC create function not found in %s", + filename); } exit: @@ -156,13 +190,16 @@ struct evmc_instance* evmc_load_and_create(const char* filename, struct evmc_instance* instance = create_fn(); if (!instance) { - *error_code = EVMC_LOADER_INSTANCE_CREATION_FAILURE; + *error_code = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, + "creating EVMC instance of %s has failed", filename); return NULL; } if (!evmc_is_abi_compatible(instance)) { - *error_code = EVMC_LOADER_ABI_VERSION_MISMATCH; + *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); return NULL; } diff --git a/test/unittests/test_loader.cpp b/test/unittests/test_loader.cpp index 3ff2dcc..b3af7ee 100644 --- a/test/unittests/test_loader.cpp +++ b/test/unittests/test_loader.cpp @@ -66,8 +66,14 @@ TEST_F(loader, load_long_path) const std::string path(5000, 'a'); evmc_loader_error_code ec; EXPECT_EQ(evmc_load(path.c_str(), &ec), nullptr); + EXPECT_STREQ(evmc_last_error_msg(), + "invalid argument: file name is too long (5000, maximum allowed length is 4096)"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); EXPECT_EQ(evmc_load(path.c_str(), nullptr), nullptr); + EXPECT_STREQ(evmc_last_error_msg(), + "invalid argument: file name is too long (5000, maximum allowed length is 4096)"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); } TEST_F(loader, load_null_path) @@ -75,15 +81,23 @@ TEST_F(loader, load_null_path) evmc_loader_error_code ec; EXPECT_EQ(evmc_load(nullptr, &ec), nullptr); EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); + EXPECT_STREQ(evmc_last_error_msg(), "invalid argument: file name cannot be null"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); EXPECT_EQ(evmc_load(nullptr, nullptr), nullptr); + EXPECT_STREQ(evmc_last_error_msg(), "invalid argument: file name cannot be null"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); } TEST_F(loader, load_empty_path) { evmc_loader_error_code ec; EXPECT_EQ(evmc_load("", &ec), nullptr); + EXPECT_STREQ(evmc_last_error_msg(), "invalid argument: file name cannot be empty"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT); EXPECT_EQ(evmc_load("", nullptr), nullptr); + EXPECT_STREQ(evmc_last_error_msg(), "invalid argument: file name cannot be empty"); + EXPECT_EQ(evmc_last_error_msg(), nullptr); } TEST_F(loader, load_prefix_aaa) @@ -104,6 +118,7 @@ TEST_F(loader, load_prefix_aaa) EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); ASSERT_NE(fn, nullptr); EXPECT_EQ((uintptr_t)fn(), 0xaaa); + EXPECT_EQ(evmc_last_error_msg(), nullptr); } } @@ -115,6 +130,7 @@ TEST_F(loader, load_eee_bbb) ASSERT_NE(fn, nullptr); EXPECT_EQ(ec, EVMC_LOADER_SUCCESS); EXPECT_EQ((uintptr_t)fn(), 0xeeebbb); + EXPECT_EQ(evmc_last_error_msg(), nullptr); } @@ -162,6 +178,8 @@ TEST_F(loader, load_symbol_not_found) evmc_loader_error_code ec; EXPECT_EQ(evmc_load(evmc_test_library_path, &ec), nullptr); EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND); + EXPECT_EQ(evmc_last_error_msg(), "EVMC create function not found in " + std::string(path)); + EXPECT_EQ(evmc_last_error_msg(), nullptr); EXPECT_EQ(evmc_load(evmc_test_library_path, nullptr), nullptr); } } @@ -187,6 +205,7 @@ TEST_F(loader, load_and_create_failure) auto vm = evmc_load_and_create(evmc_test_library_path, &ec); EXPECT_EQ(vm, nullptr); EXPECT_EQ(ec, EVMC_LOADER_INSTANCE_CREATION_FAILURE); + EXPECT_STREQ(evmc_last_error_msg(), "creating EVMC instance of failure.vm has failed"); } TEST_F(loader, load_and_create_abi_mismatch) @@ -197,4 +216,6 @@ TEST_F(loader, load_and_create_abi_mismatch) auto vm = evmc_load_and_create(evmc_test_library_path, &ec); EXPECT_EQ(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"); }