diff --git a/lib/loader/loader.c b/lib/loader/loader.c index 935d30c..72b2afa 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -42,18 +42,33 @@ #define ATTR_FORMAT(...) #endif -#if !_WIN32 +#if _WIN32 +#define strcpy_sx strcpy_s +#else /* - * Provide strcpy_s() for GNU libc. + * Limited variant of strcpy_s(). + * + * Provided for C standard libraries where strcpy_s() is not available. * The availability check might need to adjusted for other C standard library implementations. */ -static void strcpy_s(char* dest, size_t destsz, const char* src) +#if !defined(EVMC_LOADER_MOCK) +static +#endif + int + strcpy_sx(char* restrict dest, size_t destsz, const char* restrict src) { size_t len = strlen(src); - if (len > destsz - 1) - len = destsz - 1; + if (len >= destsz) + { + // The input src will not fit into the dest buffer. + // Set the first byte of the dest to null to make it effectively empty string + // and return error. + dest[0] = 0; + return 1; + } memcpy(dest, src, len); dest[len] = 0; + return 0; } #endif @@ -124,7 +139,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro const char prefix[] = "evmc_create_"; const size_t prefix_length = strlen(prefix); char prefixed_name[sizeof(prefix) + PATH_MAX_LENGTH]; - strcpy_s(prefixed_name, sizeof(prefixed_name), prefix); + strcpy_sx(prefixed_name, sizeof(prefixed_name), prefix); // Find filename in the path. const char* sep_pos = strrchr(filename, '/'); @@ -142,7 +157,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro name_pos += lib_prefix_length; char* base_name = prefixed_name + prefix_length; - strcpy_s(base_name, PATH_MAX_LENGTH, name_pos); + strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos); // Trim the file extension. char* ext_pos = strrchr(prefixed_name, '.'); diff --git a/test/unittests/test_loader.cpp b/test/unittests/test_loader.cpp index bdde22e..010cbd6 100644 --- a/test/unittests/test_loader.cpp +++ b/test/unittests/test_loader.cpp @@ -16,6 +16,13 @@ static constexpr bool is_windows = false; #endif extern "C" { +#if _WIN32 +#define strcpy_sx strcpy_s +#else +/// Declaration of internal function defined in loader.c. +int strcpy_sx(char* dest, size_t destsz, const char* src); +#endif + /// The library path expected by mocked evmc_test_load_library(). extern const char* evmc_test_library_path; @@ -118,6 +125,28 @@ static evmc_instance* create_failure() return nullptr; } +TEST_F(loader, strcpy_sx) +{ + const char input_empty[] = ""; + const char input_that_fits[] = "x"; + const char input_too_big[] = "12"; + char buf[2] = {0x0f, 0x0f}; + static_assert(sizeof(input_empty) <= sizeof(buf), ""); + static_assert(sizeof(input_that_fits) <= sizeof(buf), ""); + static_assert(sizeof(input_too_big) > sizeof(buf), ""); + + EXPECT_EQ(strcpy_sx(buf, sizeof(buf), input_empty), 0); + EXPECT_EQ(buf[0], 0); + EXPECT_EQ(buf[1], 0x0f); + + EXPECT_EQ(strcpy_sx(buf, sizeof(buf), input_that_fits), 0); + EXPECT_EQ(buf[0], 'x'); + EXPECT_EQ(buf[1], 0); + + EXPECT_NE(strcpy_sx(buf, sizeof(buf), input_too_big), 0); + EXPECT_EQ(buf[0], 0); +} + TEST_F(loader, load_nonexistent) { constexpr auto path = "nonexistent";