Skip to content

Commit 16cc4b3

Browse files
committed
CDRIVER-4679 Revert BSON_HAVE_XSI_STRERROR_R and use strerror_l instead (#1370)
* Revert commit ac436db. * Depend on XSI-compliant strerror_l instead of strerror_r
1 parent 3682145 commit 16cc4b3

File tree

4 files changed

+62
-52
lines changed

4 files changed

+62
-52
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ endif ()
369369
# Enable POSIX features up to POSIX.1-2008 plus the XSI extension and BSD-derived definitions.
370370
# Both _BSD_SOURCE and _DEFAULT_SOURCE are defined for backwards-compatibility with glibc 2.19 and earlier.
371371
# _BSD_SOURCE and _DEFAULT_SOURCE are required by `getpagesize`, `h_errno`, etc.
372-
# _XOPEN_SOURCE=700 is required by `strnlen`, `strerror_r`, etc.
372+
# _XOPEN_SOURCE=700 is required by `strnlen`, `strerror_l`, etc.
373373
add_definitions (-D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_DEFAULT_SOURCE)
374374
list (APPEND CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_DEFAULT_SOURCE)
375375

src/libbson/CMakeLists.txt

+3-35
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@ include (MongoC-Warnings)
1414

1515
set (BSON_OUTPUT_BASENAME "bson" CACHE STRING "Output bson library base name")
1616

17-
include (CheckCSourceCompiles)
1817
include (CheckFunctionExists)
1918
include (CheckIncludeFile)
20-
include (CheckIncludeFiles)
2119
include (CheckStructHasMember)
2220
include (CheckSymbolExists)
23-
include (InstallRequiredSystemLibraries)
2421
include (TestBigEndian)
22+
include (InstallRequiredSystemLibraries)
2523

2624
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/build/cmake)
2725

@@ -85,6 +83,8 @@ else ()
8583
set (BSON_OS 1)
8684
endif ()
8785

86+
include (CheckIncludeFiles)
87+
8888
CHECK_INCLUDE_FILE (strings.h BSON_HAVE_STRINGS_H)
8989
if (NOT BSON_HAVE_STRINGS_H)
9090
set (BSON_HAVE_STRINGS_H 0)
@@ -119,38 +119,6 @@ else ()
119119
set (BSON_BYTE_ORDER 1234)
120120
endif ()
121121

122-
# 1. Normally, `defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600` should be enough
123-
# to detect if an XSI-compliant `strerror_r` is available.
124-
# 2. However, GNU provides an incompatible `strerror_r` as an extension,
125-
# requiring an additional test for `!defined(_GNU_SOURCE)`.
126-
# 3. However, musl does not support the GNU extension even when `_GNU_SOURCE` is
127-
# defined, preventing `!defined(_GNU_SOURCE)` from being a portable
128-
# XSI-compliance test.
129-
# 4. However, musl does not provide a feature test macro to detect compilation
130-
# with musl, and workarounds that use internal glibc feature test macros such
131-
# as `defined(__USE_GNU)` are explicitly forbidden by glibc documentation.
132-
# 5. Therefore, give up on using feature test macros: use `CheckCSourceCompiles`
133-
# to test if the current `strerror_r` is XSI-compliant if present.
134-
if (NOT WIN32)
135-
CHECK_C_SOURCE_COMPILES(
136-
[[
137-
#include <string.h>
138-
int test(int errnum, char *buf, size_t buflen) {
139-
return strerror_r (errnum, buf, buflen);
140-
}
141-
int main(void) {}
142-
]]
143-
BSON_HAVE_XSI_STRERROR_R
144-
FAIL_REGEX "int-conversion"
145-
)
146-
endif ()
147-
148-
if (BSON_HAVE_XSI_STRERROR_R)
149-
set(BSON_HAVE_XSI_STRERROR_R 1)
150-
else()
151-
set(BSON_HAVE_XSI_STRERROR_R 0)
152-
endif ()
153-
154122
configure_file (
155123
"${PROJECT_SOURCE_DIR}/src/bson/bson-config.h.in"
156124
"${PROJECT_BINARY_DIR}/src/bson/bson-config.h"

src/libbson/src/bson/bson-config.h.in

-9
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,4 @@
122122
# undef BSON_HAVE_STRLCPY
123123
#endif
124124

125-
126-
/*
127-
* Define to 1 if you have an XSI-compliant strerror_r on your platform.
128-
*/
129-
#define BSON_HAVE_XSI_STRERROR_R @BSON_HAVE_XSI_STRERROR_R@
130-
#if BSON_HAVE_XSI_STRERROR_R != 1
131-
# undef BSON_HAVE_XSI_STRERROR_R
132-
#endif
133-
134125
#endif /* BSON_CONFIG_H */

src/libbson/src/bson/bson-error.c

+58-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
#include "bson-string.h"
2626
#include "bson-types.h"
2727

28+
// See `bson_strerror_r()` definition below.
29+
#if !defined(_WIN32) && !defined(__APPLE__)
30+
#include <locale.h> // uselocale()
31+
#endif
32+
2833

2934
/*
3035
*--------------------------------------------------------------------------
@@ -98,25 +103,71 @@ bson_set_error (bson_error_t *error, /* OUT */
98103
*/
99104

100105
char *
101-
bson_strerror_r (int err_code, /* IN */
102-
char *buf, /* IN */
103-
size_t buflen) /* IN */
106+
bson_strerror_r (int err_code, /* IN */
107+
char *buf BSON_MAYBE_UNUSED, /* IN */
108+
size_t buflen BSON_MAYBE_UNUSED) /* IN */
104109
{
105110
static const char *unknown_msg = "Unknown error";
106111
char *ret = NULL;
107112

108113
#if defined(_WIN32)
114+
// Windows does not provide `strerror_l` or `strerror_r`, but it does
115+
// unconditionally provide `strerror_s`.
109116
if (strerror_s (buf, buflen, err_code) != 0) {
110117
ret = buf;
111118
}
112-
#elif defined(BSON_HAVE_XSI_STRERROR_R)
113-
if (strerror_r (err_code, buf, buflen) == 0) {
114-
ret = buf;
119+
#elif defined(__APPLE__)
120+
// Apple does not provide `strerror_l`, but it does unconditionally provide
121+
// the XSI-compliant `strerror_r`, but only when compiling with Apple Clang.
122+
// GNU extensions may still be a problem if we are being compiled with GCC on
123+
// Apple. Avoid the compatibility headaches with GNU extensions and the musl
124+
// library by assuming the implementation will not cause UB when reading the
125+
// error message string even when `strerror_r` fails, as encouraged (but not
126+
// required) by the POSIX spec (see:
127+
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html#tag_16_574_08).
128+
(void) strerror_r (err_code, buf, buflen);
129+
#elif defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
130+
// The behavior (of `strerror_l`) is undefined if the locale argument to
131+
// `strerror_l()` is the special locale object LC_GLOBAL_LOCALE or is not a
132+
// valid locale object handle.
133+
locale_t locale = uselocale ((locale_t) 0);
134+
// No need to test for error (it can only be [EINVAL]).
135+
if (locale == LC_GLOBAL_LOCALE) {
136+
// Only use our own locale if a thread-local locale was not already set.
137+
// This is just to satisfy `strerror_l`. We do NOT want to unconditionally
138+
// set a thread-local locale.
139+
locale = newlocale (LC_MESSAGES_MASK, "C", (locale_t) 0);
140+
}
141+
BSON_ASSERT (locale != LC_GLOBAL_LOCALE);
142+
143+
// Avoid `strerror_r` compatibility headaches with GNU extensions and the
144+
// musl library by using `strerror_l` instead. Furthermore, `strerror_r` is
145+
// scheduled to be marked as obsolete in favor of `strerror_l` in the
146+
// upcoming POSIX Issue 8 (see:
147+
// https://www.austingroupbugs.net/view.php?id=655).
148+
//
149+
// POSIX Spec: since strerror_l() is required to return a string for some
150+
// errors, an application wishing to check for all error situations should
151+
// set errno to 0, then call strerror_l(), then check errno.
152+
if (locale != (locale_t) 0) {
153+
errno = 0;
154+
ret = strerror_l (err_code, locale);
155+
156+
if (errno != 0) {
157+
ret = NULL;
158+
}
159+
160+
freelocale (locale);
161+
} else {
162+
// Could not obtain a valid `locale_t` object to satisfy `strerror_l`.
163+
// Fallback to `bson_strncpy` below.
115164
}
116165
#elif defined(_GNU_SOURCE)
166+
// Unlikely, but continue supporting use of GNU extension in cases where the
167+
// C Driver is being built without _XOPEN_SOURCE=700.
117168
ret = strerror_r (err_code, buf, buflen);
118169
#else
119-
#error "Unable to find a supported strerror_r candidate"
170+
#error "Unable to find a supported strerror_r candidate"
120171
#endif
121172

122173
if (!ret) {

0 commit comments

Comments
 (0)