Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.20260708.1
1.20260708.2
56 changes: 37 additions & 19 deletions src/bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,27 +35,44 @@ 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().
# 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)
# 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( LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL)
list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc.cpp)
if(WIN32 OR APPLE)
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)

if( LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOLL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOUL
AND LIBOPENCOR_GLIBC_HAS_ISOC23_STRTOULL)
list(REMOVE_ITEM ACTUAL_PYTHON_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/glibc_strtol.cpp)
endif()
endif()

# Build our Python bindings.
Expand Down
122 changes: 122 additions & 0 deletions src/bindings/python/glibc_arc4random.cpp
Original file line number Diff line number Diff line change
@@ -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 <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>

#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<char *>(&res) + offset, sizeof(res) - offset);

if (bytesRead > 0) {
offset += static_cast<size_t>(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<char *>(pBuf) + offset, pSize - offset);

if (bytesRead > 0) {
offset += static_cast<size_t>(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
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

#include <stddef.h>
#include <stdint.h>

#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 <stdlib.h> 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 <stdlib.h>, which would
// conflict with our own forward declarations (different asm labels on the same symbol). Either way, omitting <stdlib.h>
// 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);
Expand Down
Loading