This is an automated email from the ASF dual-hosted git repository.
szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
The following commit(s) were added to refs/heads/main by this push:
new 58838fd7b MINIFICPP-2877 Fix ASan and LSan warnings (#2254)
58838fd7b is described below
commit 58838fd7b6179ea9007141fef0b2b5bd51533573
Author: Ferenc Gerlits <[email protected]>
AuthorDate: Thu Sep 17 19:11:47 2026 +0200
MINIFICPP-2877 Fix ASan and LSan warnings (#2254)
ASan/LSan fixes:
* fix a SEGV-at-shutdown error in Tcp/UdpServer
* fix a return-string_view-to-temporary-string bug in TestUtils
* fix a leak in the libssh2 library
* free the shared_ptr result of async_resolve when it is no longer needed
* fix a leak in HTTPSiteToSiteTest
* fix a leak by restoring the old controller service provider if an update
fails
* fix a leak in the Python bindings
* fix an alloc-dealloc-mismatch bug: new + free (test code only)
ODR violation warnings are suppressed, because the way we link and dlopen
libraries causes many false positives. If two symbols are defined with the
same name but different sizes, they will still be flagged.
Upgrade Abseil to the latest release to fix a leak, and upgrade Grpc (which
uses
Abseil) to the latest release. These required some new valgrind suppressions
(generated by Claude Code, like the earlier suppressions).
Add a new ASan/LSan job to the weekly memcheck CI run.
* skip memory usage test in ASan builds
* use CMake 3.x when building Conan packages
* suppress <ciso646> header warnings in Clang, too
in Debug mode, with fail-on-warnings on, Clang-20 cannot build
minifi-couchbase
without this suppression
* use std::span as the argument type instead of std::vector
---
.github/workflows/memcheck_ci.yml | 61 +++++++++++++++--
CMakeLists.txt | 10 +++
cmake/Abseil.cmake | 4 +-
cmake/BuildTests.cmake | 16 +++++
cmake/BundledIodbc.cmake | 2 +-
cmake/CivetWeb.cmake | 5 ++
cmake/FetchLibSSH2.cmake | 12 ++--
cmake/Grpc.cmake | 2 +-
cmake/valgrind.supp | 76 ++++++++++++++++++++--
conanfile.py | 2 +-
.../include/utils/net/ConnectionHandler.h | 30 ++++++---
extension-framework/include/utils/net/Server.h | 25 ++++++-
extension-framework/src/utils/net/TcpServer.cpp | 9 ++-
extension-framework/src/utils/net/UdpServer.cpp | 3 +
extensions/aws/tests/MockS3RequestSender.h | 3 +-
extensions/couchbase/CMakeLists.txt | 2 +
extensions/couchbase/tests/CMakeLists.txt | 2 +
extensions/python/types/Types.h | 6 +-
.../standard-processors/processors/GetTCP.cpp | 22 ++++---
.../standard-processors/tests/unit/PutTCPTests.cpp | 4 +-
libminifi/src/core/FlowConfiguration.cpp | 16 ++++-
libminifi/test/integration/HTTPSiteToSiteTests.cpp | 24 +++----
.../test/libtest/integration/HTTPHandlers.cpp | 5 ++
libminifi/test/libtest/integration/HTTPHandlers.h | 3 -
libminifi/test/libtest/unit/TestUtils.cpp | 2 +-
libminifi/test/unit/FileStreamTests.cpp | 30 ---------
libminifi/test/unit/FileSystemRepositoryTests.cpp | 11 ++++
thirdparty/abseil/rename-crc32.patch | 44 ++++++-------
thirdparty/couchbase/all/conanfile.py | 2 +-
thirdparty/grpc/all/conandata.yml | 8 +--
thirdparty/grpc/all/conanfile.py | 2 +-
.../fix-msvc-auto-return-type-template-arg.patch | 16 ++---
.../{grpc_1.82.0.yml => grpc_1.83.1.yml} | 2 +-
thirdparty/grpc/config.yml | 2 +-
thirdparty/libssh2/fix-ecdh-leak.patch | 18 +++++
35 files changed, 342 insertions(+), 139 deletions(-)
diff --git a/.github/workflows/memcheck_ci.yml
b/.github/workflows/memcheck_ci.yml
index 62192071f..6fc127a85 100644
--- a/.github/workflows/memcheck_ci.yml
+++ b/.github/workflows/memcheck_ci.yml
@@ -16,10 +16,10 @@ env:
SCCACHE_GHA_ENABLE: true
CCACHE_DIR: ${{ GITHUB.WORKSPACE }}/.ccache
jobs:
- ubuntu_24_04:
- name: "valgrind on ubuntu-24.04"
+ valgrind:
+ name: "Valgrind on ubuntu-24.04"
runs-on: ubuntu-24.04
- timeout-minutes: 120
+ timeout-minutes: 300
steps:
- id: checkout
uses: actions/checkout@v6
@@ -27,10 +27,10 @@ jobs:
uses: actions/cache/restore@v5
with:
path: ${{ env.CCACHE_DIR }}
- key: memcheck-ccache-${{github.ref}}-${{github.sha}}
+ key: ubuntu-24.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
restore-keys: |
- memcheck-ccache-${{github.ref}}-
- memcheck-ccache-refs/heads/main-
+ ubuntu-24.04-valgrind-ccache-${{github.ref}}-
+ ubuntu-24.04-valgrind-ccache-refs/heads/main-
- id: install_deps
run: |
sudo apt update
@@ -48,7 +48,7 @@ jobs:
if: always()
with:
path: ${{ env.CCACHE_DIR }}
- key: memcheck-ccache-${{github.ref}}-${{github.sha}}
+ key: ubuntu-24.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
- name: test
id: test
run: |
@@ -75,3 +75,50 @@ jobs:
build/Testing/Temporary/MemoryChecker.*.log
build/Testing/Temporary/LastDynamicAnalysis_*.log
if-no-files-found: ignore
+ address-sanitizer:
+ name: "AddressSanitizer+LeakSanitizer on ubuntu 26.04"
+ runs-on: ubuntu-26.04
+ timeout-minutes: 300
+ steps:
+ - id: checkout
+ uses: actions/checkout@v6
+ - name: cache restore
+ uses: actions/cache/restore@v5
+ with:
+ path: ${{ env.CCACHE_DIR }}
+ key: ubuntu-26.04-asan-ccache-${{github.ref}}-${{github.sha}}
+ restore-keys: |
+ ubuntu-26.04-asan-ccache-${{github.ref}}-
+ ubuntu-26.04-asan-ccache-refs/heads/main-
+ - id: install_deps
+ run: |
+ sudo apt update
+ sudo apt install -y ccache libfl-dev python3 python3-venv
+ echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV
+ echo -e "127.0.0.1\t$HOSTNAME" | sudo tee -a /etc/hosts > /dev/null
+ - name: build
+ run: |
+ python3 -m venv venv && source venv/bin/activate \
+ && pip install -r requirements.txt \
+ && python main.py --noninteractive
--minifi-options="${CMAKE_FLAGS} -DMINIFI_ADVANCED_ASAN_BUILD=ON"
--cmake-options="-DSTRICT_GSL_CHECKS=AUDIT"
+ working-directory: bootstrap
+ - name: cache save
+ uses: actions/cache/save@v5
+ if: always()
+ with:
+ path: ${{ env.CCACHE_DIR }}
+ key: ubuntu-26.04-asan-ccache-${{github.ref}}-${{github.sha}}
+ - name: test
+ id: test
+ run: |
+ # Set core file size limit to unlimited
+ ulimit -c unlimited
+ ctest -j$(nproc) --output-on-failure
+ working-directory: build
+ - name: upload ASan/LSan output
+ if: ${{ failure() && steps.test.conclusion == 'failure' }}
+ uses: actions/upload-artifact@v7
+ with:
+ name: asan-logs
+ path: build/asan_logs/*
+ if-no-files-found: ignore
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d860d38b..bc2a74cf4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -142,6 +142,16 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Use ASAN if instructed
if (MINIFI_ADVANCED_ASAN_BUILD)
+ if (CUSTOM_MALLOC)
+ message(FATAL_ERROR "MINIFI_ADVANCED_ASAN_BUILD is incompatible with
CUSTOM_MALLOC "
+ "(currently '${CUSTOM_MALLOC}'): AddressSanitizer supplies its own
allocator. "
+ "Disable one of them.")
+ endif()
+ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(WARNING "MINIFI_ADVANCED_ASAN_BUILD is enabled but
CMAKE_BUILD_TYPE is "
+ "'${CMAKE_BUILD_TYPE}', not Debug; a Debug build is recommended
for the most "
+ "accurate AddressSanitizer diagnostics.")
+ endif()
set(ASAN_FLAGS "-g -fsanitize=address -fsanitize-address-use-after-scope
-fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ASAN_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ASAN_FLAGS}")
diff --git a/cmake/Abseil.cmake b/cmake/Abseil.cmake
index de94599e4..27ae11453 100644
--- a/cmake/Abseil.cmake
+++ b/cmake/Abseil.cmake
@@ -28,8 +28,8 @@ set(PC ${Bash_EXECUTABLE} -c "set -x &&\
FetchContent_Declare(
absl
- URL
https://github.com/abseil/abseil-cpp/archive/refs/tags/20260526.0.tar.gz
- URL_HASH
SHA256=6e1aee535473414164bf83e4ebc40240dec71a4701f8a642d906e95bea1aea0c
+ URL
https://github.com/abseil/abseil-cpp/archive/refs/tags/20260817.0.tar.gz
+ URL_HASH
SHA256=f7e05179df39c45434cad433f5783840bb3788ef322976f9138bc6b72b3a107d
PATCH_COMMAND "${PC}"
OVERRIDE_FIND_PACKAGE
SYSTEM
diff --git a/cmake/BuildTests.cmake b/cmake/BuildTests.cmake
index b602e7ec1..9a64fba30 100644
--- a/cmake/BuildTests.cmake
+++ b/cmake/BuildTests.cmake
@@ -17,6 +17,22 @@
include(GetCatch2)
+if (MINIFI_ADVANCED_ASAN_BUILD)
+ file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/asan_logs")
+
+ # Route each test's AddressSanitizer/LeakSanitizer output to
<build>/asan_logs/<test-name>.<pid>
+ # and suppress odr-violation warnings (only if the two symbols have the
same size). Hundreds of global
+ # symbols are present in two or more .so's, so we don't want to suppress
each individually.
+ function(add_test)
+ _add_test(${ARGV})
+ cmake_parse_arguments(MINIFI_TEST "" "NAME" "COMMAND" ${ARGV})
+ if (MINIFI_TEST_NAME)
+ set_property(TEST "${MINIFI_TEST_NAME}" APPEND PROPERTY
+ ENVIRONMENT
"ASAN_OPTIONS=detect_odr_violation=1:log_path=${CMAKE_BINARY_DIR}/asan_logs/${MINIFI_TEST_NAME}")
+ endif()
+ endfunction()
+endif()
+
### test functions
MACRO(GETSOURCEFILES result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
diff --git a/cmake/BundledIodbc.cmake b/cmake/BundledIodbc.cmake
index f90bab01b..267b535b9 100644
--- a/cmake/BundledIodbc.cmake
+++ b/cmake/BundledIodbc.cmake
@@ -33,7 +33,7 @@ ExternalProject_Add(
CMAKE_COMMAND ""
UPDATE_COMMAND ""
INSTALL_COMMAND make install
- CONFIGURE_COMMAND bash "-c" "./autogen.sh && ./configure
--prefix=${IODBC_BYPRODUCT_DIR} --with-pic CFLAGS='${CMAKE_C_FLAGS} -std=gnu17'"
+ CONFIGURE_COMMAND bash "-c" "./autogen.sh && ./configure
--prefix=${IODBC_BYPRODUCT_DIR} --with-pic CFLAGS='${PASSTHROUGH_CMAKE_C_FLAGS}
-std=gnu17'"
STEP_TARGETS build
BUILD_BYPRODUCTS "${IODBC_BYPRODUCT_DIR}/${IODBC_BYPRODUCT}"
EXCLUDE_FROM_ALL TRUE
diff --git a/cmake/CivetWeb.cmake b/cmake/CivetWeb.cmake
index 81089b8bc..dadc3f553 100644
--- a/cmake/CivetWeb.cmake
+++ b/cmake/CivetWeb.cmake
@@ -29,6 +29,11 @@ set(CIVETWEB_ENABLE_LUA "OFF" CACHE STRING "" FORCE)
set(CIVETWEB_ENABLE_CXX "ON" CACHE STRING "" FORCE)
set(CIVETWEB_ALLOW_WARNINGS "ON" CACHE STRING "" FORCE)
set(CIVETWEB_ENABLE_ASAN "OFF" CACHE STRING "" FORCE)
+if(MINIFI_ADVANCED_ASAN_BUILD)
+ # this defaults to 100kB, which is too little for ASan; change it to 1MB
+ set(CIVETWEB_THREAD_STACK_SIZE 1048576 CACHE STRING "" FORCE)
+endif()
+
set(PATCH_FILE "${CMAKE_SOURCE_DIR}/thirdparty/civetweb/openssl3.patch")
set(PC ${Bash_EXECUTABLE} -c "set -x &&\
(\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE}\\\")")
diff --git a/cmake/FetchLibSSH2.cmake b/cmake/FetchLibSSH2.cmake
index 7b589cb59..b264906f0 100644
--- a/cmake/FetchLibSSH2.cmake
+++ b/cmake/FetchLibSSH2.cmake
@@ -22,14 +22,18 @@ find_package(ZLIB REQUIRED)
include(FetchContent)
+set(PATCH_FILE_1
"${CMAKE_SOURCE_DIR}/thirdparty/libssh2/libssh2-CMAKE_MODULE_PATH.patch")
+set(PATCH_FILE_2
"${CMAKE_SOURCE_DIR}/thirdparty/libssh2/fix-windows-ioctl.patch")
+set(PATCH_FILE_3 "${CMAKE_SOURCE_DIR}/thirdparty/libssh2/fix-ecdh-leak.patch")
if (WIN32)
- set(PATCH_FILE_1
"${CMAKE_SOURCE_DIR}/thirdparty/libssh2/libssh2-CMAKE_MODULE_PATH.patch")
- set(PATCH_FILE_2
"${CMAKE_SOURCE_DIR}/thirdparty/libssh2/fix-windows-ioctl.patch")
set(PC ${Bash_EXECUTABLE} -c "set -x &&\
(\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_1}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_1}\\\") &&\
- (\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_2}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_2}\\\")")
+ (\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_2}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_2}\\\") &&\
+ (\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_3}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_3}\\\")")
else()
- set(PC "${Patch_EXECUTABLE}" -p1 -i
"${CMAKE_SOURCE_DIR}/thirdparty/libssh2/libssh2-CMAKE_MODULE_PATH.patch")
+ set(PC ${Bash_EXECUTABLE} -c "set -x &&\
+ (\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_1}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_1}\\\") &&\
+ (\\\"${Patch_EXECUTABLE}\\\" -p1 -R -s -f --dry-run -i
\\\"${PATCH_FILE_3}\\\" || \\\"${Patch_EXECUTABLE}\\\" -p1 -N -i
\\\"${PATCH_FILE_3}\\\")")
endif()
FetchContent_Declare(
diff --git a/cmake/Grpc.cmake b/cmake/Grpc.cmake
index de461cb4d..cab5f1044 100644
--- a/cmake/Grpc.cmake
+++ b/cmake/Grpc.cmake
@@ -39,7 +39,7 @@ set(PC ${Bash_EXECUTABLE} -c "set -x &&\
FetchContent_Declare(
grpc
GIT_REPOSITORY https://github.com/grpc/grpc
- GIT_TAG v1.82.0
+ GIT_TAG v1.83.1
GIT_SUBMODULES "third_party/cares/cares third_party/re2 third_party/upb"
PATCH_COMMAND "${PC}"
SYSTEM
diff --git a/cmake/valgrind.supp b/cmake/valgrind.supp
index 3a4ce186c..9beb95743 100644
--- a/cmake/valgrind.supp
+++ b/cmake/valgrind.supp
@@ -91,18 +91,22 @@
...
}
-# glibc grows the Thread-Local-Storage descriptor vector (DTV) via malloc the
-# first time spdlog::details::os::thread_id() runs on a new thread. The block
-# is kept for the thread's lifetime and freed by the OS at exit. Not a leak.
+# glibc grows the Thread-Local-Storage descriptor vector (DTV) the first time a
+# thread touches a dynamically-loaded TLS variable. The DTV block is kept for
+# the thread's lifetime and freed by the OS at exit; the growth happens only
+# through this __tls_get_addr -> _dl_update_slotinfo -> _dl_resize_dtv chain,
so
+# whatever sits above it (spdlog thread_id, __cxa_throw's __cxa_get_globals,
+# std::call_once, ...) is irrelevant. Never a real leak.
{
- possibly_lost_tls_dtv_spdlog_thread_id
+ possibly_lost_tls_dtv_growth
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
fun:_dl_resize_dtv
- ...
- fun:*spdlog*thread_id*
+ fun:_dl_update_slotinfo
+ fun:update_get_addr
+ fun:__tls_get_addr
...
}
@@ -119,3 +123,63 @@
fun:*ChannelInit*BuildStackConfig*
...
}
+
+# absl::flags_internal::FlagRegistry::RegisterFlag populates the
process-lifetime
+# global flag registry (an absl flat-hash-map) from static initializers (e.g.
+# gRPC's config_vars.cc) at dlopen()/startup time. Held via interior
abseil-hash
+# pointers, freed by the OS at exit. Not a leak.
+{
+ possibly_lost_absl_flags_registry_static_init
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:_Znwm
+ ...
+ fun:*FlagRegistry*RegisterFlag*
+ ...
+}
+
+# grpc_core::instrument_detail::InstrumentIndex::Register populates the
+# process-lifetime instrument/telemetry registry (a global absl node-hash-map).
+# It runs from static initializers in gRPC's telemetry.cc/instrument.cc while
an
+# extension is dlopen()ed. The map is held via interior abseil-hash pointers
and
+# freed by the OS at exit. Not a leak.
+{
+ possibly_lost_grpc_instrument_registry_static_init
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:_Znwm
+ ...
+ fun:*instrument_detail*Register*
+ ...
+}
+
+# grpc_core::BasicMemoryQuota::AddNewAllocator inserts each new allocator into
+# the process-lifetime MemoryQuota's allocator set (an absl flat-hash-set).
This
+# runs on gRPC's own EventEngine worker threads, which are still alive at exit,
+# so valgrind reaches the set's backing array via an interior pointer and flags
+# it "possibly lost". Owned by a process-lifetime singleton; not a leak.
+{
+ possibly_lost_grpc_memory_quota_add_allocator
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:_Znwm
+ ...
+ fun:*BasicMemoryQuota*AddNewAllocator*
+ ...
+}
+
+# grpc_core::CollectionScope registers itself (into an absl flat-hash-set) with
+# the process-lifetime GlobalStatsPluginRegistry the first time a
channel/server
+# is created (GetStatsPluginsForChannel / ...ForServer ->
CreateCollectionScope).
+# This runs on gRPC's EventEngine/WorkSerializer threads, so the set's backing
+# array is reached via an interior pointer and flagged "possibly lost". Owned
by
+# a process-lifetime singleton; not a leak.
+{
+ possibly_lost_grpc_stats_plugin_collection_scope
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:_Znwm
+ ...
+ fun:*CollectionScope*
+ ...
+}
diff --git a/conanfile.py b/conanfile.py
index fa7e07b78..18276242e 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -198,7 +198,7 @@ class MiNiFiCppMain(ConanFile):
if self.options.enable_all or self.options.enable_bustache:
self.requires("bustache/0.1.0@minifi/develop")
if self.options.enable_all or self.options.enable_grpc_for_loki:
- self.requires("grpc/1.82.0@minifi/develop", force=True)
+ self.requires("grpc/1.83.1@minifi/develop", force=True)
if self.options.enable_all or self.options.enable_gcp:
self.requires("google-cloud-cpp/2.47.1@minifi/develop")
if not self.options.skip_tests:
diff --git a/extension-framework/include/utils/net/ConnectionHandler.h
b/extension-framework/include/utils/net/ConnectionHandler.h
index b3d5075ec..ca269061a 100644
--- a/extension-framework/include/utils/net/ConnectionHandler.h
+++ b/extension-framework/include/utils/net/ConnectionHandler.h
@@ -17,6 +17,9 @@
#pragma once
+#include <span>
+#include <vector>
+
#include <asio/read.hpp>
#include "utils/net/AsioCoro.h"
@@ -64,7 +67,7 @@ class ConnectionHandler final : public ConnectionHandlerBase {
[[nodiscard]] asio::awaitable<std::error_code>
setupUsableSocket(asio::io_context& io_context) override;
[[nodiscard]] bool hasUsableSocket() const { return socket_ &&
socket_->lowest_layer().is_open(); }
- asio::awaitable<std::error_code> establishNewConnection(const
asio::ip::tcp::resolver::results_type& endpoints, asio::io_context&
io_context_);
+ asio::awaitable<std::error_code>
establishNewConnection(std::span<asio::ip::tcp::endpoint> endpoints,
asio::io_context& io_context_);
[[nodiscard]] asio::awaitable<std::tuple<std::error_code, size_t>>
write(const asio::const_buffer& buffer) override;
[[nodiscard]] asio::awaitable<std::tuple<std::error_code, size_t>>
read(asio::mutable_buffer& buffer) override;
@@ -116,19 +119,19 @@ inline void
ConnectionHandler<SslSocket>::shutdownSocket() {
}
template<class SocketType>
-asio::awaitable<std::error_code>
ConnectionHandler<SocketType>::establishNewConnection(const
asio::ip::tcp::resolver::results_type& endpoints, asio::io_context& io_context)
{
+asio::awaitable<std::error_code>
ConnectionHandler<SocketType>::establishNewConnection(std::span<asio::ip::tcp::endpoint>
endpoints, asio::io_context& io_context) {
auto socket = createNewSocket(io_context);
std::error_code last_error;
for (const auto& endpoint : endpoints) {
auto [connection_error] = co_await
asyncOperationWithTimeout(socket.lowest_layer().async_connect(endpoint,
use_nothrow_awaitable), timeout_duration_);
if (connection_error) {
- logger_->log_debug("Connecting to {} failed due to {}",
endpoint.endpoint(), connection_error.message());
+ logger_->log_debug("Connecting to {} failed due to {}", endpoint,
connection_error.message());
last_error = connection_error;
continue;
}
auto [handshake_error] = co_await handshake(socket, timeout_duration_);
if (handshake_error) {
- logger_->log_debug("Handshake with {} failed due to {}",
endpoint.endpoint(), handshake_error.message());
+ logger_->log_debug("Handshake with {} failed due to {}", endpoint,
handshake_error.message());
last_error = handshake_error;
continue;
}
@@ -144,12 +147,19 @@ template<class SocketType>
[[nodiscard]] asio::awaitable<std::error_code>
ConnectionHandler<SocketType>::setupUsableSocket(asio::io_context& io_context) {
if (hasUsableSocket())
co_return std::error_code();
- asio::ip::tcp::resolver resolver(io_context);
- auto [resolve_error, resolve_result] = co_await asyncOperationWithTimeout(
- resolver.async_resolve(connection_id_.getHostname(),
connection_id_.getService(), use_nothrow_awaitable), timeout_duration_);
- if (resolve_error)
- co_return resolve_error;
- co_return co_await establishNewConnection(resolve_result, io_context);
+ std::vector<asio::ip::tcp::endpoint> endpoints;
+ {
+ asio::ip::tcp::resolver resolver(io_context);
+ auto [resolve_error, resolve_result] = co_await asyncOperationWithTimeout(
+ resolver.async_resolve(connection_id_.getHostname(),
connection_id_.getService(), use_nothrow_awaitable), timeout_duration_);
+ if (resolve_error) {
+ co_return resolve_error;
+ }
+ for (const auto& entry : resolve_result) {
+ endpoints.push_back(entry.endpoint());
+ }
+ }
+ co_return co_await establishNewConnection(endpoints, io_context);
}
template<class SocketType>
diff --git a/extension-framework/include/utils/net/Server.h
b/extension-framework/include/utils/net/Server.h
index 34d6d89a8..b196a788f 100644
--- a/extension-framework/include/utils/net/Server.h
+++ b/extension-framework/include/utils/net/Server.h
@@ -16,6 +16,7 @@
*/
#pragma once
+#include <list>
#include <optional>
#include <string>
#include <utility>
@@ -26,8 +27,11 @@
#include "minifi-cpp/core/logging/Logger.h"
#include "asio/ts/buffer.hpp"
#include "asio/awaitable.hpp"
+#include "asio/bind_cancellation_slot.hpp"
+#include "asio/cancellation_signal.hpp"
#include "asio/co_spawn.hpp"
#include "asio/detached.hpp"
+#include "asio/post.hpp"
#include "Message.h"
namespace org::apache::nifi::minifi::utils::net {
@@ -35,14 +39,18 @@ namespace org::apache::nifi::minifi::utils::net {
class Server {
public:
virtual void run() {
- asio::co_spawn(io_context_, doReceive(), asio::detached);
+ asyncSpawn(doReceive());
io_context_.run();
}
virtual void reset() {
io_context_.restart();
}
virtual void stop() {
- io_context_.stop();
+ asio::post(io_context_, [this] {
+ for (auto& cancellation_signal : cancellation_signals_) {
+ cancellation_signal.emit(asio::cancellation_type::all);
+ }
+ });
}
bool queueEmpty() {
return concurrent_queue_.empty();
@@ -67,9 +75,22 @@ class Server {
Server(std::optional<size_t> max_queue_size, uint16_t port,
std::shared_ptr<core::logging::Logger> logger)
: port_(port), max_queue_size_(max_queue_size),
logger_(std::move(logger)) {}
+ // Spawn a coroutine on io_context_ with a cancellation slot so stop() can
end it and let the context drain
+ // gracefully. Must be called from the io_context thread (i.e. from run()
before io_context_.run(), or from within
+ // a coroutine running on it); cancellation_signals_ is only ever touched on
that thread, so it needs no locking.
+ // Exceptions are swallowed silently, as this is intended as a wrapper for
asio::co_spawn(..., asio::detached).
+ template<typename T>
+ void asyncSpawn(asio::awaitable<T> coroutine) {
+ const auto cancellation_signal_it =
cancellation_signals_.emplace(cancellation_signals_.end());
+ asio::co_spawn(io_context_, std::move(coroutine),
+ asio::bind_cancellation_slot(cancellation_signal_it->slot(),
+ [this, cancellation_signal_it](std::exception_ptr, auto&&...) {
cancellation_signals_.erase(cancellation_signal_it); }));
+ }
+
std::atomic<uint16_t> port_;
utils::ConcurrentQueue<Message> concurrent_queue_;
asio::io_context io_context_;
+ std::list<asio::cancellation_signal> cancellation_signals_;
std::optional<size_t> max_queue_size_;
std::shared_ptr<core::logging::Logger> logger_;
};
diff --git a/extension-framework/src/utils/net/TcpServer.cpp
b/extension-framework/src/utils/net/TcpServer.cpp
index abcf6b827..39a4a96ea 100644
--- a/extension-framework/src/utils/net/TcpServer.cpp
+++ b/extension-framework/src/utils/net/TcpServer.cpp
@@ -29,6 +29,9 @@ asio::awaitable<void> TcpServer::doReceive() {
while (true) {
auto [accept_error, socket] = co_await
acceptor.async_accept(use_nothrow_awaitable);
if (accept_error) {
+ if (accept_error == asio::error::operation_aborted) {
+ co_return;
+ }
logger_->log_error("Error during accepting new connection: {}",
accept_error.message());
co_await utils::net::async_wait(1s);
continue;
@@ -43,9 +46,9 @@ asio::awaitable<void> TcpServer::doReceive() {
logger_->log_warn("Error during fetching local endpoint: {}",
error.message());
}
if (ssl_data_) {
- co_spawn(io_context_, secureSession(std::move(socket),
remote_endpoint.address(), remote_endpoint.port(), local_port), asio::detached);
+ asyncSpawn(secureSession(std::move(socket), remote_endpoint.address(),
remote_endpoint.port(), local_port));
} else {
- co_spawn(io_context_, insecureSession(std::move(socket),
remote_endpoint.address(), remote_endpoint.port(), local_port), asio::detached);
+ asyncSpawn(insecureSession(std::move(socket), remote_endpoint.address(),
remote_endpoint.port(), local_port));
}
}
}
@@ -55,7 +58,7 @@ asio::awaitable<void> TcpServer::readLoop(auto& socket,
asio::ip::address remote
while (true) {
auto [read_error, bytes_read] = co_await asio::async_read_until(socket,
asio::dynamic_buffer(read_message), delimiter_, use_nothrow_awaitable); //
NOLINT
if (read_error) {
- if (read_error != asio::error::eof) {
+ if (read_error != asio::error::eof && read_error !=
asio::error::operation_aborted) {
logger_->log_error("Error during reading from socket: {}",
read_error.message());
}
co_return;
diff --git a/extension-framework/src/utils/net/UdpServer.cpp
b/extension-framework/src/utils/net/UdpServer.cpp
index 35f0933d7..342cb6f7e 100644
--- a/extension-framework/src/utils/net/UdpServer.cpp
+++ b/extension-framework/src/utils/net/UdpServer.cpp
@@ -37,6 +37,9 @@ asio::awaitable<void> UdpServer::doReceive() {
auto [receive_error, bytes_received] = co_await
socket.async_receive_from(asio::buffer(buffer, MAX_UDP_PACKET_SIZE),
sender_endpoint, utils::net::use_nothrow_awaitable);
if (receive_error) {
+ if (receive_error == asio::error::operation_aborted) {
+ co_return;
+ }
logger_->log_warn("Error during receive: {}", receive_error.message());
continue;
}
diff --git a/extensions/aws/tests/MockS3RequestSender.h
b/extensions/aws/tests/MockS3RequestSender.h
index 2ee77cae0..24656d2d7 100644
--- a/extensions/aws/tests/MockS3RequestSender.h
+++ b/extensions/aws/tests/MockS3RequestSender.h
@@ -28,6 +28,7 @@
#include "s3/S3RequestSender.h"
#include "aws/core/utils/DateTime.h"
+#include "aws/core/utils/memory/AWSMemory.h"
const std::string S3_VERSION_1 = "1.2.3";
const std::string S3_VERSION_2 = "1.2.4";
@@ -133,7 +134,7 @@ class MockS3RequestSender : public
minifi::aws::s3::S3RequestSender {
get_s3_result.SetExpiration(S3_EXPIRATION);
get_s3_result.SetServerSideEncryption(S3_SSEALGORITHM);
get_s3_result.SetContentType(S3_CONTENT_TYPE);
- get_s3_result.ReplaceBody(new std::stringstream(S3_CONTENT));
+
get_s3_result.ReplaceBody(Aws::New<std::stringstream>("MockS3RequestSender",
S3_CONTENT));
get_s3_result.SetContentLength(S3_CONTENT.size());
get_s3_result.SetMetadata(S3_OBJECT_USER_METADATA);
}
diff --git a/extensions/couchbase/CMakeLists.txt
b/extensions/couchbase/CMakeLists.txt
index 8677e7474..7267c6fed 100644
--- a/extensions/couchbase/CMakeLists.txt
+++ b/extensions/couchbase/CMakeLists.txt
@@ -34,6 +34,8 @@ target_include_directories(minifi-couchbase SYSTEM PRIVATE
${COUCHBASE_INCLUDE_D
# Demote preprocessor warnings so <ciso646> doesn't break the build
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(minifi-couchbase PRIVATE -Wno-error=cpp)
+elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ target_compile_options(minifi-couchbase PRIVATE -Wno-error=\#warnings)
endif()
target_link_libraries(minifi-couchbase ${LIBMINIFI}
couchbase_cxx_client::couchbase_cxx_client hdr_histogram_static snappy
llhttp::llhttp taocpp::json)
diff --git a/extensions/couchbase/tests/CMakeLists.txt
b/extensions/couchbase/tests/CMakeLists.txt
index 99f0e0172..d050042e7 100644
--- a/extensions/couchbase/tests/CMakeLists.txt
+++ b/extensions/couchbase/tests/CMakeLists.txt
@@ -33,6 +33,8 @@ FOREACH(testfile ${COUCHBASE_TESTS})
# Demote preprocessor warnings so <ciso646> doesn't break the build
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(${testfilename} PRIVATE -Wno-error=cpp)
+ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ target_compile_options(${testfilename} PRIVATE -Wno-error=\#warnings)
endif()
createTests("${testfilename}")
target_link_libraries(${testfilename} Catch2WithMain)
diff --git a/extensions/python/types/Types.h b/extensions/python/types/Types.h
index 98f114bab..9e3e3f741 100644
--- a/extensions/python/types/Types.h
+++ b/extensions/python/types/Types.h
@@ -246,7 +246,8 @@ class List : public ReferenceHolder<reference_type> {
template<object::convertible T>
void append(T value) {
- PyList_Append(this->ref_.get(),
object::from(std::move(value)).releaseReference());
+ auto value_object = object::from(std::move(value));
+ PyList_Append(this->ref_.get(), value_object.get());
}
size_t length() {
@@ -296,7 +297,8 @@ class Dict : public ReferenceHolder<reference_type> {
template<object::convertible T>
void put(const char* key, T value) {
- PyDict_SetItemString(this->ref_.get(), key,
object::from(std::move(value)).releaseReference());
+ auto value_object = object::from(std::move(value));
+ PyDict_SetItemString(this->ref_.get(), key, value_object.get());
}
template<object::convertible T>
diff --git a/extensions/standard-processors/processors/GetTCP.cpp
b/extensions/standard-processors/processors/GetTCP.cpp
index ee515662b..c4c7c78af 100644
--- a/extensions/standard-processors/processors/GetTCP.cpp
+++ b/extensions/standard-processors/processors/GetTCP.cpp
@@ -231,17 +231,23 @@ asio::awaitable<std::error_code>
GetTCP::TcpClient::doReceiveFromEndpoint(const
asio::awaitable<void> GetTCP::TcpClient::doReceiveFrom(const
utils::net::ConnectionId& connection_id) {
while (true) {
- asio::ip::tcp::resolver resolver(io_context_);
- auto [resolve_error, resolve_result] = co_await
utils::net::asyncOperationWithTimeout( // NOLINT
- resolver.async_resolve(connection_id.getHostname(),
connection_id.getService(), utils::net::use_nothrow_awaitable),
timeout_duration_);
- if (resolve_error) {
- logger_->log_error("Error during resolution: {}",
resolve_error.message());
- co_await utils::net::async_wait(reconnection_interval_);
- continue;
+ std::vector<asio::ip::tcp::endpoint> endpoints;
+ {
+ asio::ip::tcp::resolver resolver(io_context_);
+ auto [resolve_error, resolve_result] = co_await
utils::net::asyncOperationWithTimeout(
+ resolver.async_resolve(connection_id.getHostname(),
connection_id.getService(), utils::net::use_nothrow_awaitable),
timeout_duration_);
+ if (resolve_error) {
+ logger_->log_error("Error during resolution: {}",
resolve_error.message());
+ co_await utils::net::async_wait(reconnection_interval_);
+ continue;
+ }
+ for (const auto& entry : resolve_result) {
+ endpoints.push_back(entry.endpoint());
+ }
}
std::error_code last_error;
- for (const auto& endpoint : resolve_result) {
+ for (const auto& endpoint : endpoints) {
if (ssl_context_) {
utils::net::SslSocket ssl_socket{io_context_, *ssl_context_};
last_error = co_await
doReceiveFromEndpoint<utils::net::SslSocket>(endpoint, ssl_socket);
diff --git a/extensions/standard-processors/tests/unit/PutTCPTests.cpp
b/extensions/standard-processors/tests/unit/PutTCPTests.cpp
index 5e1e8a359..62b8c4e51 100644
--- a/extensions/standard-processors/tests/unit/PutTCPTests.cpp
+++ b/extensions/standard-processors/tests/unit/PutTCPTests.cpp
@@ -71,9 +71,9 @@ class CancellableTcpServer : public utils::net::TcpServer {
auto cancellable_timer =
std::make_shared<asio::steady_timer>(io_context_);
cancellable_timers_.push_back(cancellable_timer);
if (ssl_data_)
- co_spawn(io_context_, secureSession(std::move(socket),
std::move(remote_address), remote_port, port_) ||
wait_until_cancelled(cancellable_timer), asio::detached);
+ asyncSpawn(secureSession(std::move(socket), std::move(remote_address),
remote_port, port_) || wait_until_cancelled(cancellable_timer));
else
- co_spawn(io_context_, insecureSession(std::move(socket),
std::move(remote_address), remote_port, port_) ||
wait_until_cancelled(cancellable_timer), asio::detached);
+ asyncSpawn(insecureSession(std::move(socket),
std::move(remote_address), remote_port, port_) ||
wait_until_cancelled(cancellable_timer));
}
}
diff --git a/libminifi/src/core/FlowConfiguration.cpp
b/libminifi/src/core/FlowConfiguration.cpp
index c63ce6bd9..7edcad89d 100644
--- a/libminifi/src/core/FlowConfiguration.cpp
+++ b/libminifi/src/core/FlowConfiguration.cpp
@@ -27,6 +27,7 @@
#include "utils/StringUtils.h"
#include "utils/file/FileUtils.h"
#include "minifi-cpp/SwapManager.h"
+#include "minifi-cpp/utils/gsl.h"
#include "Connection.h"
namespace {
@@ -113,11 +114,19 @@ std::unique_ptr<core::ProcessGroup>
FlowConfiguration::updateFromPayload(const s
auto old_parameter_contexts = std::move(parameter_contexts_);
auto old_parameter_providers = std::move(parameter_providers_);
service_provider_ =
std::make_shared<core::controller::StandardControllerServiceProvider>(std::make_unique<core::controller::ControllerServiceNodeMap>(),
configuration_);
+
+ bool success = false;
+ auto restore_on_failure = gsl::finally([&] {
+ if (!success) {
+ service_provider_->clearControllerServices();
+ service_provider_ = old_provider;
+ parameter_contexts_ = std::move(old_parameter_contexts);
+ parameter_providers_ = std::move(old_parameter_providers);
+ }
+ });
+
auto payload = getRootFromPayload(yamlConfigPayload);
if (!payload) {
- service_provider_ = old_provider;
- parameter_contexts_ = std::move(old_parameter_contexts);
- parameter_providers_ = std::move(old_parameter_providers);
return nullptr;
}
@@ -135,6 +144,7 @@ std::unique_ptr<core::ProcessGroup>
FlowConfiguration::updateFromPayload(const s
flow_version_->setFlowVersion(url, bucket_id, flow_id ? *flow_id :
payload_flow_id);
}
+ success = true;
return payload;
}
diff --git a/libminifi/test/integration/HTTPSiteToSiteTests.cpp
b/libminifi/test/integration/HTTPSiteToSiteTests.cpp
index c7da04333..faa487bdd 100644
--- a/libminifi/test/integration/HTTPSiteToSiteTests.cpp
+++ b/libminifi/test/integration/HTTPSiteToSiteTests.cpp
@@ -94,7 +94,7 @@ void run_variance(const std::filesystem::path&
test_file_location, const std::st
auto responder = std::make_unique<SiteToSiteLocationResponder>(false);
- auto *transaction_response = new TransactionResponder(url, in_port,
+ auto transaction_response = std::make_unique<TransactionResponder>(url,
in_port,
true, profile.transaction_url_broken, profile.empty_transaction_url);
std::string transaction_id = transaction_response->getTransactionId();
@@ -104,9 +104,9 @@ void run_variance(const std::filesystem::path&
test_file_location, const std::st
std::string controller_loc = url + "/controller";
std::string basesitetosite = url + "/site-to-site";
- auto *base = new SiteToSiteBaseResponder(basesitetosite);
+ auto base = std::make_unique<SiteToSiteBaseResponder>(basesitetosite);
- harness.setUrl(basesitetosite, base);
+ harness.setUrl(basesitetosite, base.get());
harness.setUrl(controller_loc, responder.get());
@@ -116,13 +116,13 @@ void run_variance(const std::filesystem::path&
test_file_location, const std::st
std::string transaction_output_url = url + "/data-transfer/output-ports/" +
out_port + "/transactions";
std::string action_output_url = url + "/site-to-site/output-ports/" +
out_port + "/transactions";
- harness.setUrl(transaction_url, transaction_response);
+ harness.setUrl(transaction_url, transaction_response.get());
std::string peer_url = url + "/site-to-site/peers";
- auto *peer_response = new PeerResponder(url);
+ auto peer_response = std::make_unique<PeerResponder>(url);
- harness.setUrl(peer_url, peer_response);
+ harness.setUrl(peer_url, peer_response.get());
std::string flow_url = action_url + "/" + transaction_id + "/flow-files";
@@ -130,12 +130,12 @@ void run_variance(const std::filesystem::path&
test_file_location, const std::st
flowResponder->setFlowUrl(flow_url);
auto producedFlows = flowResponder->getFlows();
- auto *transaction_response_output = new TransactionResponder(url, out_port,
+ auto transaction_response_output =
std::make_unique<TransactionResponder>(url, out_port,
false, profile.transaction_url_broken, profile.empty_transaction_url);
std::string transaction_output_id =
transaction_response_output->getTransactionId();
transaction_response_output->setFeed(producedFlows);
- harness.setUrl(transaction_output_url, transaction_response_output);
+ harness.setUrl(transaction_output_url, transaction_response_output.get());
std::string flow_output_url = action_output_url + "/" +
transaction_output_id + "/flow-files";
@@ -147,12 +147,12 @@ void run_variance(const std::filesystem::path&
test_file_location, const std::st
harness.setUrl(flow_output_url, flowOutputResponder.get());
std::string delete_url = transaction_url + "/" + transaction_id;
- auto *deleteResponse = new DeleteTransactionResponder(delete_url, "201 OK",
12);
- harness.setUrl(delete_url, deleteResponse);
+ auto deleteResponse =
std::make_unique<DeleteTransactionResponder>(delete_url, "201 OK", 12);
+ harness.setUrl(delete_url, deleteResponse.get());
std::string delete_output_url = transaction_output_url + "/" +
transaction_output_id;
- auto *deleteOutputResponse = new
DeleteTransactionResponder(delete_output_url, "201 OK", producedFlows);
- harness.setUrl(delete_output_url, deleteOutputResponse);
+ auto deleteOutputResponse =
std::make_unique<DeleteTransactionResponder>(delete_output_url, "201 OK",
producedFlows);
+ harness.setUrl(delete_output_url, deleteOutputResponse.get());
harness.run();
diff --git a/libminifi/test/libtest/integration/HTTPHandlers.cpp
b/libminifi/test/libtest/integration/HTTPHandlers.cpp
index 5705dc8b8..517c6fbae 100644
--- a/libminifi/test/libtest/integration/HTTPHandlers.cpp
+++ b/libminifi/test/libtest/integration/HTTPHandlers.cpp
@@ -34,6 +34,11 @@
#include "utils/net/DNS.h"
#include "io/validation.h"
+namespace {
+std::atomic<int> transaction_id;
+std::atomic<int> transaction_id_output;
+} // namespace
+
namespace org::apache::nifi::minifi::test {
bool SiteToSiteLocationResponder::handleGet(CivetServer* /*server*/, struct
mg_connection *conn) {
diff --git a/libminifi/test/libtest/integration/HTTPHandlers.h
b/libminifi/test/libtest/integration/HTTPHandlers.h
index 40304948a..74cb5a74e 100644
--- a/libminifi/test/libtest/integration/HTTPHandlers.h
+++ b/libminifi/test/libtest/integration/HTTPHandlers.h
@@ -40,9 +40,6 @@
namespace org::apache::nifi::minifi::test {
-static std::atomic<int> transaction_id;
-static std::atomic<int> transaction_id_output;
-
struct FlowObj {
FlowObj() = default;
diff --git a/libminifi/test/libtest/unit/TestUtils.cpp
b/libminifi/test/libtest/unit/TestUtils.cpp
index 4ec2fda76..d417089c0 100644
--- a/libminifi/test/libtest/unit/TestUtils.cpp
+++ b/libminifi/test/libtest/unit/TestUtils.cpp
@@ -273,7 +273,7 @@ std::vector<LogMessageView> extractLogMessageViews(const
std::string& log_str) {
std::vector<HeaderMarker> markers =
ranges::subrange<std::sregex_iterator>(std::sregex_iterator(log_str.begin(),
log_str.end(), header_pattern),
std::sregex_iterator()) |
- ranges::views::transform([=](const std::smatch& m) {
+ ranges::views::transform([&log_str](const std::smatch& m) {
return HeaderMarker{.start = static_cast<size_t>(m.position(0)),
.timestamp = std::string_view{log_str.data() + m.position(1),
static_cast<size_t>(m.length(1))},
.logger_class = std::string_view{log_str.data() + m.position(2),
static_cast<size_t>(m.length(2))},
diff --git a/libminifi/test/unit/FileStreamTests.cpp
b/libminifi/test/unit/FileStreamTests.cpp
index e73fd47be..8fd369da5 100644
--- a/libminifi/test/unit/FileStreamTests.cpp
+++ b/libminifi/test/unit/FileStreamTests.cpp
@@ -124,36 +124,6 @@ TEST_CASE("TestFileBadArgumentNoChange2", "[TestLoader]") {
REQUIRE(std::string(reinterpret_cast<char*>(data), verifybuffer.size()) ==
"tempFile");
}
-TEST_CASE("TestFileBadArgumentNoChange3", "[TestLoader]") {
- TestController testController;
- auto path = testController.createTempDirectory() / "tstFile.ext";
-
- std::fstream file;
- file.open(path, std::ios::out);
- file << "tempFile";
- file.close();
-
- minifi::io::FileStream stream(path, 0, true);
- std::vector<std::byte> readBuffer;
- readBuffer.resize(stream.size());
- REQUIRE(stream.read(readBuffer) == stream.size());
-
- auto* data = readBuffer.data();
-
- REQUIRE(std::string(reinterpret_cast<char*>(data), readBuffer.size()) ==
"tempFile");
-
- stream.seek(4);
-
- stream.write(nullptr, 0);
-
- stream.seek(0);
-
- std::vector<std::byte> verifybuffer;
- data = verifybuffer.data();
-
- REQUIRE(std::string(reinterpret_cast<char*>(data),
verifybuffer.size()).empty());
-}
-
TEST_CASE("TestFileBeyondEnd3", "[TestLoader]") {
TestController testController;
const auto path = testController.createTempDirectory() / "tstFile.ext";
diff --git a/libminifi/test/unit/FileSystemRepositoryTests.cpp
b/libminifi/test/unit/FileSystemRepositoryTests.cpp
index b54a7b3f5..bdf065c6b 100644
--- a/libminifi/test/unit/FileSystemRepositoryTests.cpp
+++ b/libminifi/test/unit/FileSystemRepositoryTests.cpp
@@ -20,6 +20,14 @@
// as we measure the absolute memory usage that would fail this test
#define EXTENSION_LIST "" // NOLINT(cppcoreguidelines-macro-usage)
+#if defined(__SANITIZE_ADDRESS__) // GCC
+# define MINIFI_ASAN_ENABLED 1
+#elif defined(__has_feature)
+# if __has_feature(address_sanitizer) // Clang
+# define MINIFI_ASAN_ENABLED 1
+# endif
+#endif
+
#include <list>
#include "minifi-cpp/utils/gsl.h"
@@ -45,6 +53,9 @@ class TestFileSystemRepository : public
minifi::core::repository::FileSystemRepo
};
TEST_CASE("Test Physical memory usage", "[testphysicalmemoryusage]") {
+#ifdef MINIFI_ASAN_ENABLED
+ SKIP("Under AddressSanitizer builds, memory isn't released, so this test
fails.");
+#endif
TestController controller;
auto dir = controller.createTempDirectory();
auto fs_repo =
std::make_shared<minifi::core::repository::FileSystemRepository>();
diff --git a/thirdparty/abseil/rename-crc32.patch
b/thirdparty/abseil/rename-crc32.patch
index 026a43004..d42223ef1 100644
--- a/thirdparty/abseil/rename-crc32.patch
+++ b/thirdparty/abseil/rename-crc32.patch
@@ -1,8 +1,7 @@
-diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
-index 32cc28fb..8e9dc702 100644
---- a/CMake/AbseilDll.cmake
-+++ b/CMake/AbseilDll.cmake
-@@ -521,7 +521,7 @@ set(ABSL_INTERNAL_DLL_TARGETS
+diff -ur a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
+--- a/CMake/AbseilDll.cmake 2026-08-18 14:53:30.000000000 +0200
++++ b/CMake/AbseilDll.cmake 2026-09-03 13:22:10.161368576 +0200
+@@ -548,7 +548,7 @@
"crc_cord_state"
"crc_cpu_detect"
"crc_internal"
@@ -11,11 +10,10 @@ index 32cc28fb..8e9dc702 100644
"debugging"
"debugging_internal"
"demangle_internal"
-diff --git a/absl/crc/BUILD.bazel b/absl/crc/BUILD.bazel
-index 890d637c..8d680c22 100644
---- a/absl/crc/BUILD.bazel
-+++ b/absl/crc/BUILD.bazel
-@@ -72,7 +72,7 @@ cc_library(
+diff -ur a/absl/crc/BUILD.bazel b/absl/crc/BUILD.bazel
+--- a/absl/crc/BUILD.bazel 2026-08-18 14:53:30.000000000 +0200
++++ b/absl/crc/BUILD.bazel 2026-09-03 13:22:10.161668055 +0200
+@@ -60,7 +60,7 @@
)
cc_library(
@@ -24,11 +22,10 @@ index 890d637c..8d680c22 100644
srcs = [
"crc32c.cc",
"internal/crc32c_inline.h",
-diff --git a/absl/crc/CMakeLists.txt b/absl/crc/CMakeLists.txt
-index d52a1bc4..3cec9dcb 100644
---- a/absl/crc/CMakeLists.txt
-+++ b/absl/crc/CMakeLists.txt
-@@ -53,7 +53,7 @@ absl_cc_library(
+diff -ur a/absl/crc/CMakeLists.txt b/absl/crc/CMakeLists.txt
+--- a/absl/crc/CMakeLists.txt 2026-08-18 14:53:30.000000000 +0200
++++ b/absl/crc/CMakeLists.txt 2026-09-03 13:22:10.161889990 +0200
+@@ -38,7 +38,7 @@
absl_cc_library(
NAME
@@ -37,7 +34,7 @@ index d52a1bc4..3cec9dcb 100644
HDRS
"crc32c.h"
"internal/crc32c.h"
-@@ -86,7 +86,7 @@ absl_cc_test(
+@@ -71,7 +71,7 @@
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -46,7 +43,7 @@ index d52a1bc4..3cec9dcb 100644
absl::strings
absl::str_format
GTest::gtest_main
-@@ -126,7 +126,7 @@ absl_cc_test(
+@@ -111,7 +111,7 @@
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -55,7 +52,7 @@ index d52a1bc4..3cec9dcb 100644
absl::memory
absl::random_random
absl::random_distributions
-@@ -156,7 +156,7 @@ absl_cc_library(
+@@ -141,7 +141,7 @@
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -64,7 +61,7 @@ index d52a1bc4..3cec9dcb 100644
absl::config
absl::strings
absl::no_destructor
-@@ -171,6 +171,6 @@ absl_cc_test(
+@@ -156,6 +156,6 @@
${ABSL_DEFAULT_COPTS}
DEPS
absl::crc_cord_state
@@ -72,11 +69,10 @@ index d52a1bc4..3cec9dcb 100644
+ absl::crc32c_internal
GTest::gtest_main
)
-diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
-index 3a1619e8..2c368cf2 100644
---- a/absl/strings/CMakeLists.txt
-+++ b/absl/strings/CMakeLists.txt
-@@ -987,7 +987,7 @@ absl_cc_library(
+diff -ur a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
+--- a/absl/strings/CMakeLists.txt 2026-08-18 14:53:30.000000000 +0200
++++ b/absl/strings/CMakeLists.txt 2026-09-03 13:22:10.162082289 +0200
+@@ -1075,7 +1075,7 @@
absl::cordz_update_scope
absl::cordz_update_tracker
absl::core_headers
diff --git a/thirdparty/couchbase/all/conanfile.py
b/thirdparty/couchbase/all/conanfile.py
index de0e27977..5a961859e 100644
--- a/thirdparty/couchbase/all/conanfile.py
+++ b/thirdparty/couchbase/all/conanfile.py
@@ -69,7 +69,7 @@ class CouchbaseCxxClientConan(ConanFile):
self.requires("openssl/[>=1.1 <4]")
def build_requirements(self):
- self.tool_requires("cmake/[>=3.19.0]")
+ self.tool_requires("cmake/[>=3.19.0 <4]")
def layout(self):
cmake_layout(self, src_folder="src")
diff --git a/thirdparty/grpc/all/conandata.yml
b/thirdparty/grpc/all/conandata.yml
index 9acfb37b5..49684e16a 100644
--- a/thirdparty/grpc/all/conandata.yml
+++ b/thirdparty/grpc/all/conandata.yml
@@ -14,11 +14,11 @@
# limitations under the License.
sources:
- "1.82.0":
- url: "https://github.com/grpc/grpc/archive/refs/tags/v1.82.0.tar.gz"
- sha256: "d6851f59b9c4edb3218d2659a3abf1138e5bb4d2871c9e89c9330ed71756ed0b"
+ "1.83.1":
+ url: "https://github.com/grpc/grpc/archive/refs/tags/v1.83.1.tar.gz"
+ sha256: "60caa8397426d8a500e3e57ab6a49d1cb5aa62a36f2591eb9da3d77fa38ad8c9"
patches:
- "1.82.0":
+ "1.83.1":
- patch_file: "patches/fix-msvc-auto-return-type-template-arg.patch"
patch_description: "Temporary patch for issue
https://github.com/grpc/grpc/issues/41436"
patch_type: "fix"
diff --git a/thirdparty/grpc/all/conanfile.py b/thirdparty/grpc/all/conanfile.py
index 71b4aa018..345f26eba 100644
--- a/thirdparty/grpc/all/conanfile.py
+++ b/thirdparty/grpc/all/conanfile.py
@@ -139,7 +139,7 @@ class GrpcConan(ConanFile):
def build_requirements(self):
# cmake >=3.25 required to use `cmake -E env --modify` below
- self.tool_requires("cmake/[>=3.25]")
+ self.tool_requires("cmake/[>=3.25 <4]")
self.tool_requires("protobuf/<host_version>")
if cross_building(self):
# when cross compiling we need pre compiled grpc plugins for protoc
diff --git
a/thirdparty/grpc/all/patches/fix-msvc-auto-return-type-template-arg.patch
b/thirdparty/grpc/all/patches/fix-msvc-auto-return-type-template-arg.patch
index de6ca850c..10b05a315 100644
--- a/thirdparty/grpc/all/patches/fix-msvc-auto-return-type-template-arg.patch
+++ b/thirdparty/grpc/all/patches/fix-msvc-auto-return-type-template-arg.patch
@@ -1,12 +1,12 @@
Temporary patch for issue https://github.com/grpc/grpc/issues/41436
diff --git a/src/core/call/call_filters.h b/src/core/call/call_filters.h
-index 4ec8ac8473..95be3e008c 100644
+index 3948167..4f8b876 100644
--- a/src/core/call/call_filters.h
+++ b/src/core/call/call_filters.h
@@ -1627,17 +1627,26 @@ constexpr bool MethodHasChannelAccess<R (T::*)(A, C)>
= true;
template <typename... Ts>
constexpr bool AnyMethodHasChannelAccess = (MethodHasChannelAccess<Ts> ||
...);
-
+
+// Helper for CallHasChannelAccess() that accepts method pointers as function
+// arguments to avoid MSVC C3539 errors when methods have deduced return types
+// (e.g. for FusedFilter types whose Call methods have auto return types).
@@ -36,15 +36,15 @@ index 4ec8ac8473..95be3e008c 100644
+ &Derived::Call::OnFinalize);
}
} // namespace filters_detail
-
+
diff --git a/src/core/lib/channel/promise_based_filter.h
b/src/core/lib/channel/promise_based_filter.h
-index 15fa55a4e7..178c12af8e 100644
+index 3d209e6..f07dea9 100644
--- a/src/core/lib/channel/promise_based_filter.h
+++ b/src/core/lib/channel/promise_based_filter.h
-@@ -1122,6 +1122,37 @@ MakeFilterCall(Derived* derived) {
+@@ -1124,6 +1124,37 @@ MakeFilterCall(Derived* derived) {
return GetContext<Arena>()->ManagedNew<FilterCallData<Derived>>(derived);
}
-
+
+// Helper functions for ImplementChannelFilter::MakeCallPromise() that accept
+// method pointers as function arguments to avoid MSVC C3539 errors when
+// methods have deduced return types (e.g. for FusedFilter types).
@@ -77,9 +77,9 @@ index 15fa55a4e7..178c12af8e 100644
+}
+
} // namespace promise_filter_detail
-
+
// Base class for promise-based channel filters.
-@@ -1192,22 +1223,16 @@ class ImplementChannelFilter : public ChannelFilter,
+@@ -1194,22 +1225,16 @@ class ImplementChannelFilter : public ChannelFilter,
CallArgs call_args, NextPromiseFactory next_promise_factory) final {
auto* call = promise_filter_detail::MakeFilterCall<Derived>(
static_cast<Derived*>(this));
diff --git a/thirdparty/grpc/all/target_info/grpc_1.82.0.yml
b/thirdparty/grpc/all/target_info/grpc_1.83.1.yml
similarity index 99%
rename from thirdparty/grpc/all/target_info/grpc_1.82.0.yml
rename to thirdparty/grpc/all/target_info/grpc_1.83.1.yml
index 878fc9265..56e0da9bc 100644
--- a/thirdparty/grpc/all/target_info/grpc_1.82.0.yml
+++ b/thirdparty/grpc/all/target_info/grpc_1.83.1.yml
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-grpc_version: 1.82.0
+grpc_version: 1.83.1
grpc_targets:
- name: "address_sorting"
lib: "address_sorting"
diff --git a/thirdparty/grpc/config.yml b/thirdparty/grpc/config.yml
index a69bf44bd..1a9d99397 100644
--- a/thirdparty/grpc/config.yml
+++ b/thirdparty/grpc/config.yml
@@ -14,5 +14,5 @@
# limitations under the License.
versions:
- "1.82.0":
+ "1.83.1":
folder: "all"
diff --git a/thirdparty/libssh2/fix-ecdh-leak.patch
b/thirdparty/libssh2/fix-ecdh-leak.patch
new file mode 100644
index 000000000..4c0bc8b76
--- /dev/null
+++ b/thirdparty/libssh2/fix-ecdh-leak.patch
@@ -0,0 +1,18 @@
+diff --git a/src/openssl.c b/src/openssl.c
+--- a/src/openssl.c
++++ b/src/openssl.c
+@@ -4345,6 +4345,15 @@ _libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key
*private_key,
+
+ clean_exit:
+ #ifdef USE_OPENSSL_3
++ if(bn_ctx)
++ BN_CTX_free(bn_ctx);
++
++ if(key_fromdata_ctx)
++ EVP_PKEY_CTX_free(key_fromdata_ctx);
++
++ if(peer_key)
++ EVP_PKEY_free(peer_key);
++
+ if(group_name)
+ OPENSSL_clear_free(group_name, group_name_len);