diff --git a/src/misc.c b/src/misc.c index 1de53fcd..3f2652d4 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1953,17 +1953,25 @@ char *ssh_strreplace(const char *src, const char *pattern, const char *replace) */ char *ssh_strerror(int err_num, char *buf, size_t buflen) { -#if defined(_WIN32) - strerror_s(buf, buflen, err_num); - return buf; -#elif defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE) +#if defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE) /* GNU extension on Linux */ return strerror_r(err_num, buf, buflen); #else - /* POSIX version available for example on FreeBSD */ - strerror_r(err_num, buf, buflen); - return buf; + int rv; + +#if defined(_WIN32) + rv = strerror_s(buf, buflen, err_num); +#else + /* POSIX version available for example on FreeBSD or in musl libc */ + rv = strerror_r(err_num, buf, buflen); #endif /* _WIN32 */ + + /* make sure the buffer is initialized and terminated with NULL */ + if (-rv == ERANGE) { + buf[0] = '\0'; + } + return buf; +#endif /* defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE) */ } /** @} */ diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c index 6fdf3ab5..6d6cf0a9 100644 --- a/tests/unittests/torture_misc.c +++ b/tests/unittests/torture_misc.c @@ -732,6 +732,34 @@ static void torture_ssh_strreplace(void **state) assert_null(replaced_string); } +static void torture_ssh_strerror(void **state) +{ + char buf[1024]; + size_t bufflen = sizeof(buf); + char *out = NULL; + + (void) state; + + out = ssh_strerror(ENOENT, buf, 1); /* too short */ + assert_string_equal(out, "\0"); + + out = ssh_strerror(256, buf, bufflen); /* unknown error code */ + /* This error is always different: + * Freebd: "Unknown error: 256" + * MinGW/Win: "Unknown error" + * Linux/glibc: "Unknown error 256" + * Alpine/musl: "No error information" + */ + assert_non_null(out); + + out = ssh_strerror(ENOMEM, buf, bufflen); + /* This actually differs too for glibc/musl: + * musl: "Out of memory" + * everything else: "Cannot allocate memory" + */ + assert_non_null(out); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -755,6 +783,7 @@ int torture_run_tests(void) { cmocka_unit_test(torture_ssh_mkdirs), cmocka_unit_test(torture_ssh_quote_file_name), cmocka_unit_test(torture_ssh_strreplace), + cmocka_unit_test(torture_ssh_strerror), }; ssh_init();