From 725cfd7dceb92d7fd1560fe19d858deb7220808e Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Mon, 9 Mar 2026 18:20:44 -0400 Subject: [PATCH 1/7] ssl-shim non-split pass, split-handshake fails Made-with: Cursor Signed-off-by: Julian Huang --- programs/CMakeLists.txt | 20 ++++ programs/main.cpp | 9 ++ programs/ssl-handshaker/CMakeLists.txt | 104 +++++++++++++++++++ programs/ssl-handshaker/SslHandshaker.cpp | 6 ++ programs/ssl-handshaker/glibc_compat.c | 9 ++ programs/ssl-shim/CMakeLists.txt | 115 ++++++++++++++++++++++ programs/ssl-shim/SslShim.cpp | 6 ++ programs/ssl-shim/glibc_compat.c | 9 ++ 8 files changed, 278 insertions(+) create mode 100644 programs/ssl-handshaker/CMakeLists.txt create mode 100644 programs/ssl-handshaker/SslHandshaker.cpp create mode 100644 programs/ssl-handshaker/glibc_compat.c create mode 100644 programs/ssl-shim/CMakeLists.txt create mode 100644 programs/ssl-shim/SslShim.cpp create mode 100644 programs/ssl-shim/glibc_compat.c diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index 59cf435fdf82..8116e063086d 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -127,6 +127,11 @@ if (ENABLE_CLICKHOUSE_KEEPER) add_subdirectory (keeper) endif() +if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) + add_subdirectory (ssl-shim) + add_subdirectory (ssl-handshaker) +endif() + if (ENABLE_CLICKHOUSE_SELF_EXTRACTING AND NOT ENABLE_DUMMY_LAUNCHERS) add_subdirectory (self-extracting) endif () @@ -141,6 +146,14 @@ endif () target_link_libraries (clickhouse PRIVATE clickhouse_common_io ${HARMFUL_LIB}) target_include_directories (clickhouse PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +# The ssl-shim/ssl-handshaker objects are compiled with the system compiler +# (libstdc++ ABI) to match the Docker-built aws-lc libraries. Linking +# libstdc++.a alongside ClickHouse's libc++/libcxxabi creates a few +# duplicate symbols (std::exception, etc.) that are safe to coalesce. +if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) + target_link_options(clickhouse PRIVATE "LINKER:--allow-multiple-definition") +endif() + if (ENABLE_CLICKHOUSE_KEEPER) clickhouse_target_link_split_lib(clickhouse keeper) endif() @@ -225,6 +238,13 @@ if (ENABLE_CLICKHOUSE_KEEPER_CLIENT) list(APPEND CLICKHOUSE_BUNDLE clickhouse-keeper-client) endif () +if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) + clickhouse_program_install(clickhouse-ssl-shim ssl-shim) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + clickhouse_program_install(clickhouse-ssl-handshaker ssl-handshaker) + endif() +endif() + add_custom_target (clickhouse-bundle ALL DEPENDS ${CLICKHOUSE_BUNDLE}) if (USE_BINARY_HASH) diff --git a/programs/main.cpp b/programs/main.cpp index 4240778f768f..b91f9291a6e2 100644 --- a/programs/main.cpp +++ b/programs/main.cpp @@ -64,6 +64,11 @@ int mainEntryClickHouseKeeperBench(int argc, char ** argv); int mainEntryClickHouseKeeperDataDumper(int argc, char ** argv); #endif +#if defined(FIPS_CLICKHOUSE) +int mainEntryClickHouseSslShim(int argc, char ** argv); +int mainEntryClickHouseSslHandshaker(int argc, char ** argv); +#endif + // install int mainEntryClickHouseInstall(int argc, char ** argv); int mainEntryClickHouseStart(int argc, char ** argv); @@ -115,6 +120,10 @@ std::pair clickhouse_applications[] = #endif #if USE_NURAFT {"keeper-data-dumper", mainEntryClickHouseKeeperDataDumper}, +#endif +#if defined(FIPS_CLICKHOUSE) + {"ssl-shim", mainEntryClickHouseSslShim}, + {"ssl-handshaker", mainEntryClickHouseSslHandshaker}, #endif // install {"install", mainEntryClickHouseInstall}, diff --git a/programs/ssl-handshaker/CMakeLists.txt b/programs/ssl-handshaker/CMakeLists.txt new file mode 100644 index 000000000000..cf9f9e775959 --- /dev/null +++ b/programs/ssl-handshaker/CMakeLists.txt @@ -0,0 +1,104 @@ +if(NOT FIPS_CLICKHOUSE OR NOT DEFINED AWSLC_SRC_DIR) + return() +endif() + +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + return() +endif() + +set(AWSLC_SSL_TEST_DIR "${AWSLC_SRC_DIR}/ssl/test") +set(AWSLC_LIBSSL "${CMAKE_BINARY_DIR}/awslc-build/output/libssl.a") +set(AWSLC_LIBCRYPTO "${CMAKE_BINARY_DIR}/awslc-build/output/libcrypto.a") + +set(HSHAKER_OBJ_DIR "${CMAKE_CURRENT_BINARY_DIR}/awslc_handshaker_objs") + +set(AWSLC_HSHAKER_CXX_SRCS + ${AWSLC_SSL_TEST_DIR}/async_bio.cc + ${AWSLC_SSL_TEST_DIR}/handshake_util.cc + ${AWSLC_SSL_TEST_DIR}/handshaker.cc + ${AWSLC_SSL_TEST_DIR}/mock_quic_transport.cc + ${AWSLC_SSL_TEST_DIR}/packeted_bio.cc + ${AWSLC_SSL_TEST_DIR}/settings_writer.cc + ${AWSLC_SSL_TEST_DIR}/test_config.cc + ${AWSLC_SSL_TEST_DIR}/test_state.cc + ${AWSLC_SRC_DIR}/crypto/test/test_util.cc +) + +set(AWSLC_HSHAKER_INCLUDE_FLAGS + -I${AWSLC_SRC_DIR}/include + -I${AWSLC_SRC_DIR} + -I${AWSLC_SSL_TEST_DIR} +) + +set(AWSLC_HSHAKER_OBJS) +foreach(src ${AWSLC_HSHAKER_CXX_SRCS}) + get_filename_component(base "${src}" NAME_WE) + get_filename_component(dir "${src}" DIRECTORY) + string(MD5 dir_hash "${dir}") + string(SUBSTRING "${dir_hash}" 0 8 dir_tag) + set(obj "${HSHAKER_OBJ_DIR}/${base}_${dir_tag}.o") + + set(extra_defs "") + if("${base}" STREQUAL "handshaker") + set(extra_defs -Dmain=handshaker_main) + endif() + + add_custom_command( + OUTPUT "${obj}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${HSHAKER_OBJ_DIR}" + COMMAND /usr/bin/c++ + -std=c++17 -fPIC -g -O2 + ${AWSLC_HSHAKER_INCLUDE_FLAGS} + ${extra_defs} + -w + -c "${src}" + -o "${obj}" + DEPENDS "${src}" build-awslc + COMMENT "Building aws-lc handshaker object ${base}.cc (system C++ ABI)" + ) + list(APPEND AWSLC_HSHAKER_OBJS "${obj}") +endforeach() + +set(GLIBC_COMPAT_HS_OBJ "${HSHAKER_OBJ_DIR}/glibc_compat.o") +add_custom_command( + OUTPUT "${GLIBC_COMPAT_HS_OBJ}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${HSHAKER_OBJ_DIR}" + COMMAND /usr/bin/cc -fPIC -O2 -w + -c "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + -o "${GLIBC_COMPAT_HS_OBJ}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + COMMENT "Building glibc C23 compat shim (handshaker)" +) +list(APPEND AWSLC_HSHAKER_OBJS "${GLIBC_COMPAT_HS_OBJ}") + +set(AWSLC_HSHAKER_LIB "${HSHAKER_OBJ_DIR}/libawslc_handshaker.a") +add_custom_command( + OUTPUT "${AWSLC_HSHAKER_LIB}" + COMMAND ${CMAKE_AR} rcs "${AWSLC_HSHAKER_LIB}" ${AWSLC_HSHAKER_OBJS} + DEPENDS ${AWSLC_HSHAKER_OBJS} + COMMENT "Archiving libawslc_handshaker.a" +) +add_custom_target(awslc_handshaker_lib DEPENDS "${AWSLC_HSHAKER_LIB}") + +set(CLICKHOUSE_SSL_HANDSHAKER_SOURCES SslHandshaker.cpp) +find_file(LIBSTDCXX_STATIC_HS libstdc++.a + PATHS /usr/lib/gcc/x86_64-linux-gnu + PATH_SUFFIXES 13 12 11 + NO_DEFAULT_PATH +) +if(NOT LIBSTDCXX_STATIC_HS) + message(FATAL_ERROR "Could not find static libstdc++.a needed for ssl-handshaker") +endif() + +set(CLICKHOUSE_SSL_HANDSHAKER_LINK + PRIVATE + "${AWSLC_HSHAKER_LIB}" + "${AWSLC_LIBSSL}" + "${AWSLC_LIBCRYPTO}" + "${LIBSTDCXX_STATIC_HS}" +) +set(CLICKHOUSE_SSL_HANDSHAKER_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) + +clickhouse_program_add(ssl-handshaker) +add_dependencies(clickhouse-ssl-handshaker-lib awslc_handshaker_lib build-awslc) +target_compile_options(clickhouse-ssl-handshaker-lib PRIVATE -Wno-everything) diff --git a/programs/ssl-handshaker/SslHandshaker.cpp b/programs/ssl-handshaker/SslHandshaker.cpp new file mode 100644 index 000000000000..a86e927a7d47 --- /dev/null +++ b/programs/ssl-handshaker/SslHandshaker.cpp @@ -0,0 +1,6 @@ +extern int handshaker_main(int argc, char ** argv); + +int mainEntryClickHouseSslHandshaker(int argc, char ** argv) +{ + return handshaker_main(argc, argv); +} diff --git a/programs/ssl-handshaker/glibc_compat.c b/programs/ssl-handshaker/glibc_compat.c new file mode 100644 index 000000000000..38973e0bcecb --- /dev/null +++ b/programs/ssl-handshaker/glibc_compat.c @@ -0,0 +1,9 @@ +#include + +unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { + return strtoull(nptr, endptr, base); +} + +long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { + return strtoll(nptr, endptr, base); +} diff --git a/programs/ssl-shim/CMakeLists.txt b/programs/ssl-shim/CMakeLists.txt new file mode 100644 index 000000000000..6cc8798298a4 --- /dev/null +++ b/programs/ssl-shim/CMakeLists.txt @@ -0,0 +1,115 @@ +if(NOT FIPS_CLICKHOUSE OR NOT DEFINED AWSLC_SRC_DIR) + return() +endif() + +set(AWSLC_SSL_TEST_DIR "${AWSLC_SRC_DIR}/ssl/test") +set(AWSLC_LIBSSL "${CMAKE_BINARY_DIR}/awslc-build/output/libssl.a") +set(AWSLC_LIBCRYPTO "${CMAKE_BINARY_DIR}/awslc-build/output/libcrypto.a") + +set(SHIM_OBJ_DIR "${CMAKE_CURRENT_BINARY_DIR}/awslc_shim_objs") + +set(AWSLC_SHIM_CXX_SRCS + ${AWSLC_SSL_TEST_DIR}/async_bio.cc + ${AWSLC_SSL_TEST_DIR}/bssl_shim.cc + ${AWSLC_SSL_TEST_DIR}/handshake_util.cc + ${AWSLC_SSL_TEST_DIR}/mock_quic_transport.cc + ${AWSLC_SSL_TEST_DIR}/packeted_bio.cc + ${AWSLC_SSL_TEST_DIR}/settings_writer.cc + ${AWSLC_SSL_TEST_DIR}/ssl_transfer.cc + ${AWSLC_SSL_TEST_DIR}/test_config.cc + ${AWSLC_SSL_TEST_DIR}/test_state.cc + ${AWSLC_SRC_DIR}/crypto/test/test_util.cc +) + +set(AWSLC_SHIM_INCLUDE_FLAGS + -I${AWSLC_SRC_DIR}/include + -I${AWSLC_SRC_DIR} + -I${AWSLC_SSL_TEST_DIR} +) + +# Build each aws-lc shim source file with the system compiler (matching +# the libstdc++ ABI of the Docker-built libssl.a / libcrypto.a). +# This avoids the libc++ ↔ libstdc++ C++ ABI mismatch for internal +# types like bssl::UniquePtr. +set(AWSLC_SHIM_OBJS) +foreach(src ${AWSLC_SHIM_CXX_SRCS}) + get_filename_component(base "${src}" NAME_WE) + get_filename_component(dir "${src}" DIRECTORY) + # Disambiguate files with same basename from different directories + string(MD5 dir_hash "${dir}") + string(SUBSTRING "${dir_hash}" 0 8 dir_tag) + set(obj "${SHIM_OBJ_DIR}/${base}_${dir_tag}.o") + + set(extra_defs "") + if("${base}" STREQUAL "bssl_shim") + set(extra_defs -Dmain=bssl_shim_main) + endif() + + add_custom_command( + OUTPUT "${obj}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${SHIM_OBJ_DIR}" + COMMAND /usr/bin/c++ + -std=c++17 -fPIC -g -O2 + ${AWSLC_SHIM_INCLUDE_FLAGS} + ${extra_defs} + -w + -c "${src}" + -o "${obj}" + DEPENDS "${src}" build-awslc + COMMENT "Building aws-lc shim object ${base}.cc (system C++ ABI)" + ) + list(APPEND AWSLC_SHIM_OBJS "${obj}") +endforeach() + +# Compile the glibc C23 compatibility shim (provides __isoc23_strtoll etc. +# which newer GCC/glibc emit but the ClickHouse sysroot's libc lacks). +set(GLIBC_COMPAT_OBJ "${SHIM_OBJ_DIR}/glibc_compat.o") +add_custom_command( + OUTPUT "${GLIBC_COMPAT_OBJ}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${SHIM_OBJ_DIR}" + COMMAND /usr/bin/cc -fPIC -O2 -w + -c "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + -o "${GLIBC_COMPAT_OBJ}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + COMMENT "Building glibc C23 compat shim" +) +list(APPEND AWSLC_SHIM_OBJS "${GLIBC_COMPAT_OBJ}") + +# Bundle all system-compiled objects into a static library +set(AWSLC_SHIM_LIB "${SHIM_OBJ_DIR}/libawslc_shim.a") +add_custom_command( + OUTPUT "${AWSLC_SHIM_LIB}" + COMMAND ${CMAKE_AR} rcs "${AWSLC_SHIM_LIB}" ${AWSLC_SHIM_OBJS} + DEPENDS ${AWSLC_SHIM_OBJS} + COMMENT "Archiving libawslc_shim.a" +) +add_custom_target(awslc_shim_lib DEPENDS "${AWSLC_SHIM_LIB}") + +# The CH-side entry point: only this file uses the ClickHouse toolchain. +# It contains a plain C-linkage function so there is no C++ ABI concern. +set(CLICKHOUSE_SSL_SHIM_SOURCES SslShim.cpp) +# The system-compiled shim objects use libstdc++ (matching the Docker-built +# libssl.a ABI). Link the full static libstdc++ to satisfy those symbols. +# The --allow-multiple-definition flag handles the few symbols that overlap +# between libstdc++ and ClickHouse's libc++/libcxxabi (e.g. std::exception). +find_file(LIBSTDCXX_STATIC libstdc++.a + PATHS /usr/lib/gcc/x86_64-linux-gnu + PATH_SUFFIXES 13 12 11 + NO_DEFAULT_PATH +) +if(NOT LIBSTDCXX_STATIC) + message(FATAL_ERROR "Could not find static libstdc++.a needed for ssl-shim") +endif() + +set(CLICKHOUSE_SSL_SHIM_LINK + PRIVATE + "${AWSLC_SHIM_LIB}" + "${AWSLC_LIBSSL}" + "${AWSLC_LIBCRYPTO}" + "${LIBSTDCXX_STATIC}" +) +set(CLICKHOUSE_SSL_SHIM_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) + +clickhouse_program_add(ssl-shim) +add_dependencies(clickhouse-ssl-shim-lib awslc_shim_lib build-awslc) +target_compile_options(clickhouse-ssl-shim-lib PRIVATE -Wno-everything) diff --git a/programs/ssl-shim/SslShim.cpp b/programs/ssl-shim/SslShim.cpp new file mode 100644 index 000000000000..a96e97c3a8ad --- /dev/null +++ b/programs/ssl-shim/SslShim.cpp @@ -0,0 +1,6 @@ +extern int bssl_shim_main(int argc, char ** argv); + +int mainEntryClickHouseSslShim(int argc, char ** argv) +{ + return bssl_shim_main(argc, argv); +} diff --git a/programs/ssl-shim/glibc_compat.c b/programs/ssl-shim/glibc_compat.c new file mode 100644 index 000000000000..38973e0bcecb --- /dev/null +++ b/programs/ssl-shim/glibc_compat.c @@ -0,0 +1,9 @@ +#include + +unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { + return strtoull(nptr, endptr, base); +} + +long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { + return strtoll(nptr, endptr, base); +} From f4233e26444ec92f3a64b804353e770bd3e4ec1f Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Mon, 9 Mar 2026 22:55:57 -0400 Subject: [PATCH 2/7] Integrated CH SSL tests! Signed-off-by: Julian Huang --- base/glibc-compatibility/musl/posix_spawn.c | 81 ++++++++++++++++++++- programs/ssl-handshaker/glibc_compat.c | 2 + programs/ssl-shim/glibc_compat.c | 2 + 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/base/glibc-compatibility/musl/posix_spawn.c b/base/glibc-compatibility/musl/posix_spawn.c index a66e64e01a20..89fdd32bad19 100644 --- a/base/glibc-compatibility/musl/posix_spawn.c +++ b/base/glibc-compatibility/musl/posix_spawn.c @@ -1,5 +1,5 @@ -/// Very limited implementation. Half of code from Musl was cut. -/// This is Ok, because for now, this function is used only from clang driver. +/// Implementation based on Musl's posix_spawn, with file actions support +/// restored for use by AWS-LC's split-handshake tests (and any other caller). #define _GNU_SOURCE #include @@ -15,6 +15,19 @@ #include #include "syscall.h" +#define FDOP_CLOSE 1 +#define FDOP_DUP2 2 +#define FDOP_OPEN 3 +#define FDOP_CHDIR 4 +#define FDOP_FCHDIR 5 + +struct fdop { + struct fdop *next, *prev; + int cmd, fd, srcfd, oflag; + mode_t mode; + char path[]; +}; + struct args { int p[2]; sigset_t oldmask; @@ -45,11 +58,71 @@ static int child(void *args_vp) pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); + /* Process file actions in POSIX-specified order (oldest-added first). + * The linked list is built by prepending, so we traverse to the tail + * and walk backwards via prev pointers. */ + if (args->fa) { + struct fdop *op = (struct fdop *)args->fa->__actions; + struct fdop *tail; + for (tail = op; tail && tail->next; tail = tail->next); + for (op = tail; op; op = op->prev) { + long r; + switch (op->cmd) { + case FDOP_CLOSE: + __syscall(SYS_close, op->fd); + break; + case FDOP_DUP2: + if (op->srcfd == op->fd) { + r = __syscall(SYS_fcntl, op->fd, F_GETFD); + if (r < 0) { ret = -(int)r; goto fail; } + if (r & FD_CLOEXEC) { + r = __syscall(SYS_fcntl, op->fd, F_SETFD, (int)r & ~FD_CLOEXEC); + if (r < 0) { ret = -(int)r; goto fail; } + } + } else { +#ifdef SYS_dup2 + r = __syscall(SYS_dup2, op->srcfd, op->fd); +#else + r = __syscall(SYS_dup3, op->srcfd, op->fd, 0); +#endif + if (r < 0) { ret = -(int)r; goto fail; } + } + break; + case FDOP_OPEN: { +#ifdef SYS_open + int fd = __syscall(SYS_open, op->path, op->oflag, op->mode); +#else + int fd = __syscall(SYS_openat, AT_FDCWD, op->path, op->oflag, op->mode); +#endif + if (fd < 0) { ret = -fd; goto fail; } + if (fd != op->fd) { +#ifdef SYS_dup2 + r = __syscall(SYS_dup2, fd, op->fd); +#else + r = __syscall(SYS_dup3, fd, op->fd, 0); +#endif + __syscall(SYS_close, fd); + if (r < 0) { ret = -(int)r; goto fail; } + } + break; + } + case FDOP_CHDIR: + r = __syscall(SYS_chdir, op->path); + if (r < 0) { ret = -(int)r; goto fail; } + break; + case FDOP_FCHDIR: + r = __syscall(SYS_fchdir, op->fd); + if (r < 0) { ret = -(int)r; goto fail; } + break; + } + } + } + args->exec(args->path, args->argv, args->envp); - ret = -errno; + ret = errno; +fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ - ret = -ret; if (ret) while (__syscall(SYS_write, p, &ret, sizeof ret) < 0); _exit(127); } diff --git a/programs/ssl-handshaker/glibc_compat.c b/programs/ssl-handshaker/glibc_compat.c index 38973e0bcecb..be0c1683afa2 100644 --- a/programs/ssl-handshaker/glibc_compat.c +++ b/programs/ssl-handshaker/glibc_compat.c @@ -1,9 +1,11 @@ #include +__attribute__((weak)) unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { return strtoull(nptr, endptr, base); } +__attribute__((weak)) long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { return strtoll(nptr, endptr, base); } diff --git a/programs/ssl-shim/glibc_compat.c b/programs/ssl-shim/glibc_compat.c index 38973e0bcecb..be0c1683afa2 100644 --- a/programs/ssl-shim/glibc_compat.c +++ b/programs/ssl-shim/glibc_compat.c @@ -1,9 +1,11 @@ #include +__attribute__((weak)) unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { return strtoull(nptr, endptr, base); } +__attribute__((weak)) long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { return strtoll(nptr, endptr, base); } From 6396426750a8b28f1ebe816643e49881500adeaa Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Tue, 10 Mar 2026 20:16:07 -0400 Subject: [PATCH 3/7] Deduplicate glibc_compat.c and add gtest include path for ssl-shim/handshaker Move the identical glibc_compat.c files from ssl-shim/ and ssl-handshaker/ into a shared programs/ssl-common/ directory. Also add the gtest include path required by test_util.cc to both CMakeLists. Signed-off-by: Julian Huang --- .../{ssl-handshaker => ssl-common}/glibc_compat.c | 0 programs/ssl-handshaker/CMakeLists.txt | 5 +++-- programs/ssl-shim/CMakeLists.txt | 5 +++-- programs/ssl-shim/glibc_compat.c | 11 ----------- 4 files changed, 6 insertions(+), 15 deletions(-) rename programs/{ssl-handshaker => ssl-common}/glibc_compat.c (100%) delete mode 100644 programs/ssl-shim/glibc_compat.c diff --git a/programs/ssl-handshaker/glibc_compat.c b/programs/ssl-common/glibc_compat.c similarity index 100% rename from programs/ssl-handshaker/glibc_compat.c rename to programs/ssl-common/glibc_compat.c diff --git a/programs/ssl-handshaker/CMakeLists.txt b/programs/ssl-handshaker/CMakeLists.txt index cf9f9e775959..800116ac3390 100644 --- a/programs/ssl-handshaker/CMakeLists.txt +++ b/programs/ssl-handshaker/CMakeLists.txt @@ -28,6 +28,7 @@ set(AWSLC_HSHAKER_INCLUDE_FLAGS -I${AWSLC_SRC_DIR}/include -I${AWSLC_SRC_DIR} -I${AWSLC_SSL_TEST_DIR} + -I${AWSLC_SRC_DIR}/third_party/googletest/include ) set(AWSLC_HSHAKER_OBJS) @@ -64,9 +65,9 @@ add_custom_command( OUTPUT "${GLIBC_COMPAT_HS_OBJ}" COMMAND ${CMAKE_COMMAND} -E make_directory "${HSHAKER_OBJ_DIR}" COMMAND /usr/bin/cc -fPIC -O2 -w - -c "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + -c "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" -o "${GLIBC_COMPAT_HS_OBJ}" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" COMMENT "Building glibc C23 compat shim (handshaker)" ) list(APPEND AWSLC_HSHAKER_OBJS "${GLIBC_COMPAT_HS_OBJ}") diff --git a/programs/ssl-shim/CMakeLists.txt b/programs/ssl-shim/CMakeLists.txt index 6cc8798298a4..425fef43c0d5 100644 --- a/programs/ssl-shim/CMakeLists.txt +++ b/programs/ssl-shim/CMakeLists.txt @@ -25,6 +25,7 @@ set(AWSLC_SHIM_INCLUDE_FLAGS -I${AWSLC_SRC_DIR}/include -I${AWSLC_SRC_DIR} -I${AWSLC_SSL_TEST_DIR} + -I${AWSLC_SRC_DIR}/third_party/googletest/include ) # Build each aws-lc shim source file with the system compiler (matching @@ -68,9 +69,9 @@ add_custom_command( OUTPUT "${GLIBC_COMPAT_OBJ}" COMMAND ${CMAKE_COMMAND} -E make_directory "${SHIM_OBJ_DIR}" COMMAND /usr/bin/cc -fPIC -O2 -w - -c "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + -c "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" -o "${GLIBC_COMPAT_OBJ}" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/glibc_compat.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" COMMENT "Building glibc C23 compat shim" ) list(APPEND AWSLC_SHIM_OBJS "${GLIBC_COMPAT_OBJ}") diff --git a/programs/ssl-shim/glibc_compat.c b/programs/ssl-shim/glibc_compat.c deleted file mode 100644 index be0c1683afa2..000000000000 --- a/programs/ssl-shim/glibc_compat.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -__attribute__((weak)) -unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { - return strtoull(nptr, endptr, base); -} - -__attribute__((weak)) -long long __isoc23_strtoll(const char *nptr, char **endptr, int base) { - return strtoll(nptr, endptr, base); -} From 4e4f1cf9a1ed59e3ca56da7a45d2988d881a514f Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Tue, 10 Mar 2026 20:20:09 -0400 Subject: [PATCH 4/7] Remove unnecessary gtest include path from ssl-shim/handshaker The FIPS 2.0.0 shim sources do not include any gtest headers, so this include path is not needed. Signed-off-by: Julian Huang --- programs/ssl-handshaker/CMakeLists.txt | 1 - programs/ssl-shim/CMakeLists.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/programs/ssl-handshaker/CMakeLists.txt b/programs/ssl-handshaker/CMakeLists.txt index 800116ac3390..16997048eac3 100644 --- a/programs/ssl-handshaker/CMakeLists.txt +++ b/programs/ssl-handshaker/CMakeLists.txt @@ -28,7 +28,6 @@ set(AWSLC_HSHAKER_INCLUDE_FLAGS -I${AWSLC_SRC_DIR}/include -I${AWSLC_SRC_DIR} -I${AWSLC_SSL_TEST_DIR} - -I${AWSLC_SRC_DIR}/third_party/googletest/include ) set(AWSLC_HSHAKER_OBJS) diff --git a/programs/ssl-shim/CMakeLists.txt b/programs/ssl-shim/CMakeLists.txt index 425fef43c0d5..cf910ba1313c 100644 --- a/programs/ssl-shim/CMakeLists.txt +++ b/programs/ssl-shim/CMakeLists.txt @@ -25,7 +25,6 @@ set(AWSLC_SHIM_INCLUDE_FLAGS -I${AWSLC_SRC_DIR}/include -I${AWSLC_SRC_DIR} -I${AWSLC_SSL_TEST_DIR} - -I${AWSLC_SRC_DIR}/third_party/googletest/include ) # Build each aws-lc shim source file with the system compiler (matching From 5f2cf938296bdc7aec71dbaec70c2c1c71d2b666 Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Wed, 11 Mar 2026 16:14:40 -0400 Subject: [PATCH 5/7] ACVP compat Signed-off-by: Julian Huang --- programs/CMakeLists.txt | 2 + programs/acvp-server/AcvpServer.cpp | 6 ++ programs/acvp-server/CMakeLists.txt | 95 +++++++++++++++++++++++++++++ programs/main.cpp | 2 + programs/ssl-common/glibc_compat.c | 4 ++ 5 files changed, 109 insertions(+) create mode 100644 programs/acvp-server/AcvpServer.cpp create mode 100644 programs/acvp-server/CMakeLists.txt diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index 8116e063086d..dcba8706fc50 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -130,6 +130,7 @@ endif() if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) add_subdirectory (ssl-shim) add_subdirectory (ssl-handshaker) + add_subdirectory (acvp-server) endif() if (ENABLE_CLICKHOUSE_SELF_EXTRACTING AND NOT ENABLE_DUMMY_LAUNCHERS) @@ -242,6 +243,7 @@ if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) clickhouse_program_install(clickhouse-ssl-shim ssl-shim) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") clickhouse_program_install(clickhouse-ssl-handshaker ssl-handshaker) + clickhouse_program_install(clickhouse-acvp-server acvp-server) endif() endif() diff --git a/programs/acvp-server/AcvpServer.cpp b/programs/acvp-server/AcvpServer.cpp new file mode 100644 index 000000000000..95209e62a0fc --- /dev/null +++ b/programs/acvp-server/AcvpServer.cpp @@ -0,0 +1,6 @@ +extern int acvp_modulewrapper_main(int argc, char ** argv); + +int mainEntryClickHouseAcvpServer(int argc, char ** argv) +{ + return acvp_modulewrapper_main(argc, argv); +} diff --git a/programs/acvp-server/CMakeLists.txt b/programs/acvp-server/CMakeLists.txt new file mode 100644 index 000000000000..b42b92f00440 --- /dev/null +++ b/programs/acvp-server/CMakeLists.txt @@ -0,0 +1,95 @@ +if(NOT FIPS_CLICKHOUSE OR NOT DEFINED AWSLC_SRC_DIR) + return() +endif() + +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + return() +endif() + +set(AWSLC_ACVP_MODULEWRAPPER_DIR "${AWSLC_SRC_DIR}/util/fipstools/acvp/modulewrapper") +set(AWSLC_LIBCRYPTO "${CMAKE_BINARY_DIR}/awslc-build/output/libcrypto.a") + +set(ACVP_OBJ_DIR "${CMAKE_CURRENT_BINARY_DIR}/awslc_acvp_server_objs") + +set(AWSLC_ACVP_SERVER_CXX_SRCS + ${AWSLC_ACVP_MODULEWRAPPER_DIR}/main.cc + ${AWSLC_ACVP_MODULEWRAPPER_DIR}/modulewrapper.cc +) + +set(AWSLC_ACVP_SERVER_INCLUDE_FLAGS + -I${AWSLC_ACVP_MODULEWRAPPER_DIR} + -I${AWSLC_SRC_DIR}/include + -I${AWSLC_SRC_DIR} +) + +set(AWSLC_ACVP_SERVER_OBJS) +foreach(src ${AWSLC_ACVP_SERVER_CXX_SRCS}) + get_filename_component(base "${src}" NAME_WE) + get_filename_component(dir "${src}" DIRECTORY) + string(MD5 dir_hash "${dir}") + string(SUBSTRING "${dir_hash}" 0 8 dir_tag) + set(obj "${ACVP_OBJ_DIR}/${base}_${dir_tag}.o") + + set(extra_defs "") + if("${base}" STREQUAL "main") + set(extra_defs -Dmain=acvp_modulewrapper_main) + endif() + + add_custom_command( + OUTPUT "${obj}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${ACVP_OBJ_DIR}" + COMMAND /usr/bin/c++ + -std=c++17 -fPIC -g -O2 + ${AWSLC_ACVP_SERVER_INCLUDE_FLAGS} + ${extra_defs} + -w + -c "${src}" + -o "${obj}" + DEPENDS "${src}" build-awslc + COMMENT "Building aws-lc acvp-server object ${base}.cc (system C++ ABI)" + ) + list(APPEND AWSLC_ACVP_SERVER_OBJS "${obj}") +endforeach() + +set(GLIBC_COMPAT_ACVP_OBJ "${ACVP_OBJ_DIR}/glibc_compat.o") +add_custom_command( + OUTPUT "${GLIBC_COMPAT_ACVP_OBJ}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${ACVP_OBJ_DIR}" + COMMAND /usr/bin/cc -fPIC -O2 -w + -c "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" + -o "${GLIBC_COMPAT_ACVP_OBJ}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../ssl-common/glibc_compat.c" + COMMENT "Building glibc C23 compat shim (acvp-server)" +) +list(APPEND AWSLC_ACVP_SERVER_OBJS "${GLIBC_COMPAT_ACVP_OBJ}") + +set(AWSLC_ACVP_SERVER_LIB "${ACVP_OBJ_DIR}/libawslc_acvp_server.a") +add_custom_command( + OUTPUT "${AWSLC_ACVP_SERVER_LIB}" + COMMAND ${CMAKE_AR} rcs "${AWSLC_ACVP_SERVER_LIB}" ${AWSLC_ACVP_SERVER_OBJS} + DEPENDS ${AWSLC_ACVP_SERVER_OBJS} + COMMENT "Archiving libawslc_acvp_server.a" +) +add_custom_target(awslc_acvp_server_lib DEPENDS "${AWSLC_ACVP_SERVER_LIB}") + +set(CLICKHOUSE_ACVP_SERVER_SOURCES AcvpServer.cpp) +find_file(LIBSTDCXX_STATIC_ACVP libstdc++.a + PATHS /usr/lib/gcc/x86_64-linux-gnu + PATH_SUFFIXES 13 12 11 + NO_DEFAULT_PATH +) +if(NOT LIBSTDCXX_STATIC_ACVP) + message(FATAL_ERROR "Could not find static libstdc++.a needed for acvp-server") +endif() + +set(CLICKHOUSE_ACVP_SERVER_LINK + PRIVATE + "${AWSLC_ACVP_SERVER_LIB}" + "${AWSLC_LIBCRYPTO}" + "${LIBSTDCXX_STATIC_ACVP}" +) +set(CLICKHOUSE_ACVP_SERVER_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) + +clickhouse_program_add(acvp-server) +add_dependencies(clickhouse-acvp-server-lib awslc_acvp_server_lib build-awslc) +target_compile_options(clickhouse-acvp-server-lib PRIVATE -Wno-everything) diff --git a/programs/main.cpp b/programs/main.cpp index b91f9291a6e2..ba2f51efae18 100644 --- a/programs/main.cpp +++ b/programs/main.cpp @@ -67,6 +67,7 @@ int mainEntryClickHouseKeeperDataDumper(int argc, char ** argv); #if defined(FIPS_CLICKHOUSE) int mainEntryClickHouseSslShim(int argc, char ** argv); int mainEntryClickHouseSslHandshaker(int argc, char ** argv); +int mainEntryClickHouseAcvpServer(int argc, char ** argv); #endif // install @@ -124,6 +125,7 @@ std::pair clickhouse_applications[] = #if defined(FIPS_CLICKHOUSE) {"ssl-shim", mainEntryClickHouseSslShim}, {"ssl-handshaker", mainEntryClickHouseSslHandshaker}, + {"acvp-server", mainEntryClickHouseAcvpServer}, #endif // install {"install", mainEntryClickHouseInstall}, diff --git a/programs/ssl-common/glibc_compat.c b/programs/ssl-common/glibc_compat.c index be0c1683afa2..25348621262f 100644 --- a/programs/ssl-common/glibc_compat.c +++ b/programs/ssl-common/glibc_compat.c @@ -1,5 +1,9 @@ #include +/* glibc 2.32+ symbol used by libstdc++; sysroot may have older libc. */ +__attribute__((weak)) +char __libc_single_threaded = 0; + __attribute__((weak)) unsigned long long __isoc23_strtoull(const char *nptr, char **endptr, int base) { return strtoull(nptr, endptr, base); From 2a24ee7fd77f661d259537f4d830e977167045ff Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Thu, 12 Mar 2026 18:13:16 -0400 Subject: [PATCH 6/7] Fix build-guard mismatch and scope --allow-multiple-definition Gate ssl-shim/ssl-handshaker/acvp-server declarations in main.cpp with per-target ENABLE_CLICKHOUSE_* defines (via config_tools.h) that match the exact CMake conditions under which targets are created, preventing unresolved symbols when FIPS_CLICKHOUSE is set without AWSLC_SRC_DIR or on non-Linux platforms. Move the --allow-multiple-definition linker flag from the global clickhouse target into each of the three library targets as an INTERFACE property, so the flag only enters the link when those specific libraries are actually consumed. Signed-off-by: Julian Huang Made-with: Cursor --- programs/CMakeLists.txt | 34 +++++++++++++++----------- programs/acvp-server/CMakeLists.txt | 1 + programs/config_tools.h.in | 3 +++ programs/main.cpp | 12 +++++++-- programs/ssl-handshaker/CMakeLists.txt | 1 + programs/ssl-shim/CMakeLists.txt | 5 ++++ 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index dcba8706fc50..aeb8cdd21631 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -62,6 +62,14 @@ else() message(STATUS "ClickHouse keeper-client mode: OFF") endif() +if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) + set(ENABLE_CLICKHOUSE_SSL_SHIM 1) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(ENABLE_CLICKHOUSE_SSL_HANDSHAKER 1) + set(ENABLE_CLICKHOUSE_ACVP_SERVER 1) + endif() +endif() + configure_file (config_tools.h.in ${CONFIG_INCLUDE_PATH}/config_tools.h) macro(clickhouse_target_link_split_lib target name) @@ -127,9 +135,13 @@ if (ENABLE_CLICKHOUSE_KEEPER) add_subdirectory (keeper) endif() -if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) +if (ENABLE_CLICKHOUSE_SSL_SHIM) add_subdirectory (ssl-shim) +endif() +if (ENABLE_CLICKHOUSE_SSL_HANDSHAKER) add_subdirectory (ssl-handshaker) +endif() +if (ENABLE_CLICKHOUSE_ACVP_SERVER) add_subdirectory (acvp-server) endif() @@ -147,14 +159,6 @@ endif () target_link_libraries (clickhouse PRIVATE clickhouse_common_io ${HARMFUL_LIB}) target_include_directories (clickhouse PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -# The ssl-shim/ssl-handshaker objects are compiled with the system compiler -# (libstdc++ ABI) to match the Docker-built aws-lc libraries. Linking -# libstdc++.a alongside ClickHouse's libc++/libcxxabi creates a few -# duplicate symbols (std::exception, etc.) that are safe to coalesce. -if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) - target_link_options(clickhouse PRIVATE "LINKER:--allow-multiple-definition") -endif() - if (ENABLE_CLICKHOUSE_KEEPER) clickhouse_target_link_split_lib(clickhouse keeper) endif() @@ -239,12 +243,14 @@ if (ENABLE_CLICKHOUSE_KEEPER_CLIENT) list(APPEND CLICKHOUSE_BUNDLE clickhouse-keeper-client) endif () -if (FIPS_CLICKHOUSE AND DEFINED AWSLC_SRC_DIR) +if (ENABLE_CLICKHOUSE_SSL_SHIM) clickhouse_program_install(clickhouse-ssl-shim ssl-shim) - if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - clickhouse_program_install(clickhouse-ssl-handshaker ssl-handshaker) - clickhouse_program_install(clickhouse-acvp-server acvp-server) - endif() +endif() +if (ENABLE_CLICKHOUSE_SSL_HANDSHAKER) + clickhouse_program_install(clickhouse-ssl-handshaker ssl-handshaker) +endif() +if (ENABLE_CLICKHOUSE_ACVP_SERVER) + clickhouse_program_install(clickhouse-acvp-server acvp-server) endif() add_custom_target (clickhouse-bundle ALL DEPENDS ${CLICKHOUSE_BUNDLE}) diff --git a/programs/acvp-server/CMakeLists.txt b/programs/acvp-server/CMakeLists.txt index b42b92f00440..54d53c5af306 100644 --- a/programs/acvp-server/CMakeLists.txt +++ b/programs/acvp-server/CMakeLists.txt @@ -93,3 +93,4 @@ set(CLICKHOUSE_ACVP_SERVER_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) clickhouse_program_add(acvp-server) add_dependencies(clickhouse-acvp-server-lib awslc_acvp_server_lib build-awslc) target_compile_options(clickhouse-acvp-server-lib PRIVATE -Wno-everything) +target_link_options(clickhouse-acvp-server-lib INTERFACE "LINKER:--allow-multiple-definition") diff --git a/programs/config_tools.h.in b/programs/config_tools.h.in index 11689cf346f9..c9e87a938f3c 100644 --- a/programs/config_tools.h.in +++ b/programs/config_tools.h.in @@ -5,3 +5,6 @@ #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER_CLIENT #cmakedefine01 ENABLE_CLICKHOUSE_KEEPER_CONVERTER +#cmakedefine01 ENABLE_CLICKHOUSE_SSL_SHIM +#cmakedefine01 ENABLE_CLICKHOUSE_SSL_HANDSHAKER +#cmakedefine01 ENABLE_CLICKHOUSE_ACVP_SERVER diff --git a/programs/main.cpp b/programs/main.cpp index ba2f51efae18..8db9ca3c50cf 100644 --- a/programs/main.cpp +++ b/programs/main.cpp @@ -64,9 +64,13 @@ int mainEntryClickHouseKeeperBench(int argc, char ** argv); int mainEntryClickHouseKeeperDataDumper(int argc, char ** argv); #endif -#if defined(FIPS_CLICKHOUSE) +#if ENABLE_CLICKHOUSE_SSL_SHIM int mainEntryClickHouseSslShim(int argc, char ** argv); +#endif +#if ENABLE_CLICKHOUSE_SSL_HANDSHAKER int mainEntryClickHouseSslHandshaker(int argc, char ** argv); +#endif +#if ENABLE_CLICKHOUSE_ACVP_SERVER int mainEntryClickHouseAcvpServer(int argc, char ** argv); #endif @@ -122,9 +126,13 @@ std::pair clickhouse_applications[] = #if USE_NURAFT {"keeper-data-dumper", mainEntryClickHouseKeeperDataDumper}, #endif -#if defined(FIPS_CLICKHOUSE) +#if ENABLE_CLICKHOUSE_SSL_SHIM {"ssl-shim", mainEntryClickHouseSslShim}, +#endif +#if ENABLE_CLICKHOUSE_SSL_HANDSHAKER {"ssl-handshaker", mainEntryClickHouseSslHandshaker}, +#endif +#if ENABLE_CLICKHOUSE_ACVP_SERVER {"acvp-server", mainEntryClickHouseAcvpServer}, #endif // install diff --git a/programs/ssl-handshaker/CMakeLists.txt b/programs/ssl-handshaker/CMakeLists.txt index 16997048eac3..8a969bc55f7a 100644 --- a/programs/ssl-handshaker/CMakeLists.txt +++ b/programs/ssl-handshaker/CMakeLists.txt @@ -102,3 +102,4 @@ set(CLICKHOUSE_SSL_HANDSHAKER_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) clickhouse_program_add(ssl-handshaker) add_dependencies(clickhouse-ssl-handshaker-lib awslc_handshaker_lib build-awslc) target_compile_options(clickhouse-ssl-handshaker-lib PRIVATE -Wno-everything) +target_link_options(clickhouse-ssl-handshaker-lib INTERFACE "LINKER:--allow-multiple-definition") diff --git a/programs/ssl-shim/CMakeLists.txt b/programs/ssl-shim/CMakeLists.txt index cf910ba1313c..57d1cec51934 100644 --- a/programs/ssl-shim/CMakeLists.txt +++ b/programs/ssl-shim/CMakeLists.txt @@ -113,3 +113,8 @@ set(CLICKHOUSE_SSL_SHIM_INCLUDE PRIVATE ${AWSLC_SRC_DIR}/include) clickhouse_program_add(ssl-shim) add_dependencies(clickhouse-ssl-shim-lib awslc_shim_lib build-awslc) target_compile_options(clickhouse-ssl-shim-lib PRIVATE -Wno-everything) +# The system-compiled shim objects use libstdc++ (matching the Docker-built +# libssl.a ABI), which creates a few duplicate symbols with ClickHouse's +# libc++/libcxxabi (e.g. std::exception). Propagate via INTERFACE so the +# flag only enters the link when this target is actually consumed. +target_link_options(clickhouse-ssl-shim-lib INTERFACE "LINKER:--allow-multiple-definition") From 0a7b7573a26e8e938d35a2b48973f92247d2fdf8 Mon Sep 17 00:00:00 2001 From: Julian Huang Date: Thu, 12 Mar 2026 18:27:24 -0400 Subject: [PATCH 7/7] Adopt full upstream musl posix_spawn implementation Replace the partial posix_spawn with the complete upstream musl implementation (https://git.musl-libc.org/cgit/musl/tree/src/process/posix_spawn.c), adapted for the glibc sysroot headers used by ClickHouse. Key safety improvements from upstream: - Pipe fd clobbering protection: if a file action targets the error-reporting pipe fd, dup it to an unoccupied fd first - Close-on-exec set after file actions (pipe may have been moved) - Block all signals before pipe2/clone; unblock after exec - EPIPE-aware error reporting back to parent - Support for POSIX_SPAWN_SETSID, SETPGROUP, RESETIDS, SETSIGDEF - Larger stack (1024 + PATH_MAX) Adaptations from upstream musl: - Uses glibc sysroot field names (__ss/__sd vs __mask/__def) - Keeps __posix_spawnx exec-function parameter (glibc attr has no __fn) - Omits LOCK(__abort_lock) (musl-internal, not available) - Omits __get_handler_set (musl-internal; signals are blocked for the child's brief pre-exec window so parent handlers cannot fire) - Uses clone() instead of musl-internal __clone() Signed-off-by: Julian Huang Made-with: Cursor --- base/glibc-compatibility/musl/posix_spawn.c | 172 +++++++++++++------- 1 file changed, 114 insertions(+), 58 deletions(-) diff --git a/base/glibc-compatibility/musl/posix_spawn.c b/base/glibc-compatibility/musl/posix_spawn.c index 89fdd32bad19..fa0309eeff88 100644 --- a/base/glibc-compatibility/musl/posix_spawn.c +++ b/base/glibc-compatibility/musl/posix_spawn.c @@ -1,5 +1,17 @@ -/// Implementation based on Musl's posix_spawn, with file actions support -/// restored for use by AWS-LC's split-handshake tests (and any other caller). +/// Full posix_spawn implementation based on upstream musl. +/// https://git.musl-libc.org/cgit/musl/tree/src/process/posix_spawn.c +/// +/// Adaptations from upstream: +/// - Uses glibc sysroot field names (__ss/__sd instead of __mask/__def) +/// since resolves to the glibc sysroot header in this build. +/// - Takes exec function as a parameter (__posix_spawnx) because glibc's +/// posix_spawnattr_t has no __fn field for posix_spawnp dispatch. +/// - Omits LOCK(__abort_lock) / UNLOCK(__abort_lock) (musl-internal lock +/// guarding against abort() races; not available outside musl). +/// - Omits __get_handler_set / __libc_sigaction signal disposition reset +/// (musl internals not available; signals are blocked for the child's +/// brief pre-exec window, so parent handlers cannot fire). +/// - Uses clone() instead of musl-internal __clone(). #define _GNU_SOURCE #include @@ -9,10 +21,9 @@ #include #include #include -#include #include -#include #include +#include #include "syscall.h" #define FDOP_CLOSE 1 @@ -38,92 +49,129 @@ struct args { char *const *argv, *const *envp; }; -void __get_handler_set(sigset_t *); +static int __sys_dup2(int old, int new) +{ +#ifdef SYS_dup2 + return __syscall(SYS_dup2, old, new); +#else + return __syscall(SYS_dup3, old, new, 0); +#endif +} static int child(void *args_vp) { - int ret; + int i, ret; + struct sigaction sa = {0}; struct args *args = args_vp; int p = args->p[1]; + const posix_spawn_file_actions_t *fa = args->fa; const posix_spawnattr_t *restrict attr = args->attr; close(args->p[0]); - /* Close-on-exec flag may have been lost if we moved the pipe - * to a different fd. We don't use F_DUPFD_CLOEXEC above because - * it would fail on older kernels and atomicity is not needed -- - * in this process there are no threads or signal handlers. */ - __syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC); - - pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) - ? &attr->__ss : &args->oldmask, 0); + if (attr->__flags & POSIX_SPAWN_SETSIGDEF) { + for (i=1; i<_NSIG; i++) { + if (sigismember(&attr->__sd, i)) { + sa.sa_handler = SIG_DFL; + __syscall(SYS_rt_sigaction, i, &sa, 0, _NSIG/8); + } + } + } - /* Process file actions in POSIX-specified order (oldest-added first). - * The linked list is built by prepending, so we traverse to the tail - * and walk backwards via prev pointers. */ - if (args->fa) { - struct fdop *op = (struct fdop *)args->fa->__actions; - struct fdop *tail; - for (tail = op; tail && tail->next; tail = tail->next); - for (op = tail; op; op = op->prev) { - long r; - switch (op->cmd) { + if (attr->__flags & POSIX_SPAWN_SETSID) + if ((ret=__syscall(SYS_setsid)) < 0) + goto fail; + + if (attr->__flags & POSIX_SPAWN_SETPGROUP) + if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp))) + goto fail; + + if (attr->__flags & POSIX_SPAWN_RESETIDS) + if ((ret=__syscall(SYS_setgid, __syscall(SYS_getgid))) || + (ret=__syscall(SYS_setuid, __syscall(SYS_getuid))) ) + goto fail; + + if (fa && fa->__actions) { + struct fdop *op; + int fd; + for (op = fa->__actions; op->next; op = op->next); + for (; op; op = op->prev) { + /* It's possible that a file operation would clobber + * the pipe fd used for synchronizing with the + * parent. To avoid that, we dup the pipe onto + * an unoccupied fd. */ + if (op->fd == p) { + ret = __syscall(SYS_dup, p); + if (ret < 0) goto fail; + __syscall(SYS_close, p); + p = ret; + } + switch(op->cmd) { case FDOP_CLOSE: __syscall(SYS_close, op->fd); break; case FDOP_DUP2: - if (op->srcfd == op->fd) { - r = __syscall(SYS_fcntl, op->fd, F_GETFD); - if (r < 0) { ret = -(int)r; goto fail; } - if (r & FD_CLOEXEC) { - r = __syscall(SYS_fcntl, op->fd, F_SETFD, (int)r & ~FD_CLOEXEC); - if (r < 0) { ret = -(int)r; goto fail; } - } + fd = op->srcfd; + if (fd == p) { + ret = -EBADF; + goto fail; + } + if (fd != op->fd) { + if ((ret=__sys_dup2(fd, op->fd))<0) + goto fail; } else { -#ifdef SYS_dup2 - r = __syscall(SYS_dup2, op->srcfd, op->fd); -#else - r = __syscall(SYS_dup3, op->srcfd, op->fd, 0); -#endif - if (r < 0) { ret = -(int)r; goto fail; } + ret = __syscall(SYS_fcntl, fd, F_GETFD); + ret = __syscall(SYS_fcntl, fd, F_SETFD, + ret & ~FD_CLOEXEC); + if (ret<0) + goto fail; } break; - case FDOP_OPEN: { + case FDOP_OPEN: #ifdef SYS_open - int fd = __syscall(SYS_open, op->path, op->oflag, op->mode); + fd = __syscall(SYS_open, op->path, op->oflag, op->mode); #else - int fd = __syscall(SYS_openat, AT_FDCWD, op->path, op->oflag, op->mode); + fd = __syscall(SYS_openat, AT_FDCWD, op->path, op->oflag, op->mode); #endif - if (fd < 0) { ret = -fd; goto fail; } + if ((ret=fd) < 0) goto fail; if (fd != op->fd) { -#ifdef SYS_dup2 - r = __syscall(SYS_dup2, fd, op->fd); -#else - r = __syscall(SYS_dup3, fd, op->fd, 0); -#endif + if ((ret=__sys_dup2(fd, op->fd))<0) + goto fail; __syscall(SYS_close, fd); - if (r < 0) { ret = -(int)r; goto fail; } } break; - } case FDOP_CHDIR: - r = __syscall(SYS_chdir, op->path); - if (r < 0) { ret = -(int)r; goto fail; } + ret = __syscall(SYS_chdir, op->path); + if (ret<0) goto fail; break; case FDOP_FCHDIR: - r = __syscall(SYS_fchdir, op->fd); - if (r < 0) { ret = -(int)r; goto fail; } + ret = __syscall(SYS_fchdir, op->fd); + if (ret<0) goto fail; break; } } } + /* Close-on-exec flag may have been lost if we moved the pipe + * to a different fd. We don't use F_DUPFD_CLOEXEC above because + * it would fail on older kernels and atomicity is not needed -- + * in this process there are no threads or signal handlers. */ + __syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC); + + pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) + ? &attr->__ss : &args->oldmask, 0); + args->exec(args->path, args->argv, args->envp); - ret = errno; + ret = -errno; fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ - if (ret) while (__syscall(SYS_write, p, &ret, sizeof ret) < 0); + ret = -ret; + if (ret) { + int r; + do r = __syscall(SYS_write, p, &ret, sizeof ret); + while (r<0 && r!=-EPIPE); + } _exit(127); } @@ -135,13 +183,10 @@ int __posix_spawnx(pid_t *restrict res, const char *restrict path, char *const argv[restrict], char *const envp[restrict]) { pid_t pid; - char stack[1024]; + char stack[1024+PATH_MAX]; int ec=0, cs; struct args args; - if (pipe2(args.p, O_CLOEXEC)) - return errno; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); args.path = path; @@ -151,6 +196,15 @@ int __posix_spawnx(pid_t *restrict res, const char *restrict path, args.argv = argv; args.envp = envp; + sigset_t allsigs; + sigfillset(&allsigs); + pthread_sigmask(SIG_BLOCK, &allsigs, &args.oldmask); + + if (pipe2(args.p, O_CLOEXEC)) { + ec = errno; + goto fail; + } + pid = clone(child, stack+sizeof stack, CLONE_VM|CLONE_VFORK|SIGCHLD, &args); close(args.p[1]); @@ -166,6 +220,8 @@ int __posix_spawnx(pid_t *restrict res, const char *restrict path, if (!ec && res) *res = pid; +fail: + pthread_sigmask(SIG_SETMASK, &args.oldmask, 0); pthread_setcancelstate(cs, 0); return ec;