Skip to content

Different strerror_r function usage #231

@Esonhugh

Description

@Esonhugh

When i was building Havoc Framework which imports toml11 on my m1 mac, I got error like

error stack

In file included from /tmp/Havoc/client/src/Havoc/PythonApi/UI/PyLoggerClass.cc:6:
In file included from /tmp/Havoc/client/include/Havoc/PythonApi/PythonApi.h:4:
In file included from /tmp/Havoc/client/include/global.hpp:36:
In file included from /tmp/Havoc/client/include/External.h:6:
In file included from /tmp/Havoc/client/external/toml/toml.hpp:32:
In file included from /tmp/Havoc/client/external/toml/toml/parser.hpp:14:
In file included from /tmp/Havoc/client/external/toml/toml/value.hpp:8:
/tmp/Havoc/client/external/toml/toml/exception.hpp:44:17: error: cannot initialize a variable of type 'const char *' with an rvalue of type 'int'
    const char* result = strerror_r(errnum, buf.data(), bufsize);
                ^        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning and 1 error generated.

Then I found this project and found different process here.

# ./toml/exception.hpp
inline std::string str_error(int errnum)
{
    // C++ standard strerror is not thread-safe.
    // C11 provides thread-safe version of this function, `strerror_s`, but it
    // is not available in C++.
    // To avoid using std::strerror, we need to use platform-specific functions.
    // If none of the conditions are met, it calls std::strerror as a fallback.
#ifdef _MSC_VER // MSVC
    constexpr std::size_t bufsize = 256;
    std::array<char, bufsize> buf;
    buf.fill('\0');
    const auto result = strerror_s(buf.data(), bufsize, errnum); # here use auto 
    if(result != 0)
    {
        return std::string("strerror_s failed");
    }
    else
    {
        return std::string(buf.data());
    }
#elif defined(_GNU_SOURCE)
    constexpr std::size_t bufsize = 256;
    std::array<char, bufsize> buf;
    buf.fill('\0');
    const char* result = strerror_r(errnum, buf.data(), bufsize); # here receive as const char*
    return std::string(result);
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
    constexpr std::size_t bufsize = 256;
    std::array<char, bufsize> buf;
    buf.fill('\0');
    const int result = strerror_r(errnum, buf.data(), bufsize); # but here is int
    if (result != 0)
    {
        return std::string("strerror_r failed");
    }
    else
    {
        return std::string(buf.data());
    }
#else // fallback
    return std::strerror(errnum);
#endif

but my macos c header looks like

# /Library/Developer/CommandLineTools/SDKs/MacOSX13.3.sdk/usr/include/string.h
/* Additional functionality provided by:
 * POSIX.1-2001
 */

#if __DARWIN_C_LEVEL >= 200112L
__BEGIN_DECLS
int	 strerror_r(int __errnum, char *__strerrbuf, size_t __buflen);
char	*strdup(const char *__s1);
void	*memccpy(void *__dst, const void *__src, int __c, size_t __n);
__END_DECLS
#endif /* __DARWIN_C_LEVEL >= 200112L */

current fix

I changed the code like following to patch a little but it's not good I think.


inline std::string str_error(int errnum) {
  // C++ standard strerror is not thread-safe.
  // C11 provides thread-safe version of this function, `strerror_s`, but it
  // is not available in C++.
  // To avoid using std::strerror, we need to use platform-specific functions.
  // If none of the conditions are met, it calls std::strerror as a fallback.
#ifdef _MSC_VER // MSVC
  constexpr std::size_t bufsize = 256;
  std::array<char, bufsize> buf;
  buf.fill('\0');
  const auto result = strerror_s(buf.data(), bufsize, errnum);
  if (result != 0) {
    return std::string("strerror_s failed");
  } else {
    return std::string(buf.data());
  }
#elif defined(_GNU_SOURCE)
  constexpr std::size_t bufsize = 256;
  std::array<char, bufsize> buf;
  buf.fill('\0');
  const int result = strerror_r(errnum, buf.data(), bufsize);
  if (result != 0) {
    return std::string("strerror_r failed");
  } else {
    return std::string(buf.data());
  }
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) ||              \
    (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
  constexpr std::size_t bufsize = 256;
  std::array<char, bufsize> buf;
  buf.fill('\0');
  const int result = strerror_r(errnum, buf.data(), bufsize);
  if (result != 0) {
    return std::string("strerror_r failed");
  } else {
    return std::string(buf.data());
  }
#else // fallback
  return std::strerror(errnum);
#endif
}

It looks like to following the (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) condition and compile the code.

Is it a bug or need more classification in macro?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions