From 088f9ebf02174620d3b3d6c9910ecb5819095e61 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 8 Jul 2026 14:31:25 +1200 Subject: [PATCH 1/4] New version. --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index d914dc43a..36769b920 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.20260708.1 +1.20260708.2 From b57af74ab190e9f38e4a0f34c2f4df414d7a00d7 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 8 Jul 2026 15:03:32 +1200 Subject: [PATCH 2/4] Python: only compile glibc if needed and if on neither Windows nor macOS. --- src/bindings/python/CMakeLists.txt | 34 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index cf9f28444..0f8551ba9 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -35,26 +35,36 @@ if(LIBOPENCOR_PYTHON_BINDINGS) set(PYTHON_BINDINGS_DIR ${CMAKE_BINARY_DIR}/tests/bindings/python CACHE INTERNAL "Python bindings directory.") # Check whether we need to provide a glibc compatibility wrapper for __isoc23_strtol(), __isoc23_strtoll(), - # __isoc23_strtoul(), and __isoc23_strtoull(). + # __isoc23_strtoul(), __isoc23_strtoull(), arc4random(), arc4random_buf(), and arc4random_uniform(). # Note: indeed, they are used by our prebuilt LLVM+Clang and libssh2 libraries which are compiled using glibc 2.38+ # (Ubuntu 24.04 LTS on GitHub Actions), but there are currently no versions of manylinux that uses glibc # 2.38+, so we need to provide a compatibility wrapper for these functions in order to be able to build our # Python wheels. - include(CheckSymbolExists) - - check_symbol_exists(__isoc23_strtol stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL) - check_symbol_exists(__isoc23_strtoll stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL) - check_symbol_exists(__isoc23_strtoul stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL) - check_symbol_exists(__isoc23_strtoull stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL) - set(ACTUAL_PYTHON_SOURCE_FILES ${PYTHON_SOURCE_FILES}) - if( LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL - AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL - AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL - AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL) + if(WIN32 OR APPLE) list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp) + else() + include(CheckSymbolExists) + + check_symbol_exists(__isoc23_strtol stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL) + check_symbol_exists(__isoc23_strtoll stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL) + check_symbol_exists(__isoc23_strtoul stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL) + check_symbol_exists(__isoc23_strtoull stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL) + check_symbol_exists(arc4random stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM) + check_symbol_exists(arc4random_buf stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF) + check_symbol_exists(arc4random_uniform stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) + + if( LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL + AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL + AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL + AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL + AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM + AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF + AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) + list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp) + endif() endif() # Build our Python bindings. From 2fdcf2d000b13c080eaddf4dd9ba95652a8cd300 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 8 Jul 2026 15:02:37 +1200 Subject: [PATCH 3/4] Added arc4random compatibility wrappers for glibc. --- src/bindings/python/glibc.cpp | 123 ++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 4 deletions(-) diff --git a/src/bindings/python/glibc.cpp b/src/bindings/python/glibc.cpp index b0f681788..66b97c578 100644 --- a/src/bindings/python/glibc.cpp +++ b/src/bindings/python/glibc.cpp @@ -14,13 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include +#include +#include +#include +#include + #if defined(__linux__) && defined(__GNUC__) // On glibc 2.38+, strtol(), strtoll(), strtoul(), and strtoull() are redirected to __isoc23_strtol(), // __isoc23_strtoll(), __isoc23_strtoul(), and __isoc23_strtoull(), respectively, either via preprocessor macro (Intel) -// or via __asm__("__isoc23_*") on the declaration (ARM). The latter is a compiler-level redirect that cannot be undone -// with #undef, so calling strtol() inside our __isoc23_strtol() wrapper would cause infinite recursion on ARM. To -// bypass this, we forward-declare the underlying functions ourselves without the compiler-level redirect, giving us a -// direct reference to glibc's implementation unaffected by any asm redirect on the standard names. +// or via __asm__("__isoc23_*") on the declaration (ARM). Both approaches would cause any call to strtol() inside our +// __isoc23_strtol() wrapper to resolve back to __isoc23_strtol() (either via macro expansion on Intel or via the asm +// redirect on ARM), leading to infinite recursion. To bypass this, we forward-declare the underlying strtol functions +// ourselves without the compiler-level redirect, giving us a direct reference to glibc's implementation that is +// unaffected by any macro or asm redirect on the standard names. +// +// Important: we do NOT include because on Intel, glibc redirects using preprocessor macros: +// #define strtol __isoc23_strtol +// #define strtoll __isoc23_strtoll +// #define strtoul __isoc23_strtoul +// #define strtoull __isoc23_strtoull +// which would macro-expand both our forward declarations and the return strtol() calls inside our wrappers, making +// them recursive. On ARM, glibc instead uses __asm__("__isoc23_strtol") on the declarations in , which would +// conflict with our own forward declarations (different asm labels on the same symbol). Either way, omitting +// entirely avoids both problems while still allowing us to forward-declare the real symbols. extern "C" { long strtol(const char *pString, char **pEndPtr, int pBase); @@ -49,4 +66,102 @@ __attribute__((weak)) unsigned long long __isoc23_strtoull(const char *pString, } } +// arc4random(), arc4random_buf(), and arc4random_uniform() were added in glibc 2.36, so we need to provide a +// compatibility implementation. + +extern "C" { +__attribute__((weak)) uint32_t arc4random() +{ + int fd; + + do { + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + } while ((fd < 0) && (errno == EINTR)); + + if (fd < 0) { + __builtin_abort(); + } + + uint32_t res = 0; + size_t offset = 0; + + while (offset < sizeof(res)) { + ssize_t bytesRead = read(fd, reinterpret_cast(&res) + offset, sizeof(res) - offset); + + if (bytesRead > 0) { + offset += static_cast(bytesRead); + } else if ((bytesRead < 0) && (errno == EINTR)) { + continue; + } else { + // EOF or unrecoverable error. + + break; + } + } + + close(fd); + + if (offset != sizeof(res)) { + __builtin_abort(); + } + + return res; +} + +__attribute__((weak)) void arc4random_buf(void *pBuf, size_t pSize) +{ + if (pSize == 0) { + return; + } + + int fd; + + do { + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + } while ((fd < 0) && (errno == EINTR)); + + if (fd < 0) { + __builtin_abort(); + } + + size_t offset = 0; + + while (offset < pSize) { + ssize_t bytesRead = read(fd, static_cast(pBuf) + offset, pSize - offset); + + if (bytesRead > 0) { + offset += static_cast(bytesRead); + } else if ((bytesRead < 0) && (errno == EINTR)) { + continue; + } else { + // EOF or unrecoverable error. + + break; + } + } + + close(fd); + + if (offset != pSize) { + __builtin_abort(); + } +} + +__attribute__((weak)) uint32_t arc4random_uniform(uint32_t pUpperBound) +{ + if (pUpperBound < 2) { + return 0; + } + + uint32_t rejectionThreshold = -pUpperBound % pUpperBound; + uint32_t number; + + do { + number = arc4random(); + } while (number < rejectionThreshold); + + return number % pUpperBound; +} +} + #endif From 3af817b95b16c4ec29e13b1638b5d5cb0410e130 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Wed, 8 Jul 2026 17:54:16 +1200 Subject: [PATCH 4/4] glibc: split compatibility wrappers. --- src/bindings/python/CMakeLists.txt | 40 +++--- src/bindings/python/glibc_arc4random.cpp | 122 ++++++++++++++++++ .../python/{glibc.cpp => glibc_strtol.cpp} | 101 --------------- 3 files changed, 146 insertions(+), 117 deletions(-) create mode 100644 src/bindings/python/glibc_arc4random.cpp rename src/bindings/python/{glibc.cpp => glibc_strtol.cpp} (59%) diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index 0f8551ba9..bb581eccb 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -18,7 +18,8 @@ set(PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/file.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/glibc_arc4random.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/glibc_strtol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sed.cpp @@ -34,36 +35,43 @@ if(LIBOPENCOR_PYTHON_BINDINGS) set(PYTHON_BINDINGS_DIR ${CMAKE_BINARY_DIR}/tests/bindings/python CACHE INTERNAL "Python bindings directory.") - # Check whether we need to provide a glibc compatibility wrapper for __isoc23_strtol(), __isoc23_strtoll(), - # __isoc23_strtoul(), __isoc23_strtoull(), arc4random(), arc4random_buf(), and arc4random_uniform(). - # Note: indeed, they are used by our prebuilt LLVM+Clang and libssh2 libraries which are compiled using glibc 2.38+ - # (Ubuntu 24.04 LTS on GitHub Actions), but there are currently no versions of manylinux that uses glibc - # 2.38+, so we need to provide a compatibility wrapper for these functions in order to be able to build our - # Python wheels. + # Check whether we need to provide glibc compatibility wrappers for __isoc23_strtol(), __isoc23_strtoll(), + # __isoc23_strtoul(), __isoc23_strtoull() (glibc 2.38+), and arc4random(), arc4random_buf(), arc4random_uniform() + # (glibc 2.36+). These are referenced by our prebuilt LLVM+Clang and libssh2 libraries which are compiled using + # glibc 2.38+ (Ubuntu 24.04 LTS on GitHub Actions), but there are currently no versions of manylinux that use glibc + # 2.38+, so we need compatibility wrappers to build Python wheels. Each symbol group is guarded independently so + # that only the wrappers for actually missing functions are compiled into the module, avoiding weak-symbol + # interposition against the native glibc implementation. set(ACTUAL_PYTHON_SOURCE_FILES ${PYTHON_SOURCE_FILES}) if(WIN32 OR APPLE) - list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp) + list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/glibc_arc4random.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/glibc_strtol.cpp) else() include(CheckSymbolExists) + check_symbol_exists(arc4random stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM) + check_symbol_exists(arc4random_buf stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF) + check_symbol_exists(arc4random_uniform stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) + + if( LIBOPENCOR_GLIBC_HAS_ARC4RANDOM + AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF + AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) + list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc_arc4random.cpp) + endif() + check_symbol_exists(__isoc23_strtol stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL) check_symbol_exists(__isoc23_strtoll stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL) check_symbol_exists(__isoc23_strtoul stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL) check_symbol_exists(__isoc23_strtoull stdlib.h LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL) - check_symbol_exists(arc4random stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM) - check_symbol_exists(arc4random_buf stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF) - check_symbol_exists(arc4random_uniform stdlib.h LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) if( LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL - AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL - AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM - AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_BUF - AND LIBOPENCOR_GLIBC_HAS_ARC4RANDOM_UNIFORM) - list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp) + AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL) + list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc_strtol.cpp) endif() endif() diff --git a/src/bindings/python/glibc_arc4random.cpp b/src/bindings/python/glibc_arc4random.cpp new file mode 100644 index 000000000..e976b6dea --- /dev/null +++ b/src/bindings/python/glibc_arc4random.cpp @@ -0,0 +1,122 @@ +/* +Copyright libOpenCOR contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include +#include +#include + +#if defined(__linux__) && defined(__GNUC__) +// arc4random(), arc4random_buf(), and arc4random_uniform() were added in glibc 2.36, so we need to provide a +// compatibility implementation on older glibc versions. + +extern "C" { +__attribute__((weak)) uint32_t arc4random() +{ + int fd; + + do { + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + } while ((fd < 0) && (errno == EINTR)); + + if (fd < 0) { + __builtin_abort(); + } + + uint32_t res = 0; + size_t offset = 0; + + while (offset < sizeof(res)) { + ssize_t bytesRead = read(fd, reinterpret_cast(&res) + offset, sizeof(res) - offset); + + if (bytesRead > 0) { + offset += static_cast(bytesRead); + } else if ((bytesRead < 0) && (errno == EINTR)) { + continue; + } else { + // EOF or unrecoverable error. + + break; + } + } + + close(fd); + + if (offset != sizeof(res)) { + __builtin_abort(); + } + + return res; +} + +__attribute__((weak)) void arc4random_buf(void *pBuf, size_t pSize) +{ + if (pSize == 0) { + return; + } + + int fd; + + do { + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + } while ((fd < 0) && (errno == EINTR)); + + if (fd < 0) { + __builtin_abort(); + } + + size_t offset = 0; + + while (offset < pSize) { + ssize_t bytesRead = read(fd, static_cast(pBuf) + offset, pSize - offset); + + if (bytesRead > 0) { + offset += static_cast(bytesRead); + } else if ((bytesRead < 0) && (errno == EINTR)) { + continue; + } else { + // EOF or unrecoverable error. + + break; + } + } + + close(fd); + + if (offset != pSize) { + __builtin_abort(); + } +} + +__attribute__((weak)) uint32_t arc4random_uniform(uint32_t pUpperBound) +{ + if (pUpperBound < 2) { + return 0; + } + + uint32_t rejectionThreshold = -pUpperBound % pUpperBound; + uint32_t number; + + do { + number = arc4random(); + } while (number < rejectionThreshold); + + return number % pUpperBound; +} +} + +#endif diff --git a/src/bindings/python/glibc.cpp b/src/bindings/python/glibc_strtol.cpp similarity index 59% rename from src/bindings/python/glibc.cpp rename to src/bindings/python/glibc_strtol.cpp index 66b97c578..c305765d6 100644 --- a/src/bindings/python/glibc.cpp +++ b/src/bindings/python/glibc_strtol.cpp @@ -14,11 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include -#include #include #include -#include #if defined(__linux__) && defined(__GNUC__) // On glibc 2.38+, strtol(), strtoll(), strtoul(), and strtoull() are redirected to __isoc23_strtol(), @@ -66,102 +63,4 @@ __attribute__((weak)) unsigned long long __isoc23_strtoull(const char *pString, } } -// arc4random(), arc4random_buf(), and arc4random_uniform() were added in glibc 2.36, so we need to provide a -// compatibility implementation. - -extern "C" { -__attribute__((weak)) uint32_t arc4random() -{ - int fd; - - do { - fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); - } while ((fd < 0) && (errno == EINTR)); - - if (fd < 0) { - __builtin_abort(); - } - - uint32_t res = 0; - size_t offset = 0; - - while (offset < sizeof(res)) { - ssize_t bytesRead = read(fd, reinterpret_cast(&res) + offset, sizeof(res) - offset); - - if (bytesRead > 0) { - offset += static_cast(bytesRead); - } else if ((bytesRead < 0) && (errno == EINTR)) { - continue; - } else { - // EOF or unrecoverable error. - - break; - } - } - - close(fd); - - if (offset != sizeof(res)) { - __builtin_abort(); - } - - return res; -} - -__attribute__((weak)) void arc4random_buf(void *pBuf, size_t pSize) -{ - if (pSize == 0) { - return; - } - - int fd; - - do { - fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); - } while ((fd < 0) && (errno == EINTR)); - - if (fd < 0) { - __builtin_abort(); - } - - size_t offset = 0; - - while (offset < pSize) { - ssize_t bytesRead = read(fd, static_cast(pBuf) + offset, pSize - offset); - - if (bytesRead > 0) { - offset += static_cast(bytesRead); - } else if ((bytesRead < 0) && (errno == EINTR)) { - continue; - } else { - // EOF or unrecoverable error. - - break; - } - } - - close(fd); - - if (offset != pSize) { - __builtin_abort(); - } -} - -__attribute__((weak)) uint32_t arc4random_uniform(uint32_t pUpperBound) -{ - if (pUpperBound < 2) { - return 0; - } - - uint32_t rejectionThreshold = -pUpperBound % pUpperBound; - uint32_t number; - - do { - number = arc4random(); - } while (number < rejectionThreshold); - - return number % pUpperBound; -} -} - #endif