-
Notifications
You must be signed in to change notification settings - Fork 454
CDRIVER-4679 Revert BSON_HAVE_XSI_STRERROR_R and use strerror_l instead #1370
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
Changes from 4 commits
2f39c02
d53fb91
f9bc8c2
58096c0
c71941a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,11 @@ | |
#include "bson-string.h" | ||
#include "bson-types.h" | ||
|
||
// See `bson_strerror_r()` definition below. | ||
#if !defined(_WIN32) && !defined(__APPLE__) | ||
#include <locale.h> // uselocale() | ||
#endif | ||
|
||
|
||
/* | ||
*-------------------------------------------------------------------------- | ||
|
@@ -98,25 +103,69 @@ bson_set_error (bson_error_t *error, /* OUT */ | |
*/ | ||
|
||
char * | ||
bson_strerror_r (int err_code, /* IN */ | ||
char *buf, /* IN */ | ||
size_t buflen) /* IN */ | ||
bson_strerror_r (int err_code, /* IN */ | ||
char *buf BSON_MAYBE_UNUSED, /* IN */ | ||
size_t buflen BSON_MAYBE_UNUSED) /* IN */ | ||
{ | ||
static const char *unknown_msg = "Unknown error"; | ||
char *ret = NULL; | ||
|
||
#if defined(_WIN32) | ||
// Windows does not provide `strerror_l` or `strerror_r`, but it does | ||
// unconditionally provide `strerror_s`. | ||
if (strerror_s (buf, buflen, err_code) != 0) { | ||
ret = buf; | ||
} | ||
#elif defined(BSON_HAVE_XSI_STRERROR_R) | ||
if (strerror_r (err_code, buf, buflen) == 0) { | ||
ret = buf; | ||
#elif defined(__APPLE__) | ||
// Apple does not provide `strerror_l`, but it does unconditionally provide | ||
// the XSI-compliant `strerror_r`, but only when compiling with Apple Clang. | ||
// GNU extensions may still be a problem if we are being compiled with GCC on | ||
// Apple. Avoid the compatibility headaches with GNU extensions and the musl | ||
// library by assuming QoI will not cause UB when reading the error message | ||
// string even when `strerror_r` fails. | ||
(void) strerror_r (err_code, buf, buflen); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we consider using this code path for the final I'll note that PHP core does that (see: php/php-src#11624 (comment)). IMO it's not the ideal approach (I disagree with PHP doing it for all cases, since they never bother to detect the XSI variant), but it generally works. And given that errno translation probably isn't a critical component of libbson, I think a user would be more inclined to receive "Unknown error" at runtime than a build error. Looking at uses of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I elected to have the
The concern isn't with the criticality of For example, The reason for including the note about "assuming QoI will not cause UB" is to document this potential scenario. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah, very good point. I was confused by jumping back and forth between libbson and PHP, which allocates the buffer in https://github.com/php/php-src/blob/3d832da8f53d2c2f93d4ffbd1c807d9a5d66536b/Zend/zend.c#L1631-L1641 (where the worst case scenario there is just an empty string). SGTM. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
FYI the linked function is stil susceptible to the same potential undefined behavior as described for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, right you are again. The |
||
#elif defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700 | ||
// The behavior (of `strerror_l`) is undefined if the locale argument to | ||
// `strerror_l()` is the special locale object LC_GLOBAL_LOCALE or is not a | ||
// valid locale object handle. | ||
locale_t locale = uselocale ((locale_t) 0); | ||
// No need to test for error (it can only be [EINVAL]). | ||
if (locale == LC_GLOBAL_LOCALE) { | ||
// Only use our own locale if a thread-local locale was not already set. | ||
// This is just to satisfy `strerror_l`. We do NOT want to unconditionally | ||
// set a thread-local locale. | ||
locale = newlocale (LC_MESSAGES_MASK, "C", (locale_t) 0); | ||
} | ||
BSON_ASSERT (locale != LC_GLOBAL_LOCALE); | ||
|
||
// Avoid `strerror_r` compatibility headaches with GNU extensions and the | ||
// musl library by using `strerror_l` instead. Furthermore, `strerror_r` is | ||
// scheduled to be marked as obsolete in favor of `strerror_l` in the | ||
// upcoming POSIX Issue 8 (see: | ||
// https://www.austingroupbugs.net/view.php?id=655). | ||
// | ||
// POSIX Spec: since strerror_l() is required to return a string for some | ||
// errors, an application wishing to check for all error situations should | ||
// set errno to 0, then call strerror_l(), then check errno. | ||
if (locale != (locale_t) 0) { | ||
errno = 0; | ||
ret = strerror_l (err_code, locale); | ||
|
||
if (errno != 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a fleeting thought of "what if we wanted to translate :laugh-cry: |
||
ret = NULL; | ||
} | ||
|
||
freelocale (locale); | ||
} else { | ||
// Could not obtain a valid `locale_t` object to satisfy `strerror_l`. | ||
// Fallback to `bson_strncpy` below. | ||
} | ||
#elif defined(_GNU_SOURCE) | ||
// Unlikely, but continue supporting use of GNU extension in cases where the | ||
// C Driver is being built without _XOPEN_SOURCE=700. | ||
ret = strerror_r (err_code, buf, buflen); | ||
#else | ||
#error "Unable to find a supported strerror_r candidate" | ||
#error "Unable to find a supported strerror_r candidate" | ||
#endif | ||
|
||
if (!ret) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.