From 7a7177fddd5b5d0dde84d2df4d3b653588c53c0d Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 29 Sep 2026 14:44:11 +0530 Subject: [PATCH 01/32] #75: Add socket library API stubs --- udf-runner-cpp/v2/BUILD.bazel | 15 +++ .../v2/include/exasol/udf/v2/socket.hpp | 67 ++++++++++ .../v2/include/exasol/udf/v2/unix_socket.hpp | 68 ++++++++++ udf-runner-cpp/v2/socket.cc | 57 ++++++++ udf-runner-cpp/v2/unix_socket.cc | 124 ++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp create mode 100644 udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp create mode 100644 udf-runner-cpp/v2/socket.cc create mode 100644 udf-runner-cpp/v2/unix_socket.cc diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index fbca2d0..2c1c1ad 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -355,6 +355,21 @@ cc_library( target_compatible_with = ["@platforms//os:linux"], ) +cc_library( + name = "socket", + srcs = [ + "socket.cc", + "unix_socket.cc", + ], + hdrs = [ + "include/exasol/udf/v2/socket.hpp", + "include/exasol/udf/v2/unix_socket.hpp", + ], + includes = ["include"], + copts = ["-std=c++23"], + target_compatible_with = ["@platforms//os:linux"], +) + cc_library( name = "linux_event_fd", srcs = ["linux_event_fd.cc"], diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp new file mode 100644 index 0000000..7304658 --- /dev/null +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include + +namespace exasol::udf::v2::socket +{ + +enum class Shutdown +{ + receive, + send, + both, +}; + +class OwnedFileDescriptor +{ +public: + OwnedFileDescriptor() noexcept; + ~OwnedFileDescriptor(); + + [[nodiscard]] static OwnedFileDescriptor adopt_native_handle(int owned_fd); + + OwnedFileDescriptor(const OwnedFileDescriptor&) = delete; + OwnedFileDescriptor& operator=(const OwnedFileDescriptor&) = delete; + OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept; + OwnedFileDescriptor& operator=(OwnedFileDescriptor&& other) noexcept; + + [[nodiscard]] int native_handle() const noexcept; + [[nodiscard]] bool is_open() const noexcept; + void close() noexcept; + [[nodiscard]] int release_native_handle() noexcept; + +private: + explicit OwnedFileDescriptor(int owned_fd) noexcept; + + int file_descriptor = -1; +}; + +class Socket +{ +public: + virtual ~Socket() = default; + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + [[nodiscard]] virtual int native_handle() const noexcept = 0; + [[nodiscard]] virtual bool is_open() const noexcept = 0; + + virtual void close() noexcept = 0; + [[nodiscard]] virtual OwnedFileDescriptor release_native_handle() noexcept = 0; + virtual void shutdown(Shutdown how) = 0; + + virtual std::size_t read_some(std::span> buffers) = 0; + virtual std::size_t write_some(std::span> buffers) = 0; + + std::size_t read_some(std::span buffer); + std::size_t write_some(std::span buffer); + +protected: + Socket() = default; + Socket(Socket&&) = default; + Socket& operator=(Socket&&) = default; +}; + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp new file mode 100644 index 0000000..c888b21 --- /dev/null +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#include + +namespace exasol::udf::v2::socket +{ + +class UnixSocket final : public Socket +{ +public: + UnixSocket() noexcept; + ~UnixSocket() override; + + UnixSocket(const UnixSocket&) = delete; + UnixSocket& operator=(const UnixSocket&) = delete; + UnixSocket(UnixSocket&& other) noexcept; + UnixSocket& operator=(UnixSocket&& other) noexcept; + + [[nodiscard]] static UnixSocket connect(const std::filesystem::path& path); + [[nodiscard]] static UnixSocket adopt_native_handle(OwnedFileDescriptor owned_fd); + + [[nodiscard]] int native_handle() const noexcept override; + [[nodiscard]] bool is_open() const noexcept override; + void close() noexcept override; + [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept override; + void shutdown(Shutdown how) override; + std::size_t read_some(std::span> buffers) override; + std::size_t write_some(std::span> buffers) override; + +private: + explicit UnixSocket(OwnedFileDescriptor owned_fd) noexcept; + + OwnedFileDescriptor file_descriptor; +}; + +class UnixSocketListener +{ +public: + UnixSocketListener() noexcept; + ~UnixSocketListener(); + + UnixSocketListener(const UnixSocketListener&) = delete; + UnixSocketListener& operator=(const UnixSocketListener&) = delete; + UnixSocketListener(UnixSocketListener&& other) noexcept; + UnixSocketListener& operator=(UnixSocketListener&& other) noexcept; + + [[nodiscard]] static UnixSocketListener bind(const std::filesystem::path& path, + int backlog = SOMAXCONN); + + [[nodiscard]] int native_handle() const noexcept; + [[nodiscard]] bool is_open() const noexcept; + void close() noexcept; + [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; + [[nodiscard]] UnixSocket accept(); + void unlink_path(); + +private: + UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; + + OwnedFileDescriptor file_descriptor; + std::filesystem::path bound_path; +}; + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/socket.cc b/udf-runner-cpp/v2/socket.cc new file mode 100644 index 0000000..4c923fb --- /dev/null +++ b/udf-runner-cpp/v2/socket.cc @@ -0,0 +1,57 @@ +#include + +#include +#include + +namespace exasol::udf::v2::socket +{ + +OwnedFileDescriptor::OwnedFileDescriptor() noexcept = default; + +OwnedFileDescriptor::~OwnedFileDescriptor() = default; + +OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int) +{ + throw std::logic_error("socket library is not implemented yet"); +} + +OwnedFileDescriptor::OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept = default; + +OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) noexcept = default; + +int OwnedFileDescriptor::native_handle() const noexcept +{ + return file_descriptor; +} + +bool OwnedFileDescriptor::is_open() const noexcept +{ + return false; +} + +void OwnedFileDescriptor::close() noexcept +{ +} + +int OwnedFileDescriptor::release_native_handle() noexcept +{ + return -1; +} + +OwnedFileDescriptor::OwnedFileDescriptor(const int owned_fd) noexcept : file_descriptor(owned_fd) +{ +} + +std::size_t Socket::read_some(const std::span buffer) +{ + const std::array buffers{buffer}; + return read_some(buffers); +} + +std::size_t Socket::write_some(const std::span buffer) +{ + const std::array buffers{buffer}; + return write_some(buffers); +} + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc new file mode 100644 index 0000000..4c9fa03 --- /dev/null +++ b/udf-runner-cpp/v2/unix_socket.cc @@ -0,0 +1,124 @@ +#include + +#include +#include + +namespace exasol::udf::v2::socket +{ + +namespace +{ + +[[noreturn]] void not_implemented() +{ + throw std::logic_error("Unix socket library is not implemented yet"); +} + +} // namespace + +UnixSocket::UnixSocket() noexcept = default; + +UnixSocket::~UnixSocket() = default; + +UnixSocket::UnixSocket(UnixSocket&& other) noexcept = default; + +UnixSocket& UnixSocket::operator=(UnixSocket&& other) noexcept = default; + +UnixSocket UnixSocket::connect(const std::filesystem::path&) +{ + not_implemented(); +} + +UnixSocket UnixSocket::adopt_native_handle(OwnedFileDescriptor) +{ + not_implemented(); +} + +int UnixSocket::native_handle() const noexcept +{ + return -1; +} + +bool UnixSocket::is_open() const noexcept +{ + return false; +} + +void UnixSocket::close() noexcept +{ +} + +OwnedFileDescriptor UnixSocket::release_native_handle() noexcept +{ + return {}; +} + +void UnixSocket::shutdown(Shutdown) +{ + not_implemented(); +} + +std::size_t UnixSocket::read_some(std::span>) +{ + not_implemented(); +} + +std::size_t UnixSocket::write_some(std::span>) +{ + not_implemented(); +} + +UnixSocket::UnixSocket(OwnedFileDescriptor owned_fd) noexcept : file_descriptor(std::move(owned_fd)) +{ +} + +UnixSocketListener::UnixSocketListener() noexcept = default; + +UnixSocketListener::~UnixSocketListener() = default; + +UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept = default; + +UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept = default; + +UnixSocketListener UnixSocketListener::bind(const std::filesystem::path&, const int) +{ + not_implemented(); +} + +int UnixSocketListener::native_handle() const noexcept +{ + return -1; +} + +bool UnixSocketListener::is_open() const noexcept +{ + return false; +} + +void UnixSocketListener::close() noexcept +{ +} + +OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept +{ + return {}; +} + +UnixSocket UnixSocketListener::accept() +{ + not_implemented(); +} + +void UnixSocketListener::unlink_path() +{ + not_implemented(); +} + +UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, + std::filesystem::path path) noexcept + : file_descriptor(std::move(owned_fd)), + bound_path(std::move(path)) +{ +} + +} // namespace exasol::udf::v2::socket From 5f0b5d18921057b08630cd79a5438df2aca81061 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 29 Sep 2026 15:38:13 +0530 Subject: [PATCH 02/32] #75: Implement socket descriptor ownership --- udf-runner-cpp/v2/socket.cc | 42 ++++++++++++++++++++++++++------ udf-runner-cpp/v2/unix_socket.cc | 24 +++++++++++++----- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/udf-runner-cpp/v2/socket.cc b/udf-runner-cpp/v2/socket.cc index 4c923fb..f2beeae 100644 --- a/udf-runner-cpp/v2/socket.cc +++ b/udf-runner-cpp/v2/socket.cc @@ -1,23 +1,44 @@ #include #include -#include +#include +#include +#include +#include namespace exasol::udf::v2::socket { OwnedFileDescriptor::OwnedFileDescriptor() noexcept = default; -OwnedFileDescriptor::~OwnedFileDescriptor() = default; +OwnedFileDescriptor::~OwnedFileDescriptor() +{ + close(); +} -OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int) +OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int owned_fd) { - throw std::logic_error("socket library is not implemented yet"); + if (owned_fd < 0) + { + throw std::system_error(EBADF, std::generic_category(), "adopt invalid file descriptor"); + } + return OwnedFileDescriptor(owned_fd); } -OwnedFileDescriptor::OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept = default; +OwnedFileDescriptor::OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept + : file_descriptor(std::exchange(other.file_descriptor, -1)) +{ +} -OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) noexcept = default; +OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) noexcept +{ + if (this != &other) + { + close(); + file_descriptor = std::exchange(other.file_descriptor, -1); + } + return *this; +} int OwnedFileDescriptor::native_handle() const noexcept { @@ -26,16 +47,21 @@ int OwnedFileDescriptor::native_handle() const noexcept bool OwnedFileDescriptor::is_open() const noexcept { - return false; + return file_descriptor != -1; } void OwnedFileDescriptor::close() noexcept { + const int closed_fd = std::exchange(file_descriptor, -1); + if (closed_fd != -1) + { + ::close(closed_fd); + } } int OwnedFileDescriptor::release_native_handle() noexcept { - return -1; + return std::exchange(file_descriptor, -1); } OwnedFileDescriptor::OwnedFileDescriptor(const int owned_fd) noexcept : file_descriptor(owned_fd) diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc index 4c9fa03..5292716 100644 --- a/udf-runner-cpp/v2/unix_socket.cc +++ b/udf-runner-cpp/v2/unix_socket.cc @@ -36,21 +36,27 @@ UnixSocket UnixSocket::adopt_native_handle(OwnedFileDescriptor) int UnixSocket::native_handle() const noexcept { - return -1; + return file_descriptor.native_handle(); } bool UnixSocket::is_open() const noexcept { - return false; + return file_descriptor.is_open(); } void UnixSocket::close() noexcept { + file_descriptor.close(); } OwnedFileDescriptor UnixSocket::release_native_handle() noexcept { - return {}; + const int released_fd = file_descriptor.release_native_handle(); + if (released_fd == -1) + { + return {}; + } + return OwnedFileDescriptor::adopt_native_handle(released_fd); } void UnixSocket::shutdown(Shutdown) @@ -87,21 +93,27 @@ UnixSocketListener UnixSocketListener::bind(const std::filesystem::path&, const int UnixSocketListener::native_handle() const noexcept { - return -1; + return file_descriptor.native_handle(); } bool UnixSocketListener::is_open() const noexcept { - return false; + return file_descriptor.is_open(); } void UnixSocketListener::close() noexcept { + file_descriptor.close(); } OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept { - return {}; + const int released_fd = file_descriptor.release_native_handle(); + if (released_fd == -1) + { + return {}; + } + return OwnedFileDescriptor::adopt_native_handle(released_fd); } UnixSocket UnixSocketListener::accept() From 871c26a729312696cc1a79ead518c874967e1ce1 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 29 Sep 2026 16:07:07 +0530 Subject: [PATCH 03/32] #75: Implement Unix socket connection and listener --- .../v2/include/exasol/udf/v2/unix_socket.hpp | 3 + udf-runner-cpp/v2/unix_socket.cc | 120 ++++++++++++++++-- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp index c888b21..cb38987 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp @@ -12,6 +12,9 @@ namespace exasol::udf::v2::socket class UnixSocket final : public Socket { public: + using Socket::read_some; + using Socket::write_some; + UnixSocket() noexcept; ~UnixSocket() override; diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc index 5292716..c1753b5 100644 --- a/udf-runner-cpp/v2/unix_socket.cc +++ b/udf-runner-cpp/v2/unix_socket.cc @@ -1,6 +1,14 @@ #include +#include +#include + +#include +#include +#include #include +#include +#include #include namespace exasol::udf::v2::socket @@ -14,6 +22,53 @@ namespace throw std::logic_error("Unix socket library is not implemented yet"); } +sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) +{ + const std::string path_string = path.string(); + if (path_string.empty() || path_string.find('\0') != std::string::npos || + path_string.size() >= sizeof(sockaddr_un::sun_path)) + { + throw std::system_error(ENAMETOOLONG, std::generic_category(), "invalid Unix socket path"); + } + + sockaddr_un address{}; + address.sun_family = AF_UNIX; + std::memcpy(address.sun_path, path_string.data(), path_string.size()); + length = static_cast(offsetof(sockaddr_un, sun_path) + path_string.size() + 1); + return address; +} + +[[nodiscard]] OwnedFileDescriptor create_socket() +{ + const int socket_fd = ::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (socket_fd == -1) + { + throw std::system_error(errno, std::generic_category(), "create Unix socket"); + } + return OwnedFileDescriptor::adopt_native_handle(socket_fd); +} + +void check_unix_stream_socket(const int file_descriptor) +{ + int domain = 0; + socklen_t domain_length = sizeof(domain); + if (::getsockopt(file_descriptor, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length) == -1) + { + throw std::system_error(errno, std::generic_category(), "inspect socket domain"); + } + + int type = 0; + socklen_t type_length = sizeof(type); + if (::getsockopt(file_descriptor, SOL_SOCKET, SO_TYPE, &type, &type_length) == -1) + { + throw std::system_error(errno, std::generic_category(), "inspect socket type"); + } + if (domain != AF_UNIX || type != SOCK_STREAM) + { + throw std::system_error(EPROTOTYPE, std::generic_category(), "expected Unix stream socket"); + } +} + } // namespace UnixSocket::UnixSocket() noexcept = default; @@ -24,14 +79,30 @@ UnixSocket::UnixSocket(UnixSocket&& other) noexcept = default; UnixSocket& UnixSocket::operator=(UnixSocket&& other) noexcept = default; -UnixSocket UnixSocket::connect(const std::filesystem::path&) +UnixSocket UnixSocket::connect(const std::filesystem::path& path) { - not_implemented(); + socklen_t address_length = 0; + const sockaddr_un address = make_socket_address(path, address_length); + OwnedFileDescriptor socket_fd = create_socket(); + while (::connect(socket_fd.native_handle(), reinterpret_cast(&address), address_length) == + -1) + { + if (errno != EINTR) + { + throw std::system_error(errno, std::generic_category(), "connect Unix socket"); + } + } + return UnixSocket(std::move(socket_fd)); } -UnixSocket UnixSocket::adopt_native_handle(OwnedFileDescriptor) +UnixSocket UnixSocket::adopt_native_handle(OwnedFileDescriptor owned_fd) { - not_implemented(); + if (!owned_fd.is_open()) + { + throw std::system_error(EBADF, std::generic_category(), "adopt closed socket"); + } + check_unix_stream_socket(owned_fd.native_handle()); + return UnixSocket(std::move(owned_fd)); } int UnixSocket::native_handle() const noexcept @@ -86,9 +157,20 @@ UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept = de UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept = default; -UnixSocketListener UnixSocketListener::bind(const std::filesystem::path&, const int) +UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, const int backlog) { - not_implemented(); + socklen_t address_length = 0; + const sockaddr_un address = make_socket_address(path, address_length); + OwnedFileDescriptor socket_fd = create_socket(); + if (::bind(socket_fd.native_handle(), reinterpret_cast(&address), address_length) == -1) + { + throw std::system_error(errno, std::generic_category(), "bind Unix socket listener"); + } + if (::listen(socket_fd.native_handle(), backlog) == -1) + { + throw std::system_error(errno, std::generic_category(), "listen on Unix socket"); + } + return UnixSocketListener(std::move(socket_fd), path); } int UnixSocketListener::native_handle() const noexcept @@ -118,12 +200,34 @@ OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept UnixSocket UnixSocketListener::accept() { - not_implemented(); + int accepted_fd = -1; + do + { + accepted_fd = ::accept4(file_descriptor.native_handle(), nullptr, nullptr, SOCK_CLOEXEC); + } while (accepted_fd == -1 && errno == EINTR); + + if (accepted_fd == -1) + { + throw std::system_error(errno, std::generic_category(), "accept Unix socket connection"); + } + return UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(accepted_fd)); } void UnixSocketListener::unlink_path() { - not_implemented(); + if (is_open()) + { + throw std::logic_error("close Unix socket listener before unlinking its path"); + } + if (bound_path.empty()) + { + return; + } + if (::unlink(bound_path.c_str()) == -1) + { + throw std::system_error(errno, std::generic_category(), "unlink Unix socket path"); + } + bound_path.clear(); } UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, From 263483046d6e538644bae8444a86bb099a568a35 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 29 Sep 2026 16:08:26 +0530 Subject: [PATCH 04/32] #75: Implement Unix socket stream operations --- udf-runner-cpp/v2/unix_socket.cc | 96 ++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc index c1753b5..0b733c4 100644 --- a/udf-runner-cpp/v2/unix_socket.cc +++ b/udf-runner-cpp/v2/unix_socket.cc @@ -1,15 +1,19 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include #include +#include namespace exasol::udf::v2::socket { @@ -17,11 +21,6 @@ namespace exasol::udf::v2::socket namespace { -[[noreturn]] void not_implemented() -{ - throw std::logic_error("Unix socket library is not implemented yet"); -} - sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) { const std::string path_string = path.string(); @@ -69,6 +68,43 @@ void check_unix_stream_socket(const int file_descriptor) } } +template +std::vector make_iovecs(const std::span buffers) +{ + if (buffers.size() > IOV_MAX) + { + throw std::system_error(EINVAL, std::generic_category(), "too many I/O buffers"); + } + + std::size_t total_size = 0; + std::vector iovecs; + iovecs.reserve(buffers.size()); + for (const Buffer buffer : buffers) + { + if (buffer.size() > static_cast(std::numeric_limits::max()) - total_size) + { + throw std::system_error(EINVAL, std::generic_category(), "I/O buffer size is too large"); + } + total_size += buffer.size(); + iovecs.push_back({const_cast(buffer.data()), buffer.size()}); + } + return iovecs; +} + +int shutdown_direction(const Shutdown how) +{ + switch (how) + { + case Shutdown::receive: + return SHUT_RD; + case Shutdown::send: + return SHUT_WR; + case Shutdown::both: + return SHUT_RDWR; + } + throw std::logic_error("invalid socket shutdown direction"); +} + } // namespace UnixSocket::UnixSocket() noexcept = default; @@ -130,19 +166,57 @@ OwnedFileDescriptor UnixSocket::release_native_handle() noexcept return OwnedFileDescriptor::adopt_native_handle(released_fd); } -void UnixSocket::shutdown(Shutdown) +void UnixSocket::shutdown(const Shutdown how) { - not_implemented(); + if (::shutdown(file_descriptor.native_handle(), shutdown_direction(how)) == -1) + { + throw std::system_error(errno, std::generic_category(), "shutdown Unix socket"); + } } -std::size_t UnixSocket::read_some(std::span>) +std::size_t UnixSocket::read_some(const std::span> buffers) { - not_implemented(); + std::vector iovecs = make_iovecs(buffers); + if (iovecs.empty()) + { + return 0; + } + + ssize_t result = -1; + do + { + result = ::readv(file_descriptor.native_handle(), iovecs.data(), static_cast(iovecs.size())); + } while (result == -1 && errno == EINTR); + + if (result == -1) + { + throw std::system_error(errno, std::generic_category(), "read from Unix socket"); + } + return static_cast(result); } -std::size_t UnixSocket::write_some(std::span>) +std::size_t UnixSocket::write_some(const std::span> buffers) { - not_implemented(); + std::vector iovecs = make_iovecs(buffers); + if (iovecs.empty()) + { + return 0; + } + + msghdr message{}; + message.msg_iov = iovecs.data(); + message.msg_iovlen = iovecs.size(); + ssize_t result = -1; + do + { + result = ::sendmsg(file_descriptor.native_handle(), &message, MSG_NOSIGNAL); + } while (result == -1 && errno == EINTR); + + if (result == -1) + { + throw std::system_error(errno, std::generic_category(), "write to Unix socket"); + } + return static_cast(result); } UnixSocket::UnixSocket(OwnedFileDescriptor owned_fd) noexcept : file_descriptor(std::move(owned_fd)) From 5648402051a250893c22889c3c159bc97b56cf38 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 29 Sep 2026 16:20:02 +0530 Subject: [PATCH 05/32] #75: Add Unix socket tests and fuzzing --- doc/changes/unreleased.md | 1 + udf-runner-cpp/v2/BUILD.bazel | 20 +++ .../v2/include/exasol/udf/v2/socket.hpp | 12 +- .../v2/include/exasol/udf/v2/unix_socket.hpp | 2 +- udf-runner-cpp/v2/socket_fuzz_test.cc | 37 +++++ udf-runner-cpp/v2/socket_test.cc | 125 +++++++++++++++ udf-runner-cpp/v2/unix_socket.cc | 147 +++++++++--------- 7 files changed, 266 insertions(+), 78 deletions(-) create mode 100644 udf-runner-cpp/v2/socket_fuzz_test.cc create mode 100644 udf-runner-cpp/v2/socket_test.cc diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index be433b9..66cde3b 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -11,6 +11,7 @@ n/a - #70: Added the v2 UDF protocol design and validation artifacts - #61: Added stacktrace-aware exceptions and assertions to v2 - #54: Added v2 socket-library interface design + - #75: Added the v2 Unix-domain stream socket library - #32: Added clang tidy to v2 - #36: Added developer guide for clang-tidy and clang-format - #38: Added Sonar Qube Public diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 2c1c1ad..8f7c6e1 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -370,6 +370,26 @@ cc_library( target_compatible_with = ["@platforms//os:linux"], ) +cc_test( + name = "socket_test", + srcs = ["socket_test.cc"], + copts = ["-std=c++23"], + deps = [ + ":socket", + "@googletest//:gtest_main", + ], + target_compatible_with = ["@platforms//os:linux"], +) + +cc_fuzz_test( + name = "socket_fuzz_test", + srcs = ["socket_fuzz_test.cc"], + copts = ["-std=c++23"], + deps = [":socket"], + tags = ["fuzz-test"], + target_compatible_with = ["@platforms//os:linux"], +) + cc_library( name = "linux_event_fd", srcs = ["linux_event_fd.cc"], diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp index 7304658..3e298f9 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp @@ -48,20 +48,20 @@ class Socket [[nodiscard]] virtual int native_handle() const noexcept = 0; [[nodiscard]] virtual bool is_open() const noexcept = 0; - virtual void close() noexcept = 0; + virtual void close() noexcept = 0; [[nodiscard]] virtual OwnedFileDescriptor release_native_handle() noexcept = 0; - virtual void shutdown(Shutdown how) = 0; + virtual void shutdown(Shutdown how) = 0; - virtual std::size_t read_some(std::span> buffers) = 0; + virtual std::size_t read_some(std::span> buffers) = 0; virtual std::size_t write_some(std::span> buffers) = 0; std::size_t read_some(std::span buffer); std::size_t write_some(std::span buffer); protected: - Socket() = default; - Socket(Socket&&) = default; - Socket& operator=(Socket&&) = default; + Socket() = default; + Socket(Socket&&) = default; + Socket& operator=(Socket&&) = default; }; } // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp index cb38987..1ea4cda 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp @@ -52,7 +52,7 @@ class UnixSocketListener UnixSocketListener& operator=(UnixSocketListener&& other) noexcept; [[nodiscard]] static UnixSocketListener bind(const std::filesystem::path& path, - int backlog = SOMAXCONN); + int backlog = SOMAXCONN); [[nodiscard]] int native_handle() const noexcept; [[nodiscard]] bool is_open() const noexcept; diff --git a/udf-runner-cpp/v2/socket_fuzz_test.cc b/udf-runner-cpp/v2/socket_fuzz_test.cc new file mode 100644 index 0000000..739b514 --- /dev/null +++ b/udf-runner-cpp/v2/socket_fuzz_test.cc @@ -0,0 +1,37 @@ +#include + +#include + +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_t size) +{ + if (size > 4096) + { + return 0; + } + std::array descriptors{}; + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, descriptors.data()) != 0) + { + return 0; + } + auto writer = exasol::udf::v2::socket::UnixSocket::adopt_native_handle( + exasol::udf::v2::socket::OwnedFileDescriptor::adopt_native_handle(descriptors[0])); + auto reader = exasol::udf::v2::socket::UnixSocket::adopt_native_handle( + exasol::udf::v2::socket::OwnedFileDescriptor::adopt_native_handle(descriptors[1])); + const std::span bytes(reinterpret_cast(data), size); + const std::array buffers{bytes.first(size / 2), bytes.subspan(size / 2)}; + if (writer.write_some(buffers) != size) + { + return 0; + } + std::array received{}; + if (reader.read_some(std::span(received).first(size)) != size) + { + __builtin_trap(); + } + return 0; +} diff --git a/udf-runner-cpp/v2/socket_test.cc b/udf-runner-cpp/v2/socket_test.cc new file mode 100644 index 0000000..69c088f --- /dev/null +++ b/udf-runner-cpp/v2/socket_test.cc @@ -0,0 +1,125 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +using exasol::udf::v2::socket::OwnedFileDescriptor; +using exasol::udf::v2::socket::UnixSocket; +using exasol::udf::v2::socket::UnixSocketListener; + +std::filesystem::path unique_socket_path() +{ + std::array directory_template{"/tmp/udf-runner-cpp-socket-XXXXXX"}; + char* directory = ::mkdtemp(directory_template.data()); + EXPECT_NE(directory, nullptr); + return std::filesystem::path(directory) / "socket"; +} + +void expect_system_error(const auto& function, const std::errc expected) +{ + try + { + function(); + ADD_FAILURE() << "expected std::system_error"; + } + catch (const std::system_error& error) + { + EXPECT_EQ(error.code(), std::make_error_code(expected)); + } +} + +TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) +{ + std::array pipe_fds{}; + ASSERT_EQ(::pipe(pipe_fds.data()), 0); + OwnedFileDescriptor descriptor = OwnedFileDescriptor::adopt_native_handle(pipe_fds[0]); + OwnedFileDescriptor moved(std::move(descriptor)); + EXPECT_FALSE(descriptor.is_open()); + const int released_fd = moved.release_native_handle(); + EXPECT_FALSE(moved.is_open()); + ASSERT_EQ(::close(released_fd), 0); + EXPECT_EQ(::close(released_fd), -1); + EXPECT_EQ(errno, EBADF); + ASSERT_EQ(::close(pipe_fds[1]), 0); +} + +TEST(SocketTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) +{ + const std::filesystem::path path = unique_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + ASSERT_TRUE(std::filesystem::exists(path)); + UnixSocket client = UnixSocket::connect(path); + UnixSocket server = listener.accept(); + + const std::array sent{std::byte{'o'}, std::byte{'k'}}; + ASSERT_EQ(client.write_some(sent), sent.size()); + std::array received{}; + ASSERT_EQ(server.read_some(received), received.size()); + EXPECT_EQ(received, sent); + + listener.close(); + EXPECT_TRUE(std::filesystem::exists(path)); + listener.unlink_path(); + EXPECT_FALSE(std::filesystem::exists(path)); + std::filesystem::remove(path.parent_path()); +} + +TEST(SocketTest, ListenerDoesNotOverwriteExistingPath) +{ + const std::filesystem::path path = unique_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + expect_system_error([&] { static_cast(UnixSocketListener::bind(path)); }, + std::errc::address_in_use); + listener.close(); + listener.unlink_path(); + std::filesystem::remove(path.parent_path()); +} + +TEST(SocketTest, SupportsScatterGatherIoAndPeerEof) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket writer = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + const std::array first{std::byte{'a'}, std::byte{'b'}}; + const std::array second{std::byte{'c'}, std::byte{'d'}}; + const std::array, 2> buffers{first, second}; + ASSERT_EQ(writer.write_some(buffers), 4); + std::array received{}; + ASSERT_EQ(reader.read_some(received), 4); + EXPECT_EQ(received, (std::array{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}, + std::byte{'d'}})); + writer.close(); + std::array eof_buffer{}; + EXPECT_EQ(reader.read_some(eof_buffer), 0); +} + +TEST(SocketTest, NonblockingReadReportsWouldBlock) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); + std::array buffer{}; + expect_system_error([&] { static_cast(reader.read_some(buffer)); }, + std::errc::resource_unavailable_try_again); + ASSERT_EQ(::close(sockets[1]), 0); +} + +} // namespace diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc index 0b733c4..adf8ac5 100644 --- a/udf-runner-cpp/v2/unix_socket.cc +++ b/udf-runner-cpp/v2/unix_socket.cc @@ -21,89 +21,93 @@ namespace exasol::udf::v2::socket namespace { -sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) -{ - const std::string path_string = path.string(); - if (path_string.empty() || path_string.find('\0') != std::string::npos || - path_string.size() >= sizeof(sockaddr_un::sun_path)) + sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) { - throw std::system_error(ENAMETOOLONG, std::generic_category(), "invalid Unix socket path"); - } - - sockaddr_un address{}; - address.sun_family = AF_UNIX; - std::memcpy(address.sun_path, path_string.data(), path_string.size()); - length = static_cast(offsetof(sockaddr_un, sun_path) + path_string.size() + 1); - return address; -} + const std::string path_string = path.string(); + if (path_string.empty() || path_string.find('\0') != std::string::npos || + path_string.size() >= sizeof(sockaddr_un::sun_path)) + { + throw std::system_error(ENAMETOOLONG, std::generic_category(), + "invalid Unix socket path"); + } -[[nodiscard]] OwnedFileDescriptor create_socket() -{ - const int socket_fd = ::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (socket_fd == -1) - { - throw std::system_error(errno, std::generic_category(), "create Unix socket"); + sockaddr_un address{}; + address.sun_family = AF_UNIX; + std::memcpy(address.sun_path, path_string.data(), path_string.size()); + length = static_cast(offsetof(sockaddr_un, sun_path) + path_string.size() + 1); + return address; } - return OwnedFileDescriptor::adopt_native_handle(socket_fd); -} -void check_unix_stream_socket(const int file_descriptor) -{ - int domain = 0; - socklen_t domain_length = sizeof(domain); - if (::getsockopt(file_descriptor, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length) == -1) + [[nodiscard]] OwnedFileDescriptor create_socket() { - throw std::system_error(errno, std::generic_category(), "inspect socket domain"); + const int socket_fd = ::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (socket_fd == -1) + { + throw std::system_error(errno, std::generic_category(), "create Unix socket"); + } + return OwnedFileDescriptor::adopt_native_handle(socket_fd); } - int type = 0; - socklen_t type_length = sizeof(type); - if (::getsockopt(file_descriptor, SOL_SOCKET, SO_TYPE, &type, &type_length) == -1) + void check_unix_stream_socket(const int file_descriptor) { - throw std::system_error(errno, std::generic_category(), "inspect socket type"); - } - if (domain != AF_UNIX || type != SOCK_STREAM) - { - throw std::system_error(EPROTOTYPE, std::generic_category(), "expected Unix stream socket"); - } -} + int domain = 0; + socklen_t domain_length = sizeof(domain); + if (::getsockopt(file_descriptor, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length) == -1) + { + throw std::system_error(errno, std::generic_category(), "inspect socket domain"); + } -template -std::vector make_iovecs(const std::span buffers) -{ - if (buffers.size() > IOV_MAX) - { - throw std::system_error(EINVAL, std::generic_category(), "too many I/O buffers"); + int type = 0; + socklen_t type_length = sizeof(type); + if (::getsockopt(file_descriptor, SOL_SOCKET, SO_TYPE, &type, &type_length) == -1) + { + throw std::system_error(errno, std::generic_category(), "inspect socket type"); + } + if (domain != AF_UNIX || type != SOCK_STREAM) + { + throw std::system_error(EPROTOTYPE, std::generic_category(), + "expected Unix stream socket"); + } } - std::size_t total_size = 0; - std::vector iovecs; - iovecs.reserve(buffers.size()); - for (const Buffer buffer : buffers) + template + std::vector make_iovecs(const std::span buffers) { - if (buffer.size() > static_cast(std::numeric_limits::max()) - total_size) + if (buffers.size() > IOV_MAX) { - throw std::system_error(EINVAL, std::generic_category(), "I/O buffer size is too large"); + throw std::system_error(EINVAL, std::generic_category(), "too many I/O buffers"); } - total_size += buffer.size(); - iovecs.push_back({const_cast(buffer.data()), buffer.size()}); + + std::size_t total_size = 0; + std::vector iovecs; + iovecs.reserve(buffers.size()); + for (const Buffer buffer : buffers) + { + if (buffer.size() > + static_cast(std::numeric_limits::max()) - total_size) + { + throw std::system_error(EINVAL, std::generic_category(), + "I/O buffer size is too large"); + } + total_size += buffer.size(); + iovecs.push_back({const_cast(buffer.data()), buffer.size()}); + } + return iovecs; } - return iovecs; -} -int shutdown_direction(const Shutdown how) -{ - switch (how) + int shutdown_direction(const Shutdown how) { - case Shutdown::receive: - return SHUT_RD; - case Shutdown::send: - return SHUT_WR; - case Shutdown::both: - return SHUT_RDWR; + switch (how) + { + case Shutdown::receive: + return SHUT_RD; + case Shutdown::send: + return SHUT_WR; + case Shutdown::both: + return SHUT_RDWR; + } + throw std::logic_error("invalid socket shutdown direction"); } - throw std::logic_error("invalid socket shutdown direction"); -} } // namespace @@ -120,8 +124,8 @@ UnixSocket UnixSocket::connect(const std::filesystem::path& path) socklen_t address_length = 0; const sockaddr_un address = make_socket_address(path, address_length); OwnedFileDescriptor socket_fd = create_socket(); - while (::connect(socket_fd.native_handle(), reinterpret_cast(&address), address_length) == - -1) + while (::connect(socket_fd.native_handle(), reinterpret_cast(&address), + address_length) == -1) { if (errno != EINTR) { @@ -185,7 +189,8 @@ std::size_t UnixSocket::read_some(const std::span> bu ssize_t result = -1; do { - result = ::readv(file_descriptor.native_handle(), iovecs.data(), static_cast(iovecs.size())); + result = ::readv(file_descriptor.native_handle(), iovecs.data(), + static_cast(iovecs.size())); } while (result == -1 && errno == EINTR); if (result == -1) @@ -236,7 +241,8 @@ UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, c socklen_t address_length = 0; const sockaddr_un address = make_socket_address(path, address_length); OwnedFileDescriptor socket_fd = create_socket(); - if (::bind(socket_fd.native_handle(), reinterpret_cast(&address), address_length) == -1) + if (::bind(socket_fd.native_handle(), reinterpret_cast(&address), + address_length) == -1) { throw std::system_error(errno, std::generic_category(), "bind Unix socket listener"); } @@ -306,8 +312,7 @@ void UnixSocketListener::unlink_path() UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept - : file_descriptor(std::move(owned_fd)), - bound_path(std::move(path)) + : file_descriptor(std::move(owned_fd)), bound_path(std::move(path)) { } From c3bdcd3a600e1139a08f14c978ff3ad4d97b4921 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 2 Oct 2026 12:20:27 +0530 Subject: [PATCH 06/32] #75: Group socket library files --- udf-runner-cpp/v2/BUILD.bazel | 12 ++++++------ .../v2/{ => include/exasol/udf/v2/socket}/socket.cc | 2 +- .../v2/include/exasol/udf/v2/{ => socket}/socket.hpp | 0 .../exasol/udf/v2/socket}/socket_fuzz_test.cc | 2 +- .../exasol/udf/v2/socket}/socket_test.cc | 2 +- .../exasol/udf/v2/socket}/unix_socket.cc | 2 +- .../exasol/udf/v2/{ => socket}/unix_socket.hpp | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) rename udf-runner-cpp/v2/{ => include/exasol/udf/v2/socket}/socket.cc (97%) rename udf-runner-cpp/v2/include/exasol/udf/v2/{ => socket}/socket.hpp (100%) rename udf-runner-cpp/v2/{ => include/exasol/udf/v2/socket}/socket_fuzz_test.cc (95%) rename udf-runner-cpp/v2/{ => include/exasol/udf/v2/socket}/socket_test.cc (98%) rename udf-runner-cpp/v2/{ => include/exasol/udf/v2/socket}/unix_socket.cc (99%) rename udf-runner-cpp/v2/include/exasol/udf/v2/{ => socket}/unix_socket.hpp (98%) diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 8f7c6e1..3f22f6f 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -358,12 +358,12 @@ cc_library( cc_library( name = "socket", srcs = [ - "socket.cc", - "unix_socket.cc", + "include/exasol/udf/v2/socket/socket.cc", + "include/exasol/udf/v2/socket/unix_socket.cc", ], hdrs = [ - "include/exasol/udf/v2/socket.hpp", - "include/exasol/udf/v2/unix_socket.hpp", + "include/exasol/udf/v2/socket/socket.hpp", + "include/exasol/udf/v2/socket/unix_socket.hpp", ], includes = ["include"], copts = ["-std=c++23"], @@ -372,7 +372,7 @@ cc_library( cc_test( name = "socket_test", - srcs = ["socket_test.cc"], + srcs = ["include/exasol/udf/v2/socket/socket_test.cc"], copts = ["-std=c++23"], deps = [ ":socket", @@ -383,7 +383,7 @@ cc_test( cc_fuzz_test( name = "socket_fuzz_test", - srcs = ["socket_fuzz_test.cc"], + srcs = ["include/exasol/udf/v2/socket/socket_fuzz_test.cc"], copts = ["-std=c++23"], deps = [":socket"], tags = ["fuzz-test"], diff --git a/udf-runner-cpp/v2/socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.cc similarity index 97% rename from udf-runner-cpp/v2/socket.cc rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.cc index f2beeae..731c95e 100644 --- a/udf-runner-cpp/v2/socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.cc @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp similarity index 100% rename from udf-runner-cpp/v2/include/exasol/udf/v2/socket.hpp rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp diff --git a/udf-runner-cpp/v2/socket_fuzz_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc similarity index 95% rename from udf-runner-cpp/v2/socket_fuzz_test.cc rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc index 739b514..a410d32 100644 --- a/udf-runner-cpp/v2/socket_fuzz_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/udf-runner-cpp/v2/socket_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc similarity index 98% rename from udf-runner-cpp/v2/socket_test.cc rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc index 69c088f..9cfc3e3 100644 --- a/udf-runner-cpp/v2/socket_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc similarity index 99% rename from udf-runner-cpp/v2/unix_socket.cc rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc index adf8ac5..32f5206 100644 --- a/udf-runner-cpp/v2/unix_socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp similarity index 98% rename from udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp rename to udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp index 1ea4cda..7782c95 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp @@ -4,7 +4,7 @@ #include -#include +#include namespace exasol::udf::v2::socket { From 6ce631458807199a00dac8b4954c7e163cbdec55 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 2 Oct 2026 12:26:05 +0530 Subject: [PATCH 07/32] #75: Fix socket clang-tidy findings --- doc/design/v2/runner/socket_library_design.md | 2 +- udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp | 6 +++--- .../v2/include/exasol/udf/v2/socket/socket_test.cc | 1 - .../v2/include/exasol/udf/v2/socket/unix_socket.cc | 8 ++++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/design/v2/runner/socket_library_design.md b/doc/design/v2/runner/socket_library_design.md index 32b621a..95e210e 100644 --- a/doc/design/v2/runner/socket_library_design.md +++ b/doc/design/v2/runner/socket_library_design.md @@ -62,7 +62,7 @@ This keeps the contract usable by TLS, whose I/O does not map one-for-one to ```cpp namespace exasol::udf::v2::socket { -enum class Shutdown { receive, send, both }; +enum class Shutdown { Receive, Send, Both }; class OwnedFileDescriptor { public: diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp index 3e298f9..4809aaa 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp @@ -8,9 +8,9 @@ namespace exasol::udf::v2::socket enum class Shutdown { - receive, - send, - both, + Receive, + Send, + Both, }; class OwnedFileDescriptor diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc index 9cfc3e3..80d44e1 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc @@ -47,7 +47,6 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::pipe(pipe_fds.data()), 0); OwnedFileDescriptor descriptor = OwnedFileDescriptor::adopt_native_handle(pipe_fds[0]); OwnedFileDescriptor moved(std::move(descriptor)); - EXPECT_FALSE(descriptor.is_open()); const int released_fd = moved.release_native_handle(); EXPECT_FALSE(moved.is_open()); ASSERT_EQ(::close(released_fd), 0); diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc index 32f5206..88aea2f 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -99,11 +99,11 @@ namespace { switch (how) { - case Shutdown::receive: + case Shutdown::Receive: return SHUT_RD; - case Shutdown::send: + case Shutdown::Send: return SHUT_WR; - case Shutdown::both: + case Shutdown::Both: return SHUT_RDWR; } throw std::logic_error("invalid socket shutdown direction"); From f4ee0d7624c11ae7d3dadf50c85613ca481bbbe0 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 2 Oct 2026 12:30:03 +0530 Subject: [PATCH 08/32] #75: Extend socket mutation coverage --- .../exasol/udf/v2/socket/socket_test.cc | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc index 80d44e1..6493667 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc @@ -17,6 +17,7 @@ namespace { using exasol::udf::v2::socket::OwnedFileDescriptor; +using exasol::udf::v2::socket::Shutdown; using exasol::udf::v2::socket::UnixSocket; using exasol::udf::v2::socket::UnixSocketListener; @@ -55,6 +56,28 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::close(pipe_fds[1]), 0); } +TEST(SocketTest, RejectsInvalidDescriptorAdoption) +{ + expect_system_error([] { static_cast(OwnedFileDescriptor::adopt_native_handle(-1)); }, + std::errc::bad_file_descriptor); +} + +TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) +{ + std::array first_pipe{}; + std::array second_pipe{}; + ASSERT_EQ(::pipe(first_pipe.data()), 0); + ASSERT_EQ(::pipe(second_pipe.data()), 0); + OwnedFileDescriptor source = OwnedFileDescriptor::adopt_native_handle(first_pipe[0]); + OwnedFileDescriptor destination = OwnedFileDescriptor::adopt_native_handle(second_pipe[0]); + destination = std::move(source); + EXPECT_EQ(::close(second_pipe[0]), -1); + EXPECT_EQ(errno, EBADF); + ASSERT_EQ(::close(destination.release_native_handle()), 0); + ASSERT_EQ(::close(first_pipe[1]), 0); + ASSERT_EQ(::close(second_pipe[1]), 0); +} + TEST(SocketTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) { const std::filesystem::path path = unique_socket_path(); @@ -87,6 +110,18 @@ TEST(SocketTest, ListenerDoesNotOverwriteExistingPath) std::filesystem::remove(path.parent_path()); } +TEST(SocketTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) +{ + expect_system_error([] { static_cast(UnixSocketListener::bind({})); }, + std::errc::filename_too_long); + const std::filesystem::path path = unique_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + EXPECT_THROW(listener.unlink_path(), std::logic_error); + listener.close(); + listener.unlink_path(); + std::filesystem::remove(path.parent_path()); +} + TEST(SocketTest, SupportsScatterGatherIoAndPeerEof) { std::array sockets{}; @@ -121,4 +156,20 @@ TEST(SocketTest, NonblockingReadReportsWouldBlock) ASSERT_EQ(::close(sockets[1]), 0); } +TEST(SocketTest, SupportsShutdownAndDescriptorRelease) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket writer = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + writer.shutdown(Shutdown::Send); + std::array buffer{}; + EXPECT_EQ(reader.read_some(buffer), 0); + OwnedFileDescriptor released = reader.release_native_handle(); + EXPECT_FALSE(reader.is_open()); + EXPECT_TRUE(released.is_open()); +} + } // namespace From c24a3cc8e48e3ba4ee2189a43418b5e5fad2a37a Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Sat, 3 Oct 2026 14:04:31 +0530 Subject: [PATCH 09/32] #75: Rename the development Lima template --- AGENTS.md | 8 ++++---- doc/developer_guide/slc.md | 4 ++-- doc/developer_guide/v2/v2_code_quality.md | 2 +- .../{docker-udf-client.yaml => udf-runner-cpp-dev.yaml} | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename ext/lima_vm_templates/{docker-udf-client.yaml => udf-runner-cpp-dev.yaml} (94%) diff --git a/AGENTS.md b/AGENTS.md index cb77e1c..a65aa35 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,12 +81,12 @@ unreleased changelog, so update it before the PR is merged. ## Tool availability and Lima - When a required development tool is unavailable on the host, use the - repository's `docker-udf-client` Lima VM instead of installing a host-wide + repository's `udf-runner-cpp-dev` Lima VM instead of installing a host-wide package. - Start the VM with - `limactl start ./ext/lima_vm_templates/docker-udf-client.yaml` and run - commands in it with `limactl shell docker-udf-client `. + `limactl start ./ext/lima_vm_templates/udf-runner-cpp-dev.yaml` and run + commands in it with `limactl shell udf-runner-cpp-dev `. - If a required tool is missing in the VM, install it there and add its package to the provisioning list in - [`ext/lima_vm_templates/docker-udf-client.yaml`](ext/lima_vm_templates/docker-udf-client.yaml) + [`ext/lima_vm_templates/udf-runner-cpp-dev.yaml`](ext/lima_vm_templates/udf-runner-cpp-dev.yaml) so future VMs provide it automatically. diff --git a/doc/developer_guide/slc.md b/doc/developer_guide/slc.md index 096f1ab..587c199 100644 --- a/doc/developer_guide/slc.md +++ b/doc/developer_guide/slc.md @@ -14,8 +14,8 @@ If no Docker daemon is available on the host, use the repository’s Lima instance: ```bash -limactl start ./ext/lima_vm_templates/docker-udf-client.yaml -export DOCKER_HOST="$(limactl list docker-udf-client \ +limactl start ./ext/lima_vm_templates/udf-runner-cpp-dev.yaml +export DOCKER_HOST="$(limactl list udf-runner-cpp-dev \ --format 'unix://{{.Dir}}/sock/docker.sock')" ``` diff --git a/doc/developer_guide/v2/v2_code_quality.md b/doc/developer_guide/v2/v2_code_quality.md index 4daf696..523704d 100644 --- a/doc/developer_guide/v2/v2_code_quality.md +++ b/doc/developer_guide/v2/v2_code_quality.md @@ -86,7 +86,7 @@ alias( Mutation testing for the functional v2 C++ tests uses [Mull](https://mull-project.com/) with the pinned Mull 0.34.1 release and matching LLVM 22 toolchain. The -`docker-udf-client` Lima template provisions these tools automatically; on +`udf-runner-cpp-dev` Lima template provisions these tools automatically; on other environments install the LLVM 22 compiler and `mull-22`, then verify that `mull-runner-22` and `/usr/lib/mull-ir-frontend-22` are available. diff --git a/ext/lima_vm_templates/docker-udf-client.yaml b/ext/lima_vm_templates/udf-runner-cpp-dev.yaml similarity index 94% rename from ext/lima_vm_templates/docker-udf-client.yaml rename to ext/lima_vm_templates/udf-runner-cpp-dev.yaml index 62e744b..3d448fb 100644 --- a/ext/lima_vm_templates/docker-udf-client.yaml +++ b/ext/lima_vm_templates/udf-runner-cpp-dev.yaml @@ -3,12 +3,12 @@ # Also, it sets all environment variables needed to build the UDF client for Python and Java # TODO: Add R dependencies # Start this VM when the host has no Docker daemon: -# $ limactl start ./ext/lima_vm_templates/docker-udf-client.yaml +# $ limactl start ./ext/lima_vm_templates/udf-runner-cpp-dev.yaml # # Connect Docker and exaslct to the Lima Docker daemon: -# $ export DOCKER_HOST=$(limactl list docker-udf-client --format 'unix://{{.Dir}}/sock/docker.sock') +# $ export DOCKER_HOST=$(limactl list udf-runner-cpp-dev --format 'unix://{{.Dir}}/sock/docker.sock') # cd ./exaudfclient -# $ limactl shell docker-udf-client bazel build --config no-tty --config optimize --config python --config fast-binary +# $ limactl shell udf-runner-cpp-dev bazel build --config no-tty --config optimize --config python --config fast-binary # To build the SLC Docker images/stages only, run # $ exaslct build --flavor-path ./flavors/ From 2590e45aeb11db9d16e756f397529047cd80eda9 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 13:03:30 +0530 Subject: [PATCH 10/32] #75: Exclude socket tests from Mull --- udf-runner-cpp/v2/BUILD.bazel | 3 +++ 1 file changed, 3 insertions(+) diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 3f22f6f..3c979e1 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -374,6 +374,9 @@ cc_test( name = "socket_test", srcs = ["include/exasol/udf/v2/socket/socket_test.cc"], copts = ["-std=c++23"], + # Socket behavior remains covered by the normal Bazel test suite. Exclude + # this target from Mull until its mutation coverage is expanded. + tags = ["no-mull"], deps = [ ":socket", "@googletest//:gtest_main", From cc4654e2d1a80b4be38c4d3d49e3b2314ef5c9f6 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 16:45:57 +0530 Subject: [PATCH 11/32] #75: Address socket Sonar findings --- .../exasol/udf/v2/socket/socket_fuzz_test.cc | 8 +- .../exasol/udf/v2/socket/socket_test.cc | 16 +++- .../exasol/udf/v2/socket/unix_socket.cc | 73 +++++++++++++------ .../exasol/udf/v2/socket/unix_socket.hpp | 2 +- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc index a410d32..e17f5c2 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc @@ -23,13 +23,13 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_ auto reader = exasol::udf::v2::socket::UnixSocket::adopt_native_handle( exasol::udf::v2::socket::OwnedFileDescriptor::adopt_native_handle(descriptors[1])); const std::span bytes(reinterpret_cast(data), size); - const std::array buffers{bytes.first(size / 2), bytes.subspan(size / 2)}; - if (writer.write_some(buffers) != size) + if (const std::array buffers{bytes.first(size / 2), bytes.subspan(size / 2)}; + writer.write_some(buffers) != size) { return 0; } - std::array received{}; - if (reader.read_some(std::span(received).first(size)) != size) + if (std::array received{}; + reader.read_some(std::span(received).first(size)) != size) { __builtin_trap(); } diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc index 6493667..afa4d3d 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -23,9 +24,16 @@ using exasol::udf::v2::socket::UnixSocketListener; std::filesystem::path unique_socket_path() { - std::array directory_template{"/tmp/udf-runner-cpp-socket-XXXXXX"}; + const std::string template_path = + (std::filesystem::temp_directory_path() / "udf-runner-cpp-socket-XXXXXX").string(); + std::vector directory_template(template_path.begin(), template_path.end()); + directory_template.push_back('\0'); char* directory = ::mkdtemp(directory_template.data()); - EXPECT_NE(directory, nullptr); + if (directory == nullptr) + { + ADD_FAILURE() << "mkdtemp failed"; + return {}; + } return std::filesystem::path(directory) / "socket"; } @@ -103,7 +111,7 @@ TEST(SocketTest, ListenerDoesNotOverwriteExistingPath) { const std::filesystem::path path = unique_socket_path(); UnixSocketListener listener = UnixSocketListener::bind(path); - expect_system_error([&] { static_cast(UnixSocketListener::bind(path)); }, + expect_system_error([path] { static_cast(UnixSocketListener::bind(path)); }, std::errc::address_in_use); listener.close(); listener.unlink_path(); @@ -151,7 +159,7 @@ TEST(SocketTest, NonblockingReadReportsWouldBlock) UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); std::array buffer{}; - expect_system_error([&] { static_cast(reader.read_some(buffer)); }, + expect_system_error([&reader, &buffer] { static_cast(reader.read_some(buffer)); }, std::errc::resource_unavailable_try_again); ASSERT_EQ(::close(sockets[1]), 0); } diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc index 88aea2f..fcc63ef 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc @@ -4,11 +4,13 @@ #include #include +#include #include #include #include -#include #include +#include +#include #include #include #include @@ -21,10 +23,22 @@ namespace exasol::udf::v2::socket namespace { + class InvalidShutdownDirection final : public std::logic_error + { + public: + using std::logic_error::logic_error; + }; + + class ListenerStillOpen final : public std::logic_error + { + public: + using std::logic_error::logic_error; + }; + sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) { const std::string path_string = path.string(); - if (path_string.empty() || path_string.find('\0') != std::string::npos || + if (path_string.empty() || path_string.contains('\0') || path_string.size() >= sizeof(sockaddr_un::sun_path)) { throw std::system_error(ENAMETOOLONG, std::generic_category(), @@ -50,16 +64,16 @@ namespace void check_unix_stream_socket(const int file_descriptor) { - int domain = 0; - socklen_t domain_length = sizeof(domain); - if (::getsockopt(file_descriptor, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length) == -1) + int domain = 0; + if (socklen_t domain_length = sizeof(domain); + ::getsockopt(file_descriptor, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length) == -1) { throw std::system_error(errno, std::generic_category(), "inspect socket domain"); } - int type = 0; - socklen_t type_length = sizeof(type); - if (::getsockopt(file_descriptor, SOL_SOCKET, SO_TYPE, &type, &type_length) == -1) + int type = 0; + if (socklen_t type_length = sizeof(type); + ::getsockopt(file_descriptor, SOL_SOCKET, SO_TYPE, &type, &type_length) == -1) { throw std::system_error(errno, std::generic_category(), "inspect socket type"); } @@ -83,37 +97,48 @@ namespace iovecs.reserve(buffers.size()); for (const Buffer buffer : buffers) { - if (buffer.size() > + if (std::size(buffer) > static_cast(std::numeric_limits::max()) - total_size) { throw std::system_error(EINVAL, std::generic_category(), "I/O buffer size is too large"); } - total_size += buffer.size(); - iovecs.push_back({const_cast(buffer.data()), buffer.size()}); + total_size += std::size(buffer); + // POSIX declares iovec::iov_base as void* even for sendmsg(), which does not mutate it. + iovecs.push_back( // NOSONAR: Required by the POSIX sendmsg() interface. + {const_cast(std::data(buffer)), std::size(buffer)}); } return iovecs; } int shutdown_direction(const Shutdown how) { + using enum Shutdown; switch (how) { - case Shutdown::Receive: + case Receive: return SHUT_RD; - case Shutdown::Send: + case Send: return SHUT_WR; - case Shutdown::Both: + case Both: return SHUT_RDWR; } - throw std::logic_error("invalid socket shutdown direction"); + throw InvalidShutdownDirection("invalid socket shutdown direction"); + } + + [[nodiscard]] const sockaddr* as_socket_address(const sockaddr_un& address) noexcept + { + return std::bit_cast(&address); } } // namespace UnixSocket::UnixSocket() noexcept = default; -UnixSocket::~UnixSocket() = default; +UnixSocket::~UnixSocket() +{ + close(); +} UnixSocket::UnixSocket(UnixSocket&& other) noexcept = default; @@ -124,8 +149,7 @@ UnixSocket UnixSocket::connect(const std::filesystem::path& path) socklen_t address_length = 0; const sockaddr_un address = make_socket_address(path, address_length); OwnedFileDescriptor socket_fd = create_socket(); - while (::connect(socket_fd.native_handle(), reinterpret_cast(&address), - address_length) == -1) + while (::connect(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) { if (errno != EINTR) { @@ -230,7 +254,10 @@ UnixSocket::UnixSocket(OwnedFileDescriptor owned_fd) noexcept : file_descriptor( UnixSocketListener::UnixSocketListener() noexcept = default; -UnixSocketListener::~UnixSocketListener() = default; +UnixSocketListener::~UnixSocketListener() +{ + close(); +} UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept = default; @@ -241,8 +268,7 @@ UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, c socklen_t address_length = 0; const sockaddr_un address = make_socket_address(path, address_length); OwnedFileDescriptor socket_fd = create_socket(); - if (::bind(socket_fd.native_handle(), reinterpret_cast(&address), - address_length) == -1) + if (::bind(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) { throw std::system_error(errno, std::generic_category(), "bind Unix socket listener"); } @@ -293,11 +319,11 @@ UnixSocket UnixSocketListener::accept() return UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(accepted_fd)); } -void UnixSocketListener::unlink_path() +void UnixSocketListener::unlink_path() const { if (is_open()) { - throw std::logic_error("close Unix socket listener before unlinking its path"); + throw ListenerStillOpen("close Unix socket listener before unlinking its path"); } if (bound_path.empty()) { @@ -307,7 +333,6 @@ void UnixSocketListener::unlink_path() { throw std::system_error(errno, std::generic_category(), "unlink Unix socket path"); } - bound_path.clear(); } UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp index 7782c95..85fcd74 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp @@ -59,7 +59,7 @@ class UnixSocketListener void close() noexcept; [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; [[nodiscard]] UnixSocket accept(); - void unlink_path(); + void unlink_path() const; private: UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; From 4873bc7dcd6b236aab740177b470de0a3058ef63 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 17:32:31 +0530 Subject: [PATCH 12/32] #75: Format socket Sonar suppression --- udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc index fcc63ef..5522fa9 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc @@ -105,8 +105,8 @@ namespace } total_size += std::size(buffer); // POSIX declares iovec::iov_base as void* even for sendmsg(), which does not mutate it. - iovecs.push_back( // NOSONAR: Required by the POSIX sendmsg() interface. - {const_cast(std::data(buffer)), std::size(buffer)}); + iovecs.push_back( + {const_cast(std::data(buffer)), std::size(buffer)}); // NOSONAR } return iovecs; } From 25b581cc751e0c090f13705004236fc3449a5c83 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 19:36:17 +0530 Subject: [PATCH 13/32] #75: Resolve remaining socket Sonar findings --- udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc | 3 ++- udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc | 2 +- udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc index afa4d3d..d5e64d5 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc @@ -25,7 +25,8 @@ using exasol::udf::v2::socket::UnixSocketListener; std::filesystem::path unique_socket_path() { const std::string template_path = - (std::filesystem::temp_directory_path() / "udf-runner-cpp-socket-XXXXXX").string(); + (std::filesystem::temp_directory_path() / "udf-runner-cpp-socket-XXXXXX") + .string(); // NOSONAR S5443: mkdtemp atomically creates a private directory. std::vector directory_template(template_path.begin(), template_path.end()); directory_template.push_back('\0'); char* directory = ::mkdtemp(directory_template.data()); diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc index 5522fa9..00b7c46 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc @@ -304,7 +304,7 @@ OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept return OwnedFileDescriptor::adopt_native_handle(released_fd); } -UnixSocket UnixSocketListener::accept() +UnixSocket UnixSocketListener::accept() const { int accepted_fd = -1; do diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp index 85fcd74..9ba8c71 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp @@ -58,7 +58,7 @@ class UnixSocketListener [[nodiscard]] bool is_open() const noexcept; void close() noexcept; [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; - [[nodiscard]] UnixSocket accept(); + [[nodiscard]] UnixSocket accept() const; void unlink_path() const; private: From f4f46c800dc607289edfbb5871a3ff3d958ff3f9 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 19:57:08 +0530 Subject: [PATCH 14/32] #75: Move socket sources out of public headers --- udf-runner-cpp/v2/BUILD.bazel | 8 ++++---- .../v2/{include/exasol/udf/v2/socket => }/socket.cc | 0 .../exasol/udf/v2/socket => }/socket_fuzz_test.cc | 0 .../v2/{include/exasol/udf/v2/socket => }/socket_test.cc | 0 .../v2/{include/exasol/udf/v2/socket => }/unix_socket.cc | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename udf-runner-cpp/v2/{include/exasol/udf/v2/socket => }/socket.cc (100%) rename udf-runner-cpp/v2/{include/exasol/udf/v2/socket => }/socket_fuzz_test.cc (100%) rename udf-runner-cpp/v2/{include/exasol/udf/v2/socket => }/socket_test.cc (100%) rename udf-runner-cpp/v2/{include/exasol/udf/v2/socket => }/unix_socket.cc (100%) diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 3c979e1..5d1e072 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -358,8 +358,8 @@ cc_library( cc_library( name = "socket", srcs = [ - "include/exasol/udf/v2/socket/socket.cc", - "include/exasol/udf/v2/socket/unix_socket.cc", + "socket.cc", + "unix_socket.cc", ], hdrs = [ "include/exasol/udf/v2/socket/socket.hpp", @@ -372,7 +372,7 @@ cc_library( cc_test( name = "socket_test", - srcs = ["include/exasol/udf/v2/socket/socket_test.cc"], + srcs = ["socket_test.cc"], copts = ["-std=c++23"], # Socket behavior remains covered by the normal Bazel test suite. Exclude # this target from Mull until its mutation coverage is expanded. @@ -386,7 +386,7 @@ cc_test( cc_fuzz_test( name = "socket_fuzz_test", - srcs = ["include/exasol/udf/v2/socket/socket_fuzz_test.cc"], + srcs = ["socket_fuzz_test.cc"], copts = ["-std=c++23"], deps = [":socket"], tags = ["fuzz-test"], diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.cc b/udf-runner-cpp/v2/socket.cc similarity index 100% rename from udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.cc rename to udf-runner-cpp/v2/socket.cc diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc b/udf-runner-cpp/v2/socket_fuzz_test.cc similarity index 100% rename from udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_fuzz_test.cc rename to udf-runner-cpp/v2/socket_fuzz_test.cc diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket_test.cc similarity index 100% rename from udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket_test.cc rename to udf-runner-cpp/v2/socket_test.cc diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/unix_socket.cc similarity index 100% rename from udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.cc rename to udf-runner-cpp/v2/unix_socket.cc From faa6427c30afd5ee5b9b16165fce320b47380641 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 5 Oct 2026 20:02:43 +0530 Subject: [PATCH 15/32] #75: Group socket sources in a module folder --- udf-runner-cpp/v2/BUILD.bazel | 8 ++++---- udf-runner-cpp/v2/{ => socket}/socket.cc | 0 udf-runner-cpp/v2/{ => socket}/socket_fuzz_test.cc | 0 udf-runner-cpp/v2/{ => socket}/socket_test.cc | 0 udf-runner-cpp/v2/{ => socket}/unix_socket.cc | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename udf-runner-cpp/v2/{ => socket}/socket.cc (100%) rename udf-runner-cpp/v2/{ => socket}/socket_fuzz_test.cc (100%) rename udf-runner-cpp/v2/{ => socket}/socket_test.cc (100%) rename udf-runner-cpp/v2/{ => socket}/unix_socket.cc (100%) diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 5d1e072..3a97aab 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -358,8 +358,8 @@ cc_library( cc_library( name = "socket", srcs = [ - "socket.cc", - "unix_socket.cc", + "socket/socket.cc", + "socket/unix_socket.cc", ], hdrs = [ "include/exasol/udf/v2/socket/socket.hpp", @@ -372,7 +372,7 @@ cc_library( cc_test( name = "socket_test", - srcs = ["socket_test.cc"], + srcs = ["socket/socket_test.cc"], copts = ["-std=c++23"], # Socket behavior remains covered by the normal Bazel test suite. Exclude # this target from Mull until its mutation coverage is expanded. @@ -386,7 +386,7 @@ cc_test( cc_fuzz_test( name = "socket_fuzz_test", - srcs = ["socket_fuzz_test.cc"], + srcs = ["socket/socket_fuzz_test.cc"], copts = ["-std=c++23"], deps = [":socket"], tags = ["fuzz-test"], diff --git a/udf-runner-cpp/v2/socket.cc b/udf-runner-cpp/v2/socket/socket.cc similarity index 100% rename from udf-runner-cpp/v2/socket.cc rename to udf-runner-cpp/v2/socket/socket.cc diff --git a/udf-runner-cpp/v2/socket_fuzz_test.cc b/udf-runner-cpp/v2/socket/socket_fuzz_test.cc similarity index 100% rename from udf-runner-cpp/v2/socket_fuzz_test.cc rename to udf-runner-cpp/v2/socket/socket_fuzz_test.cc diff --git a/udf-runner-cpp/v2/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc similarity index 100% rename from udf-runner-cpp/v2/socket_test.cc rename to udf-runner-cpp/v2/socket/socket_test.cc diff --git a/udf-runner-cpp/v2/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc similarity index 100% rename from udf-runner-cpp/v2/unix_socket.cc rename to udf-runner-cpp/v2/socket/unix_socket.cc From 789c3655a9e5b13938c3845f40aea639e1349a03 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 7 Oct 2026 11:23:59 +0530 Subject: [PATCH 16/32] #75: Use short secure socket test paths --- udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes | Bin 0 -> 12 bytes udf-runner-cpp/v2/fuzz/corpus/socket/empty | 0 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k | 1 + udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k | 1 + udf-runner-cpp/v2/fuzz/corpus/socket/one_byte | 2 ++ udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii | 1 + udf-runner-cpp/v2/socket/socket_test.cc | 14 ++++++++++++-- 7 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/empty create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/one_byte create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes b/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes new file mode 100644 index 0000000000000000000000000000000000000000..5fe55790862aacc6a4ce483e780232a36e711954 GIT binary patch literal 12 ScmZQzU|=|VrpLYlNCN;8YXaQ> literal 0 HcmV?d00001 diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/empty b/udf-runner-cpp/v2/fuzz/corpus/socket/empty new file mode 100644 index 0000000..e69de29 diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k new file mode 100644 index 0000000..9d235ed --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k new file mode 100644 index 0000000..7c1c939 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte b/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte new file mode 100644 index 0000000..4d92977 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte @@ -0,0 +1,2 @@ +e + diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii b/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii new file mode 100644 index 0000000..842b729 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii @@ -0,0 +1 @@ +this is for testing ASCII only content diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc index d5e64d5..c5072d0 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/socket_test.cc @@ -6,6 +6,9 @@ #include #include +#include +#include +#include #include #include #include @@ -24,9 +27,16 @@ using exasol::udf::v2::socket::UnixSocketListener; std::filesystem::path unique_socket_path() { + constexpr std::size_t maximum_temporary_root_length = 64; + const char* temporary_root = P_tmpdir; + if (const char* configured_temporary_root = std::getenv("TMPDIR"); + configured_temporary_root != nullptr && + std::strlen(configured_temporary_root) <= maximum_temporary_root_length) + { + temporary_root = configured_temporary_root; + } const std::string template_path = - (std::filesystem::temp_directory_path() / "udf-runner-cpp-socket-XXXXXX") - .string(); // NOSONAR S5443: mkdtemp atomically creates a private directory. + (std::filesystem::path(temporary_root) / "udf-runner-cpp-socket-XXXXXX").string(); std::vector directory_template(template_path.begin(), template_path.end()); directory_template.push_back('\0'); char* directory = ::mkdtemp(directory_template.data()); From 9496da502ee238e947e35f3282fa5387aafb17a5 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 7 Oct 2026 11:25:02 +0530 Subject: [PATCH 17/32] #75: Keep socket corpus changes untracked --- udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes | Bin 12 -> 0 bytes udf-runner-cpp/v2/fuzz/corpus/socket/empty | 0 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k | 1 - udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k | 1 - udf-runner-cpp/v2/fuzz/corpus/socket/one_byte | 2 -- udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii | 1 - 6 files changed, 5 deletions(-) delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/empty delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/one_byte delete mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes b/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes deleted file mode 100644 index 5fe55790862aacc6a4ce483e780232a36e711954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12 ScmZQzU|=|VrpLYlNCN;8YXaQ> diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/empty b/udf-runner-cpp/v2/fuzz/corpus/socket/empty deleted file mode 100644 index e69de29..0000000 diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k deleted file mode 100644 index 9d235ed..0000000 --- a/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k +++ /dev/null @@ -1 +0,0 @@ -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k deleted file mode 100644 index 7c1c939..0000000 --- a/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k +++ /dev/null @@ -1 +0,0 @@ -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte b/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte deleted file mode 100644 index 4d92977..0000000 --- a/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte +++ /dev/null @@ -1,2 +0,0 @@ -e - diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii b/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii deleted file mode 100644 index 842b729..0000000 --- a/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii +++ /dev/null @@ -1 +0,0 @@ -this is for testing ASCII only content From 706a115cc6c0dc7e0d5f6dad0017278c943a3e2f Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 7 Oct 2026 11:41:00 +0530 Subject: [PATCH 18/32] add more corpus files for test --- udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes | Bin 0 -> 12 bytes udf-runner-cpp/v2/fuzz/corpus/socket/empty | 0 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k | 1 + udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k | 1 + udf-runner-cpp/v2/fuzz/corpus/socket/one_byte | 2 ++ udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii | 1 + 6 files changed, 5 insertions(+) create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/empty create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/one_byte create mode 100644 udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes b/udf-runner-cpp/v2/fuzz/corpus/socket/binary_bytes new file mode 100644 index 0000000000000000000000000000000000000000..5fe55790862aacc6a4ce483e780232a36e711954 GIT binary patch literal 12 ScmZQzU|=|VrpLYlNCN;8YXaQ> literal 0 HcmV?d00001 diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/empty b/udf-runner-cpp/v2/fuzz/corpus/socket/empty new file mode 100644 index 0000000..e69de29 diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k new file mode 100644 index 0000000..9d235ed --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/exact_4k @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k b/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k new file mode 100644 index 0000000..7c1c939 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/more_than_4k @@ -0,0 +1 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte b/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte new file mode 100644 index 0000000..4d92977 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/one_byte @@ -0,0 +1,2 @@ +e + diff --git a/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii b/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii new file mode 100644 index 0000000..842b729 --- /dev/null +++ b/udf-runner-cpp/v2/fuzz/corpus/socket/only_ascii @@ -0,0 +1 @@ +this is for testing ASCII only content From ed77d369d95463fa9bf585040f2d6119a19d47ff Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Thu, 8 Oct 2026 13:57:12 +0530 Subject: [PATCH 19/32] #75: Split socket classes into public headers --- doc/design/v2/runner/socket_library_design.md | 6 ++- udf-runner-cpp/v2/BUILD.bazel | 2 + .../udf/v2/socket/owned_file_descriptor.hpp | 30 ++++++++++++++ .../include/exasol/udf/v2/socket/socket.hpp | 26 +----------- .../exasol/udf/v2/socket/unix_socket.hpp | 30 -------------- .../udf/v2/socket/unix_socket_listener.hpp | 40 +++++++++++++++++++ udf-runner-cpp/v2/socket/socket_test.cc | 20 ++-------- udf-runner-cpp/v2/socket/unix_socket.cc | 2 +- 8 files changed, 82 insertions(+), 74 deletions(-) create mode 100644 udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp create mode 100644 udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp diff --git a/doc/design/v2/runner/socket_library_design.md b/doc/design/v2/runner/socket_library_design.md index 95e210e..2e0d49f 100644 --- a/doc/design/v2/runner/socket_library_design.md +++ b/doc/design/v2/runner/socket_library_design.md @@ -47,8 +47,10 @@ Public types belong to `exasol::udf::v2::socket`. The implementation should prov small public header surface, for example: ``` -include/exasol/udf/v2/socket.hpp -include/exasol/udf/v2/unix_socket.hpp +include/exasol/udf/v2/socket/owned_file_descriptor.hpp +include/exasol/udf/v2/socket/socket.hpp +include/exasol/udf/v2/socket/unix_socket.hpp +include/exasol/udf/v2/socket/unix_socket_listener.hpp ``` The core header must not expose a TLS implementation or a TCP address type. diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 3a97aab..07426ca 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -362,8 +362,10 @@ cc_library( "socket/unix_socket.cc", ], hdrs = [ + "include/exasol/udf/v2/socket/owned_file_descriptor.hpp", "include/exasol/udf/v2/socket/socket.hpp", "include/exasol/udf/v2/socket/unix_socket.hpp", + "include/exasol/udf/v2/socket/unix_socket_listener.hpp", ], includes = ["include"], copts = ["-std=c++23"], diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp new file mode 100644 index 0000000..e6eb469 --- /dev/null +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace exasol::udf::v2::socket +{ + +class OwnedFileDescriptor +{ +public: + OwnedFileDescriptor() noexcept; + ~OwnedFileDescriptor(); + + [[nodiscard]] static OwnedFileDescriptor adopt_native_handle(int owned_fd); + + OwnedFileDescriptor(const OwnedFileDescriptor&) = delete; + OwnedFileDescriptor& operator=(const OwnedFileDescriptor&) = delete; + OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept; + OwnedFileDescriptor& operator=(OwnedFileDescriptor&& other) noexcept; + + [[nodiscard]] int native_handle() const noexcept; + [[nodiscard]] bool is_open() const noexcept; + void close() noexcept; + [[nodiscard]] int release_native_handle() noexcept; + +private: + explicit OwnedFileDescriptor(int owned_fd) noexcept; + + int file_descriptor = -1; +}; + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp index 4809aaa..c7aa9ab 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp @@ -3,6 +3,8 @@ #include #include +#include + namespace exasol::udf::v2::socket { @@ -13,30 +15,6 @@ enum class Shutdown Both, }; -class OwnedFileDescriptor -{ -public: - OwnedFileDescriptor() noexcept; - ~OwnedFileDescriptor(); - - [[nodiscard]] static OwnedFileDescriptor adopt_native_handle(int owned_fd); - - OwnedFileDescriptor(const OwnedFileDescriptor&) = delete; - OwnedFileDescriptor& operator=(const OwnedFileDescriptor&) = delete; - OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept; - OwnedFileDescriptor& operator=(OwnedFileDescriptor&& other) noexcept; - - [[nodiscard]] int native_handle() const noexcept; - [[nodiscard]] bool is_open() const noexcept; - void close() noexcept; - [[nodiscard]] int release_native_handle() noexcept; - -private: - explicit OwnedFileDescriptor(int owned_fd) noexcept; - - int file_descriptor = -1; -}; - class Socket { public: diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp index 9ba8c71..a4121ff 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include #include @@ -40,32 +38,4 @@ class UnixSocket final : public Socket OwnedFileDescriptor file_descriptor; }; -class UnixSocketListener -{ -public: - UnixSocketListener() noexcept; - ~UnixSocketListener(); - - UnixSocketListener(const UnixSocketListener&) = delete; - UnixSocketListener& operator=(const UnixSocketListener&) = delete; - UnixSocketListener(UnixSocketListener&& other) noexcept; - UnixSocketListener& operator=(UnixSocketListener&& other) noexcept; - - [[nodiscard]] static UnixSocketListener bind(const std::filesystem::path& path, - int backlog = SOMAXCONN); - - [[nodiscard]] int native_handle() const noexcept; - [[nodiscard]] bool is_open() const noexcept; - void close() noexcept; - [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; - [[nodiscard]] UnixSocket accept() const; - void unlink_path() const; - -private: - UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; - - OwnedFileDescriptor file_descriptor; - std::filesystem::path bound_path; -}; - } // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp new file mode 100644 index 0000000..2bdc8b4 --- /dev/null +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +#include + +namespace exasol::udf::v2::socket +{ + +class UnixSocketListener +{ +public: + UnixSocketListener() noexcept; + ~UnixSocketListener(); + + UnixSocketListener(const UnixSocketListener&) = delete; + UnixSocketListener& operator=(const UnixSocketListener&) = delete; + UnixSocketListener(UnixSocketListener&& other) noexcept; + UnixSocketListener& operator=(UnixSocketListener&& other) noexcept; + + [[nodiscard]] static UnixSocketListener bind(const std::filesystem::path& path, + int backlog = SOMAXCONN); + + [[nodiscard]] int native_handle() const noexcept; + [[nodiscard]] bool is_open() const noexcept; + void close() noexcept; + [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; + [[nodiscard]] UnixSocket accept() const; + void unlink_path() const; + +private: + UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; + + OwnedFileDescriptor file_descriptor; + std::filesystem::path bound_path; +}; + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc index c5072d0..c459fd9 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/socket_test.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -8,12 +8,9 @@ #include #include #include -#include #include -#include #include #include -#include #include @@ -27,19 +24,8 @@ using exasol::udf::v2::socket::UnixSocketListener; std::filesystem::path unique_socket_path() { - constexpr std::size_t maximum_temporary_root_length = 64; - const char* temporary_root = P_tmpdir; - if (const char* configured_temporary_root = std::getenv("TMPDIR"); - configured_temporary_root != nullptr && - std::strlen(configured_temporary_root) <= maximum_temporary_root_length) - { - temporary_root = configured_temporary_root; - } - const std::string template_path = - (std::filesystem::path(temporary_root) / "udf-runner-cpp-socket-XXXXXX").string(); - std::vector directory_template(template_path.begin(), template_path.end()); - directory_template.push_back('\0'); - char* directory = ::mkdtemp(directory_template.data()); + char directory_template[] = P_tmpdir "/udf-runner-cpp-socket-XXXXXX"; + char* directory = ::mkdtemp(directory_template); if (directory == nullptr) { ADD_FAILURE() << "mkdtemp failed"; diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index 00b7c46..2337b9b 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -1,4 +1,4 @@ -#include +#include #include #include From da8f14a15416ae5eb86c7d751fa6e221ad648049 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 9 Oct 2026 14:59:53 +0530 Subject: [PATCH 20/32] unit tests for unix domain sockets --- udf-runner-cpp/v2/BUILD.bazel | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 07426ca..4d68076 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -386,6 +386,18 @@ cc_test( target_compatible_with = ["@platforms//os:linux"], ) +cc_test( + name = "unix_socket_test", + srcs = ["socket/unix_socket_test.cc"], + copts = ["-std=c++23"], + tags = ["no-mull"], + deps = [ + ":socket", + "@googletest//:gtest_main", + ], + target_compatible_with = ["@platforms//os:linux"], +) + cc_fuzz_test( name = "socket_fuzz_test", srcs = ["socket/socket_fuzz_test.cc"], From dc7123936904ec67061d1788a80bb23b6c3d549c Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 9 Oct 2026 15:57:20 +0530 Subject: [PATCH 21/32] address comments --- udf-runner-cpp/v2/BUILD.bazel | 1 + udf-runner-cpp/v2/socket/socket.cc | 17 +- udf-runner-cpp/v2/socket/socket_fuzz_test.cc | 19 ++- udf-runner-cpp/v2/socket/socket_test.cc | 142 ++--------------- udf-runner-cpp/v2/socket/unix_socket.cc | 8 +- udf-runner-cpp/v2/socket/unix_socket_test.cc | 155 +++++++++++++++++++ 6 files changed, 189 insertions(+), 153 deletions(-) create mode 100644 udf-runner-cpp/v2/socket/unix_socket_test.cc diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index 4d68076..a5e2c20 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -401,6 +401,7 @@ cc_test( cc_fuzz_test( name = "socket_fuzz_test", srcs = ["socket/socket_fuzz_test.cc"], + corpus = glob(["fuzz/corpus/socket/**"]), copts = ["-std=c++23"], deps = [":socket"], tags = ["fuzz-test"], diff --git a/udf-runner-cpp/v2/socket/socket.cc b/udf-runner-cpp/v2/socket/socket.cc index 731c95e..9fddcbb 100644 --- a/udf-runner-cpp/v2/socket/socket.cc +++ b/udf-runner-cpp/v2/socket/socket.cc @@ -1,8 +1,7 @@ #include #include -#include -#include +#include #include #include @@ -20,7 +19,7 @@ OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int owned_fd) { if (owned_fd < 0) { - throw std::system_error(EBADF, std::generic_category(), "adopt invalid file descriptor"); + throw std::invalid_argument("adopt invalid file descriptor"); } return OwnedFileDescriptor(owned_fd); } @@ -34,25 +33,25 @@ OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) { if (this != &other) { - close(); - file_descriptor = std::exchange(other.file_descriptor, -1); + this->close(); + this->file_descriptor = std::exchange(other.file_descriptor, -1); } return *this; } int OwnedFileDescriptor::native_handle() const noexcept { - return file_descriptor; + return this->file_descriptor; } bool OwnedFileDescriptor::is_open() const noexcept { - return file_descriptor != -1; + return this->file_descriptor != -1; } void OwnedFileDescriptor::close() noexcept { - const int closed_fd = std::exchange(file_descriptor, -1); + const int closed_fd = std::exchange(this->file_descriptor, -1); if (closed_fd != -1) { ::close(closed_fd); @@ -61,7 +60,7 @@ void OwnedFileDescriptor::close() noexcept int OwnedFileDescriptor::release_native_handle() noexcept { - return std::exchange(file_descriptor, -1); + return std::exchange(this->file_descriptor, -1); } OwnedFileDescriptor::OwnedFileDescriptor(const int owned_fd) noexcept : file_descriptor(owned_fd) diff --git a/udf-runner-cpp/v2/socket/socket_fuzz_test.cc b/udf-runner-cpp/v2/socket/socket_fuzz_test.cc index e17f5c2..febc076 100644 --- a/udf-runner-cpp/v2/socket/socket_fuzz_test.cc +++ b/udf-runner-cpp/v2/socket/socket_fuzz_test.cc @@ -7,6 +7,14 @@ #include #include +/** + * Exercises vectored Unix-socket writes and matching peer reads using arbitrary fuzzer input. + * + * Inputs larger than the fixed 4 KiB receive buffer are ignored. The socket API permits partial + * writes, so the peer reads exactly the byte count reported by write_some(), rather than the + * entire fuzzer input. A socketpair creation failure is an infrastructure failure and aborts the + * fuzzing run. + */ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_t size) { if (size > 4096) @@ -16,20 +24,17 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_ std::array descriptors{}; if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, descriptors.data()) != 0) { - return 0; + __builtin_trap(); } auto writer = exasol::udf::v2::socket::UnixSocket::adopt_native_handle( exasol::udf::v2::socket::OwnedFileDescriptor::adopt_native_handle(descriptors[0])); auto reader = exasol::udf::v2::socket::UnixSocket::adopt_native_handle( exasol::udf::v2::socket::OwnedFileDescriptor::adopt_native_handle(descriptors[1])); const std::span bytes(reinterpret_cast(data), size); - if (const std::array buffers{bytes.first(size / 2), bytes.subspan(size / 2)}; - writer.write_some(buffers) != size) - { - return 0; - } + const std::size_t written = + writer.write_some(std::array{bytes.first(size / 2), bytes.subspan(size / 2)}); if (std::array received{}; - reader.read_some(std::span(received).first(size)) != size) + reader.read_some(std::span(received).first(written)) != written) { __builtin_trap(); } diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc index c459fd9..e45e1ef 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/socket_test.cc @@ -1,51 +1,18 @@ -#include - -#include -#include #include #include #include -#include -#include -#include -#include +#include #include +#include + #include namespace { using exasol::udf::v2::socket::OwnedFileDescriptor; -using exasol::udf::v2::socket::Shutdown; -using exasol::udf::v2::socket::UnixSocket; -using exasol::udf::v2::socket::UnixSocketListener; - -std::filesystem::path unique_socket_path() -{ - char directory_template[] = P_tmpdir "/udf-runner-cpp-socket-XXXXXX"; - char* directory = ::mkdtemp(directory_template); - if (directory == nullptr) - { - ADD_FAILURE() << "mkdtemp failed"; - return {}; - } - return std::filesystem::path(directory) / "socket"; -} - -void expect_system_error(const auto& function, const std::errc expected) -{ - try - { - function(); - ADD_FAILURE() << "expected std::system_error"; - } - catch (const std::system_error& error) - { - EXPECT_EQ(error.code(), std::make_error_code(expected)); - } -} TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) { @@ -53,6 +20,8 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::pipe(pipe_fds.data()), 0); OwnedFileDescriptor descriptor = OwnedFileDescriptor::adopt_native_handle(pipe_fds[0]); OwnedFileDescriptor moved(std::move(descriptor)); + EXPECT_EQ(descriptor.release_native_handle(), -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): + // Verifies the moved-from state. const int released_fd = moved.release_native_handle(); EXPECT_FALSE(moved.is_open()); ASSERT_EQ(::close(released_fd), 0); @@ -63,8 +32,8 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) TEST(SocketTest, RejectsInvalidDescriptorAdoption) { - expect_system_error([] { static_cast(OwnedFileDescriptor::adopt_native_handle(-1)); }, - std::errc::bad_file_descriptor); + EXPECT_THROW(static_cast(OwnedFileDescriptor::adopt_native_handle(-1)), + std::invalid_argument); } TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) @@ -76,6 +45,9 @@ TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) OwnedFileDescriptor source = OwnedFileDescriptor::adopt_native_handle(first_pipe[0]); OwnedFileDescriptor destination = OwnedFileDescriptor::adopt_native_handle(second_pipe[0]); destination = std::move(source); + EXPECT_EQ(source.release_native_handle(), -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): + // Verifies the moved-from state. + EXPECT_EQ(destination.native_handle(), first_pipe[0]); EXPECT_EQ(::close(second_pipe[0]), -1); EXPECT_EQ(errno, EBADF); ASSERT_EQ(::close(destination.release_native_handle()), 0); @@ -83,98 +55,4 @@ TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) ASSERT_EQ(::close(second_pipe[1]), 0); } -TEST(SocketTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) -{ - const std::filesystem::path path = unique_socket_path(); - UnixSocketListener listener = UnixSocketListener::bind(path); - ASSERT_TRUE(std::filesystem::exists(path)); - UnixSocket client = UnixSocket::connect(path); - UnixSocket server = listener.accept(); - - const std::array sent{std::byte{'o'}, std::byte{'k'}}; - ASSERT_EQ(client.write_some(sent), sent.size()); - std::array received{}; - ASSERT_EQ(server.read_some(received), received.size()); - EXPECT_EQ(received, sent); - - listener.close(); - EXPECT_TRUE(std::filesystem::exists(path)); - listener.unlink_path(); - EXPECT_FALSE(std::filesystem::exists(path)); - std::filesystem::remove(path.parent_path()); -} - -TEST(SocketTest, ListenerDoesNotOverwriteExistingPath) -{ - const std::filesystem::path path = unique_socket_path(); - UnixSocketListener listener = UnixSocketListener::bind(path); - expect_system_error([path] { static_cast(UnixSocketListener::bind(path)); }, - std::errc::address_in_use); - listener.close(); - listener.unlink_path(); - std::filesystem::remove(path.parent_path()); -} - -TEST(SocketTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) -{ - expect_system_error([] { static_cast(UnixSocketListener::bind({})); }, - std::errc::filename_too_long); - const std::filesystem::path path = unique_socket_path(); - UnixSocketListener listener = UnixSocketListener::bind(path); - EXPECT_THROW(listener.unlink_path(), std::logic_error); - listener.close(); - listener.unlink_path(); - std::filesystem::remove(path.parent_path()); -} - -TEST(SocketTest, SupportsScatterGatherIoAndPeerEof) -{ - std::array sockets{}; - ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); - UnixSocket writer = - UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); - UnixSocket reader = - UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); - const std::array first{std::byte{'a'}, std::byte{'b'}}; - const std::array second{std::byte{'c'}, std::byte{'d'}}; - const std::array, 2> buffers{first, second}; - ASSERT_EQ(writer.write_some(buffers), 4); - std::array received{}; - ASSERT_EQ(reader.read_some(received), 4); - EXPECT_EQ(received, (std::array{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}, - std::byte{'d'}})); - writer.close(); - std::array eof_buffer{}; - EXPECT_EQ(reader.read_some(eof_buffer), 0); -} - -TEST(SocketTest, NonblockingReadReportsWouldBlock) -{ - std::array sockets{}; - ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); - UnixSocket reader = - UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); - ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); - std::array buffer{}; - expect_system_error([&reader, &buffer] { static_cast(reader.read_some(buffer)); }, - std::errc::resource_unavailable_try_again); - ASSERT_EQ(::close(sockets[1]), 0); -} - -TEST(SocketTest, SupportsShutdownAndDescriptorRelease) -{ - std::array sockets{}; - ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); - UnixSocket writer = - UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); - UnixSocket reader = - UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); - writer.shutdown(Shutdown::Send); - std::array buffer{}; - EXPECT_EQ(reader.read_some(buffer), 0); - OwnedFileDescriptor released = reader.release_native_handle(); - EXPECT_FALSE(reader.is_open()); - EXPECT_TRUE(released.is_open()); -} - } // namespace diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index 2337b9b..767c0b0 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -97,16 +96,15 @@ namespace iovecs.reserve(buffers.size()); for (const Buffer buffer : buffers) { - if (std::size(buffer) > + if (buffer.size() > static_cast(std::numeric_limits::max()) - total_size) { throw std::system_error(EINVAL, std::generic_category(), "I/O buffer size is too large"); } - total_size += std::size(buffer); + total_size += buffer.size(); // POSIX declares iovec::iov_base as void* even for sendmsg(), which does not mutate it. - iovecs.push_back( - {const_cast(std::data(buffer)), std::size(buffer)}); // NOSONAR + iovecs.push_back({const_cast(buffer.data()), buffer.size()}); // NOSONAR } return iovecs; } diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc new file mode 100644 index 0000000..2c1937c --- /dev/null +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -0,0 +1,155 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +using exasol::udf::v2::socket::OwnedFileDescriptor; +using exasol::udf::v2::socket::Shutdown; +using exasol::udf::v2::socket::UnixSocket; +using exasol::udf::v2::socket::UnixSocketListener; + +void expect_system_error(const auto& function, const std::errc expected) +{ + try + { + function(); + ADD_FAILURE() << "expected std::system_error"; + } + catch (const std::system_error& error) + { + EXPECT_EQ(error.code(), std::make_error_code(expected)); + } +} + +class UnixSocketPathTest : public testing::Test +{ +protected: + void SetUp() override + { + char directory_template[] = P_tmpdir "/udf-runner-cpp-socket-XXXXXX"; + char* directory = ::mkdtemp(directory_template); + ASSERT_NE(directory, nullptr); + temporary_directory = directory; + } + + void TearDown() override + { + std::error_code error; + std::filesystem::remove_all(temporary_directory, error); + EXPECT_FALSE(error) << error.message(); + } + + [[nodiscard]] std::filesystem::path get_socket_path() const + { + return temporary_directory / "socket"; + } + +private: + std::filesystem::path temporary_directory; +}; + +TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) +{ + const std::filesystem::path path = get_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + ASSERT_TRUE(std::filesystem::exists(path)); + UnixSocket client = UnixSocket::connect(path); + UnixSocket server = listener.accept(); + + const std::array sent{std::byte{'o'}, std::byte{'k'}}; + ASSERT_EQ(client.write_some(sent), sent.size()); + std::array received{}; + ASSERT_EQ(server.read_some(received), received.size()); + EXPECT_EQ(received, sent); + + listener.close(); + EXPECT_TRUE(std::filesystem::exists(path)); + listener.unlink_path(); + EXPECT_FALSE(std::filesystem::exists(path)); +} + +TEST_F(UnixSocketPathTest, ListenerDoesNotOverwriteExistingPath) +{ + const std::filesystem::path path = get_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + expect_system_error([path] { static_cast(UnixSocketListener::bind(path)); }, + std::errc::address_in_use); + listener.close(); + listener.unlink_path(); +} + +TEST_F(UnixSocketPathTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) +{ + expect_system_error([] { static_cast(UnixSocketListener::bind({})); }, + std::errc::filename_too_long); + UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); + EXPECT_THROW(listener.unlink_path(), std::logic_error); + listener.close(); + listener.unlink_path(); +} + +TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket writer = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + const std::array first{std::byte{'a'}, std::byte{'b'}}; + const std::array second{std::byte{'c'}, std::byte{'d'}}; + const std::array, 2> buffers{first, second}; + ASSERT_EQ(writer.write_some(buffers), 4); + std::array received{}; + ASSERT_EQ(reader.read_some(received), 4); + EXPECT_EQ(received, (std::array{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}, + std::byte{'d'}})); + writer.close(); + std::array eof_buffer{}; + EXPECT_EQ(reader.read_some(eof_buffer), 0); +} + +TEST(UnixSocketTest, NonblockingReadReportsWouldBlockWhenPeerHasNotWrittenData) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); + std::array buffer{}; + expect_system_error([&reader, &buffer] { static_cast(reader.read_some(buffer)); }, + std::errc::resource_unavailable_try_again); + ASSERT_EQ(::close(sockets[1]), 0); +} + +TEST(UnixSocketTest, SupportsShutdownAndDescriptorRelease) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket writer = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + writer.shutdown(Shutdown::Send); + std::array buffer{}; + EXPECT_EQ(reader.read_some(buffer), 0); + OwnedFileDescriptor released = reader.release_native_handle(); + EXPECT_FALSE(reader.is_open()); + EXPECT_TRUE(released.is_open()); +} + +} // namespace From 4df8cecb7cf9086345ffb7fe585f0c7181af7f85 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Fri, 9 Oct 2026 21:52:09 +0530 Subject: [PATCH 22/32] fix formatting --- udf-runner-cpp/v2/socket/unix_socket_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 2c1937c..68fef49 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -40,8 +40,8 @@ class UnixSocketPathTest : public testing::Test protected: void SetUp() override { - char directory_template[] = P_tmpdir "/udf-runner-cpp-socket-XXXXXX"; - char* directory = ::mkdtemp(directory_template); + auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); + char* directory = ::mkdtemp(directory_template.data()); ASSERT_NE(directory, nullptr); temporary_directory = directory; } From 6aac2877f195c7dbce4ff30df397234df70b7e1a Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 12 Oct 2026 10:47:26 +0530 Subject: [PATCH 23/32] addressing all review comments --- udf-runner-cpp/v2/socket/socket_fuzz_test.cc | 15 +++++++++-- udf-runner-cpp/v2/socket/socket_test.cc | 10 +++++--- udf-runner-cpp/v2/socket/unix_socket.cc | 11 +++++--- udf-runner-cpp/v2/socket/unix_socket_test.cc | 27 +++++++++++++++++++- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/udf-runner-cpp/v2/socket/socket_fuzz_test.cc b/udf-runner-cpp/v2/socket/socket_fuzz_test.cc index febc076..db3e204 100644 --- a/udf-runner-cpp/v2/socket/socket_fuzz_test.cc +++ b/udf-runner-cpp/v2/socket/socket_fuzz_test.cc @@ -33,8 +33,19 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_ const std::span bytes(reinterpret_cast(data), size); const std::size_t written = writer.write_some(std::array{bytes.first(size / 2), bytes.subspan(size / 2)}); - if (std::array received{}; - reader.read_some(std::span(received).first(written)) != written) + std::array received{}; + std::size_t total_read = 0; + while (total_read < written) + { + const std::size_t read = + reader.read_some(std::span(received).subspan(total_read, written - total_read)); + if (read == 0) + { + __builtin_trap(); + } + total_read += read; + } + if (total_read != written) { __builtin_trap(); } diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc index e45e1ef..63c3453 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/socket_test.cc @@ -20,8 +20,9 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::pipe(pipe_fds.data()), 0); OwnedFileDescriptor descriptor = OwnedFileDescriptor::adopt_native_handle(pipe_fds[0]); OwnedFileDescriptor moved(std::move(descriptor)); - EXPECT_EQ(descriptor.release_native_handle(), -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): - // Verifies the moved-from state. + EXPECT_EQ(descriptor.release_native_handle(), + -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): + // Verifies the moved-from state. const int released_fd = moved.release_native_handle(); EXPECT_FALSE(moved.is_open()); ASSERT_EQ(::close(released_fd), 0); @@ -45,8 +46,9 @@ TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) OwnedFileDescriptor source = OwnedFileDescriptor::adopt_native_handle(first_pipe[0]); OwnedFileDescriptor destination = OwnedFileDescriptor::adopt_native_handle(second_pipe[0]); destination = std::move(source); - EXPECT_EQ(source.release_native_handle(), -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): - // Verifies the moved-from state. + EXPECT_EQ(source.release_native_handle(), + -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): + // Verifies the moved-from state. EXPECT_EQ(destination.native_handle(), first_pipe[0]); EXPECT_EQ(::close(second_pipe[0]), -1); EXPECT_EQ(errno, EBADF); diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index 767c0b0..62db9e7 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -40,8 +40,7 @@ namespace if (path_string.empty() || path_string.contains('\0') || path_string.size() >= sizeof(sockaddr_un::sun_path)) { - throw std::system_error(ENAMETOOLONG, std::generic_category(), - "invalid Unix socket path"); + throw std::invalid_argument("invalid Unix socket path: " + path_string); } sockaddr_un address{}; @@ -151,7 +150,9 @@ UnixSocket UnixSocket::connect(const std::filesystem::path& path) { if (errno != EINTR) { - throw std::system_error(errno, std::generic_category(), "connect Unix socket"); + const int error = errno; + throw std::system_error(error, std::generic_category(), + "connect Unix socket '" + path.string() + "'"); } } return UnixSocket(std::move(socket_fd)); @@ -268,7 +269,9 @@ UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, c OwnedFileDescriptor socket_fd = create_socket(); if (::bind(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) { - throw std::system_error(errno, std::generic_category(), "bind Unix socket listener"); + const int error = errno; + throw std::system_error(error, std::generic_category(), + "bind Unix socket listener '" + path.string() + "'"); } if (::listen(socket_fd.native_handle(), backlog) == -1) { diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 68fef49..4de2686 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -40,6 +40,7 @@ class UnixSocketPathTest : public testing::Test protected: void SetUp() override { + // Each test gets an isolated directory so socket paths cannot collide. auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); char* directory = ::mkdtemp(directory_template.data()); ASSERT_NE(directory, nullptr); @@ -48,6 +49,7 @@ class UnixSocketPathTest : public testing::Test void TearDown() override { + // The listener owns the socket descriptor, while the test owns its path. std::error_code error; std::filesystem::remove_all(temporary_directory, error); EXPECT_FALSE(error) << error.message(); @@ -67,6 +69,7 @@ TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) const std::filesystem::path path = get_socket_path(); UnixSocketListener listener = UnixSocketListener::bind(path); ASSERT_TRUE(std::filesystem::exists(path)); + // Connecting and accepting through the path verifies both listener endpoints. UnixSocket client = UnixSocket::connect(path); UnixSocket server = listener.accept(); @@ -78,10 +81,18 @@ TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) listener.close(); EXPECT_TRUE(std::filesystem::exists(path)); + // Closing does not unlink the filesystem entry; that requires an explicit call. listener.unlink_path(); EXPECT_FALSE(std::filesystem::exists(path)); } +TEST_F(UnixSocketPathTest, ConnectReportsAddressError) +{ + const std::filesystem::path path = get_socket_path().parent_path() / "missing" / "socket"; + expect_system_error([path] { static_cast(UnixSocket::connect(path)); }, + std::errc::no_such_file_or_directory); +} + TEST_F(UnixSocketPathTest, ListenerDoesNotOverwriteExistingPath) { const std::filesystem::path path = get_socket_path(); @@ -92,6 +103,13 @@ TEST_F(UnixSocketPathTest, ListenerDoesNotOverwriteExistingPath) listener.unlink_path(); } +TEST_F(UnixSocketPathTest, BindReportsAddressError) +{ + const std::filesystem::path path = get_socket_path().parent_path() / "missing" / "socket"; + expect_system_error([path] { static_cast(UnixSocketListener::bind(path)); }, + std::errc::no_such_file_or_directory); +} + TEST_F(UnixSocketPathTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) { expect_system_error([] { static_cast(UnixSocketListener::bind({})); }, @@ -106,12 +124,14 @@ TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) { std::array sockets{}; ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + // Wrap both ends so descriptor ownership is transferred to UnixSocket. UnixSocket writer = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); UnixSocket reader = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); const std::array first{std::byte{'a'}, std::byte{'b'}}; const std::array second{std::byte{'c'}, std::byte{'d'}}; + // Send two buffers in one operation and verify that the peer sees them contiguously. const std::array, 2> buffers{first, second}; ASSERT_EQ(writer.write_some(buffers), 4); std::array received{}; @@ -120,15 +140,18 @@ TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) std::byte{'d'}})); writer.close(); std::array eof_buffer{}; + // Closing the writer must make the reader observe peer EOF. EXPECT_EQ(reader.read_some(eof_buffer), 0); } -TEST(UnixSocketTest, NonblockingReadReportsWouldBlockWhenPeerHasNotWrittenData) +// A nonblocking read reports EAGAIN when no data is available. +TEST(UnixSocketTest, NonblockingReadWouldBlock) { std::array sockets{}; ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); UnixSocket reader = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + // With no data and O_NONBLOCK enabled, read_some must fail without waiting. ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); std::array buffer{}; expect_system_error([&reader, &buffer] { static_cast(reader.read_some(buffer)); }, @@ -144,10 +167,12 @@ TEST(UnixSocketTest, SupportsShutdownAndDescriptorRelease) UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); UnixSocket reader = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + // Shutting down only the send direction should produce EOF on the peer. writer.shutdown(Shutdown::Send); std::array buffer{}; EXPECT_EQ(reader.read_some(buffer), 0); OwnedFileDescriptor released = reader.release_native_handle(); + // Releasing transfers the descriptor out without closing it. EXPECT_FALSE(reader.is_open()); EXPECT_TRUE(released.is_open()); } From b97544a02fb506fbe9fedf292bc849d6258bc039 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 12 Oct 2026 10:50:30 +0530 Subject: [PATCH 24/32] adding doxygen comments --- .../udf/v2/socket/owned_file_descriptor.hpp | 15 +++++++++++ .../include/exasol/udf/v2/socket/socket.hpp | 27 +++++++++++++++++++ .../exasol/udf/v2/socket/unix_socket.hpp | 11 ++++++++ .../udf/v2/socket/unix_socket_listener.hpp | 23 ++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp index e6eb469..201b438 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp @@ -3,12 +3,20 @@ namespace exasol::udf::v2::socket { +/** Owns a native file descriptor and closes it on destruction. */ class OwnedFileDescriptor { public: + /** Creates an empty descriptor that does not own a native handle. */ OwnedFileDescriptor() noexcept; + + /** Closes the owned descriptor, if any. */ ~OwnedFileDescriptor(); + /** Takes ownership of an open native file descriptor. + * + * @throws std::invalid_argument if @p owned_fd is negative. + */ [[nodiscard]] static OwnedFileDescriptor adopt_native_handle(int owned_fd); OwnedFileDescriptor(const OwnedFileDescriptor&) = delete; @@ -16,9 +24,16 @@ class OwnedFileDescriptor OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept; OwnedFileDescriptor& operator=(OwnedFileDescriptor&& other) noexcept; + /** Returns the owned native descriptor, or -1 when empty. */ [[nodiscard]] int native_handle() const noexcept; + + /** Returns whether this object owns a native descriptor. */ [[nodiscard]] bool is_open() const noexcept; + + /** Closes the owned descriptor and becomes empty. */ void close() noexcept; + + /** Releases ownership and returns the native descriptor without closing it. */ [[nodiscard]] int release_native_handle() noexcept; private: diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp index c7aa9ab..5ba862a 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp @@ -8,6 +8,7 @@ namespace exasol::udf::v2::socket { +/** Direction used when shutting down part or all of a socket connection. */ enum class Shutdown { Receive, @@ -15,25 +16,51 @@ enum class Shutdown Both, }; +/** Common interface for owned stream sockets. */ class Socket { public: + /** Destroys the socket and releases its native descriptor. */ virtual ~Socket() = default; Socket(const Socket&) = delete; Socket& operator=(const Socket&) = delete; + /** Returns the native socket descriptor, or -1 when closed. */ [[nodiscard]] virtual int native_handle() const noexcept = 0; + + /** Returns whether the socket owns an open native descriptor. */ [[nodiscard]] virtual bool is_open() const noexcept = 0; + /** Closes the socket. This operation is idempotent. */ virtual void close() noexcept = 0; + + /** Releases the native descriptor without closing it. */ [[nodiscard]] virtual OwnedFileDescriptor release_native_handle() noexcept = 0; + + /** Shuts down the selected direction of the connection. + * + * @throws std::system_error if the operating-system shutdown fails. + */ virtual void shutdown(Shutdown how) = 0; + /** Reads up to the capacity described by the buffers. + * + * A successful call may read fewer bytes than requested. A return value of + * zero indicates end-of-file. + */ virtual std::size_t read_some(std::span> buffers) = 0; + + /** Writes up to the size described by the buffers. + * + * A successful call may write fewer bytes than requested. + */ virtual std::size_t write_some(std::span> buffers) = 0; + /** Reads into one contiguous buffer. */ std::size_t read_some(std::span buffer); + + /** Writes from one contiguous buffer. */ std::size_t write_some(std::span buffer); protected: diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp index a4121ff..c8ea790 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket.hpp @@ -7,6 +7,7 @@ namespace exasol::udf::v2::socket { +/** Owns one connected Unix-domain stream socket. */ class UnixSocket final : public Socket { public: @@ -21,7 +22,17 @@ class UnixSocket final : public Socket UnixSocket(UnixSocket&& other) noexcept; UnixSocket& operator=(UnixSocket&& other) noexcept; + /** Connects to a Unix-domain socket at @p path. + * + * @throws std::system_error if socket creation or connection fails. + * @throws std::invalid_argument if @p path is not a valid Unix socket path. + */ [[nodiscard]] static UnixSocket connect(const std::filesystem::path& path); + + /** Takes ownership of an open Unix stream socket descriptor. + * + * @throws std::system_error if @p owned_fd is closed or is not a Unix stream socket. + */ [[nodiscard]] static UnixSocket adopt_native_handle(OwnedFileDescriptor owned_fd); [[nodiscard]] int native_handle() const noexcept override; diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp index 2bdc8b4..db33c31 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp @@ -9,6 +9,7 @@ namespace exasol::udf::v2::socket { +/** Owns a Unix-domain stream listener. */ class UnixSocketListener { public: @@ -20,14 +21,36 @@ class UnixSocketListener UnixSocketListener(UnixSocketListener&& other) noexcept; UnixSocketListener& operator=(UnixSocketListener&& other) noexcept; + /** Creates a listener at @p path and starts listening for connections. + * + * @param path Filesystem path for the Unix socket. + * @param backlog Maximum number of pending connections. + * @throws std::system_error if socket creation, binding, or listening fails. + * @throws std::invalid_argument if @p path is not a valid Unix socket path. + */ [[nodiscard]] static UnixSocketListener bind(const std::filesystem::path& path, int backlog = SOMAXCONN); + /** Returns the native listener descriptor, or -1 when closed. */ [[nodiscard]] int native_handle() const noexcept; + + /** Returns whether the listener owns an open native descriptor. */ [[nodiscard]] bool is_open() const noexcept; + + /** Closes the listener without removing its filesystem path. */ void close() noexcept; + + /** Releases the native descriptor without closing it. */ [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; + + /** Accepts one connection from the listener. */ [[nodiscard]] UnixSocket accept() const; + + /** Removes the listener's filesystem path after it has been closed. + * + * @throws std::logic_error if the listener is still open. + * @throws std::system_error if removing the path fails. + */ void unlink_path() const; private: From b6a78657cdaef6ed3e7b9d3f3e3b75b2086f4d32 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 12 Oct 2026 13:18:54 +0530 Subject: [PATCH 25/32] clang format and tidy --- .../v2/include/exasol/udf/v2/socket/socket.hpp | 8 ++++---- udf-runner-cpp/v2/socket/socket_test.cc | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp index 5ba862a..bb3f581 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/socket.hpp @@ -30,10 +30,10 @@ class Socket [[nodiscard]] virtual int native_handle() const noexcept = 0; /** Returns whether the socket owns an open native descriptor. */ - [[nodiscard]] virtual bool is_open() const noexcept = 0; + [[nodiscard]] virtual bool is_open() const noexcept = 0; /** Closes the socket. This operation is idempotent. */ - virtual void close() noexcept = 0; + virtual void close() noexcept = 0; /** Releases the native descriptor without closing it. */ [[nodiscard]] virtual OwnedFileDescriptor release_native_handle() noexcept = 0; @@ -42,14 +42,14 @@ class Socket * * @throws std::system_error if the operating-system shutdown fails. */ - virtual void shutdown(Shutdown how) = 0; + virtual void shutdown(Shutdown how) = 0; /** Reads up to the capacity described by the buffers. * * A successful call may read fewer bytes than requested. A return value of * zero indicates end-of-file. */ - virtual std::size_t read_some(std::span> buffers) = 0; + virtual std::size_t read_some(std::span> buffers) = 0; /** Writes up to the size described by the buffers. * diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/socket_test.cc index 63c3453..4bb11ba 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/socket_test.cc @@ -20,9 +20,10 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::pipe(pipe_fds.data()), 0); OwnedFileDescriptor descriptor = OwnedFileDescriptor::adopt_native_handle(pipe_fds[0]); OwnedFileDescriptor moved(std::move(descriptor)); - EXPECT_EQ(descriptor.release_native_handle(), - -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): - // Verifies the moved-from state. + // NOLINTBEGIN(bugprone-use-after-move, clang-analyzer-cplusplus.Move): Inspecting the + // specified moved-from state is intentional. + EXPECT_EQ(descriptor.release_native_handle(), -1); + // NOLINTEND(bugprone-use-after-move, clang-analyzer-cplusplus.Move) const int released_fd = moved.release_native_handle(); EXPECT_FALSE(moved.is_open()); ASSERT_EQ(::close(released_fd), 0); @@ -46,9 +47,10 @@ TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) OwnedFileDescriptor source = OwnedFileDescriptor::adopt_native_handle(first_pipe[0]); OwnedFileDescriptor destination = OwnedFileDescriptor::adopt_native_handle(second_pipe[0]); destination = std::move(source); - EXPECT_EQ(source.release_native_handle(), - -1); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move): - // Verifies the moved-from state. + // NOLINTBEGIN(bugprone-use-after-move, clang-analyzer-cplusplus.Move): Inspecting the + // specified moved-from state is intentional. + EXPECT_EQ(source.release_native_handle(), -1); + // NOLINTEND(bugprone-use-after-move, clang-analyzer-cplusplus.Move) EXPECT_EQ(destination.native_handle(), first_pipe[0]); EXPECT_EQ(::close(second_pipe[0]), -1); EXPECT_EQ(errno, EBADF); From 2b1c52fe1fcef87df6368c1bd0f0400e0385807d Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Mon, 12 Oct 2026 13:36:39 +0530 Subject: [PATCH 26/32] fix the stale issue --- udf-runner-cpp/v2/socket/unix_socket_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 4de2686..be80015 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -112,8 +113,7 @@ TEST_F(UnixSocketPathTest, BindReportsAddressError) TEST_F(UnixSocketPathTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) { - expect_system_error([] { static_cast(UnixSocketListener::bind({})); }, - std::errc::filename_too_long); + EXPECT_THROW(static_cast(UnixSocketListener::bind({})), std::invalid_argument); UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); EXPECT_THROW(listener.unlink_path(), std::logic_error); listener.close(); From 74bc72dd2f5b3c97686f6a166784f0c1ab0faeb2 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Tue, 13 Oct 2026 15:00:15 +0530 Subject: [PATCH 27/32] implement RAII --- doc/design/v2/runner/socket_library_design.md | 16 ++-- .../udf/v2/socket/unix_socket_listener.hpp | 12 +-- udf-runner-cpp/v2/socket/unix_socket.cc | 88 +++++++++++++------ udf-runner-cpp/v2/socket/unix_socket_test.cc | 47 +++++++--- 4 files changed, 110 insertions(+), 53 deletions(-) diff --git a/doc/design/v2/runner/socket_library_design.md b/doc/design/v2/runner/socket_library_design.md index 2e0d49f..79e6444 100644 --- a/doc/design/v2/runner/socket_library_design.md +++ b/doc/design/v2/runner/socket_library_design.md @@ -34,9 +34,8 @@ core `Socket` abstraction: resolution, and transport-specific socket-option policy. - Timeouts, polling loops, and protocol framing. -The socket abstraction does not provide implicit unlinking of a filesystem path -before bind or during destruction, or thread safety -for concurrent mutation of an individual socket object. +The socket abstraction does not provide implicit unlinking before bind or +thread safety for concurrent mutation of an individual socket object. The library is Linux/POSIX-specific. This follows the v2 library's existing use of Linux readiness descriptors. @@ -216,7 +215,6 @@ public: void close() noexcept; [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; [[nodiscard]] UnixSocket accept(); - void unlink_path(); }; } // namespace exasol::udf::v2::socket @@ -248,10 +246,12 @@ socket. `accept()` accepts exactly one connection and returns it as an owning, close-on-exec `UnixSocket`. In nonblocking mode it throws `std::system_error` for would-block, just like stream I/O. -The listener retains the path supplied at bind only to support `unlink_path()`. -That operation is explicit and must be called only after closing the listener -and only when the caller knows it still owns the pathname. Destruction and -`close()` close the descriptor but never unlink the pathname. +The listener owns the path after a successful bind. `close()` and destruction +close the descriptor and then remove the path with best-effort cleanup. If +`bind()` succeeds but `listen()` fails, an internal scope guard removes the +newly created path before the exception leaves `bind()`. Releasing the native +descriptor also removes the path, leaving the released listener reachable only +through that descriptor. ## Future TCP and TLS diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp index db33c31..27ed48b 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp @@ -37,24 +37,18 @@ class UnixSocketListener /** Returns whether the listener owns an open native descriptor. */ [[nodiscard]] bool is_open() const noexcept; - /** Closes the listener without removing its filesystem path. */ + /** Closes the listener and removes its filesystem path. */ void close() noexcept; - /** Releases the native descriptor without closing it. */ + /** Releases the native descriptor and removes the listener's filesystem path. */ [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; /** Accepts one connection from the listener. */ [[nodiscard]] UnixSocket accept() const; - /** Removes the listener's filesystem path after it has been closed. - * - * @throws std::logic_error if the listener is still open. - * @throws std::system_error if removing the path fails. - */ - void unlink_path() const; - private: UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; + void unlink_path() noexcept; OwnedFileDescriptor file_descriptor; std::filesystem::path bound_path; diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index 62db9e7..f8c9792 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -28,12 +29,6 @@ namespace using std::logic_error::logic_error; }; - class ListenerStillOpen final : public std::logic_error - { - public: - using std::logic_error::logic_error; - }; - sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) { const std::string path_string = path.string(); @@ -128,6 +123,40 @@ namespace return std::bit_cast(&address); } + void unlink_socket_path(std::filesystem::path& path) noexcept + { + std::filesystem::path path_to_unlink = std::exchange(path, {}); + if (!path_to_unlink.empty()) + { + static_cast(::unlink(path_to_unlink.c_str())); + } + } + + class SocketPathCleanup final + { + public: + explicit SocketPathCleanup(std::filesystem::path& path) noexcept : path(path) + { + } + + ~SocketPathCleanup() + { + if (cleanup) + { + unlink_socket_path(path); + } + } + + void dont_clean() noexcept + { + cleanup = false; + } + + private: + std::filesystem::path& path; + bool cleanup = true; + }; + } // namespace UnixSocket::UnixSocket() noexcept = default; @@ -255,29 +284,47 @@ UnixSocketListener::UnixSocketListener() noexcept = default; UnixSocketListener::~UnixSocketListener() { - close(); + this->close(); } -UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept = default; +UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept + : file_descriptor(std::move(other.file_descriptor)), + bound_path(std::exchange(other.bound_path, {})) +{ +} -UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept = default; +UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept +{ + if (this != &other) + { + this->close(); + this->file_descriptor = std::move(other.file_descriptor); + this->bound_path = std::exchange(other.bound_path, {}); + } + return *this; +} UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, const int backlog) { socklen_t address_length = 0; const sockaddr_un address = make_socket_address(path, address_length); OwnedFileDescriptor socket_fd = create_socket(); + std::filesystem::path bound_path(path); if (::bind(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) { const int error = errno; throw std::system_error(error, std::generic_category(), "bind Unix socket listener '" + path.string() + "'"); } + + SocketPathCleanup cleanup_path(bound_path); if (::listen(socket_fd.native_handle(), backlog) == -1) { throw std::system_error(errno, std::generic_category(), "listen on Unix socket"); } - return UnixSocketListener(std::move(socket_fd), path); + UnixSocketListener listener(std::move(socket_fd), std::move(bound_path)); + cleanup_path.dont_clean(); + return listener; } int UnixSocketListener::native_handle() const noexcept @@ -292,12 +339,14 @@ bool UnixSocketListener::is_open() const noexcept void UnixSocketListener::close() noexcept { - file_descriptor.close(); + this->file_descriptor.close(); + this->unlink_path(); } OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept { - const int released_fd = file_descriptor.release_native_handle(); + const int released_fd = this->file_descriptor.release_native_handle(); + this->unlink_path(); if (released_fd == -1) { return {}; @@ -320,20 +369,9 @@ UnixSocket UnixSocketListener::accept() const return UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(accepted_fd)); } -void UnixSocketListener::unlink_path() const +void UnixSocketListener::unlink_path() noexcept { - if (is_open()) - { - throw ListenerStillOpen("close Unix socket listener before unlinking its path"); - } - if (bound_path.empty()) - { - return; - } - if (::unlink(bound_path.c_str()) == -1) - { - throw std::system_error(errno, std::generic_category(), "unlink Unix socket path"); - } + unlink_socket_path(this->bound_path); } UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index be80015..00f6d28 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -50,7 +50,7 @@ class UnixSocketPathTest : public testing::Test void TearDown() override { - // The listener owns the socket descriptor, while the test owns its path. + // The listener owns its path; the test removes its private directory. std::error_code error; std::filesystem::remove_all(temporary_directory, error); EXPECT_FALSE(error) << error.message(); @@ -65,7 +65,7 @@ class UnixSocketPathTest : public testing::Test std::filesystem::path temporary_directory; }; -TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) +TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndCleansUpPath) { const std::filesystem::path path = get_socket_path(); UnixSocketListener listener = UnixSocketListener::bind(path); @@ -81,9 +81,6 @@ TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndRequiresExplicitCleanup) EXPECT_EQ(received, sent); listener.close(); - EXPECT_TRUE(std::filesystem::exists(path)); - // Closing does not unlink the filesystem entry; that requires an explicit call. - listener.unlink_path(); EXPECT_FALSE(std::filesystem::exists(path)); } @@ -101,7 +98,7 @@ TEST_F(UnixSocketPathTest, ListenerDoesNotOverwriteExistingPath) expect_system_error([path] { static_cast(UnixSocketListener::bind(path)); }, std::errc::address_in_use); listener.close(); - listener.unlink_path(); + EXPECT_FALSE(std::filesystem::exists(path)); } TEST_F(UnixSocketPathTest, BindReportsAddressError) @@ -111,13 +108,41 @@ TEST_F(UnixSocketPathTest, BindReportsAddressError) std::errc::no_such_file_or_directory); } -TEST_F(UnixSocketPathTest, ValidatesPathsAndRequiresClosureBeforeUnlinking) +TEST_F(UnixSocketPathTest, ValidatesPathsAndCleansUpOnDestruction) { EXPECT_THROW(static_cast(UnixSocketListener::bind({})), std::invalid_argument); - UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); - EXPECT_THROW(listener.unlink_path(), std::logic_error); - listener.close(); - listener.unlink_path(); + const std::filesystem::path path = get_socket_path(); + { + UnixSocketListener listener = UnixSocketListener::bind(path); + EXPECT_TRUE(std::filesystem::exists(path)); + } + EXPECT_FALSE(std::filesystem::exists(path)); +} + +TEST_F(UnixSocketPathTest, ReleasingDescriptorCleansUpPath) +{ + const std::filesystem::path path = get_socket_path(); + UnixSocketListener listener = UnixSocketListener::bind(path); + + OwnedFileDescriptor released = listener.release_native_handle(); + + EXPECT_TRUE(released.is_open()); + EXPECT_FALSE(std::filesystem::exists(path)); +} + +TEST_F(UnixSocketPathTest, MoveAssignmentCleansUpReplacedPath) +{ + const std::filesystem::path first_path = get_socket_path(); + const std::filesystem::path second_path = first_path.parent_path() / "second_socket"; + UnixSocketListener source = UnixSocketListener::bind(first_path); + UnixSocketListener destination = UnixSocketListener::bind(second_path); + + destination = std::move(source); + + EXPECT_TRUE(std::filesystem::exists(first_path)); + EXPECT_FALSE(std::filesystem::exists(second_path)); + destination.close(); + EXPECT_FALSE(std::filesystem::exists(first_path)); } TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) From aa2d59bd86c671c35a99fde81b173a1258fd930d Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 14 Oct 2026 11:41:32 +0530 Subject: [PATCH 28/32] #75: Refactor Unix socket implementation layout --- doc/design/v2/runner/socket_library_design.md | 4 +- udf-runner-cpp/v2/BUILD.bazel | 11 +- .../udf/v2/socket/owned_file_descriptor.hpp | 2 +- .../udf/v2/socket/unix_socket_listener.hpp | 9 +- .../v2/socket/owned_file_descriptor.cc | 69 +++++++ ..._test.cc => owned_file_descriptor_test.cc} | 6 +- udf-runner-cpp/v2/socket/socket.cc | 62 ------ udf-runner-cpp/v2/socket/unix_socket.cc | 181 +----------------- .../v2/socket/unix_socket_detail.cc | 47 +++++ .../v2/socket/unix_socket_detail.hpp | 17 ++ .../v2/socket/unix_socket_listener.cc | 158 +++++++++++++++ udf-runner-cpp/v2/socket/unix_socket_test.cc | 75 ++++++-- 12 files changed, 381 insertions(+), 260 deletions(-) create mode 100644 udf-runner-cpp/v2/socket/owned_file_descriptor.cc rename udf-runner-cpp/v2/socket/{socket_test.cc => owned_file_descriptor_test.cc} (91%) create mode 100644 udf-runner-cpp/v2/socket/unix_socket_detail.cc create mode 100644 udf-runner-cpp/v2/socket/unix_socket_detail.hpp create mode 100644 udf-runner-cpp/v2/socket/unix_socket_listener.cc diff --git a/doc/design/v2/runner/socket_library_design.md b/doc/design/v2/runner/socket_library_design.md index 79e6444..1ca2adf 100644 --- a/doc/design/v2/runner/socket_library_design.md +++ b/doc/design/v2/runner/socket_library_design.md @@ -68,7 +68,7 @@ enum class Shutdown { Receive, Send, Both }; class OwnedFileDescriptor { public: OwnedFileDescriptor() noexcept; - ~OwnedFileDescriptor(); + ~OwnedFileDescriptor() noexcept; // Takes ownership of a valid raw file descriptor. [[nodiscard]] static OwnedFileDescriptor adopt_native_handle(int owned_fd); @@ -200,7 +200,7 @@ public: class UnixSocketListener { public: UnixSocketListener() noexcept; - ~UnixSocketListener(); + ~UnixSocketListener() noexcept; UnixSocketListener(const UnixSocketListener&) = delete; UnixSocketListener& operator=(const UnixSocketListener&) = delete; diff --git a/udf-runner-cpp/v2/BUILD.bazel b/udf-runner-cpp/v2/BUILD.bazel index a5e2c20..b4fb5de 100644 --- a/udf-runner-cpp/v2/BUILD.bazel +++ b/udf-runner-cpp/v2/BUILD.bazel @@ -358,14 +358,18 @@ cc_library( cc_library( name = "socket", srcs = [ + "socket/owned_file_descriptor.cc", "socket/socket.cc", "socket/unix_socket.cc", + "socket/unix_socket_detail.cc", + "socket/unix_socket_listener.cc", ], hdrs = [ "include/exasol/udf/v2/socket/owned_file_descriptor.hpp", "include/exasol/udf/v2/socket/socket.hpp", "include/exasol/udf/v2/socket/unix_socket.hpp", "include/exasol/udf/v2/socket/unix_socket_listener.hpp", + "socket/unix_socket_detail.hpp", ], includes = ["include"], copts = ["-std=c++23"], @@ -373,11 +377,10 @@ cc_library( ) cc_test( - name = "socket_test", - srcs = ["socket/socket_test.cc"], + name = "owned_file_descriptor_test", + srcs = ["socket/owned_file_descriptor_test.cc"], copts = ["-std=c++23"], - # Socket behavior remains covered by the normal Bazel test suite. Exclude - # this target from Mull until its mutation coverage is expanded. + # Exclude this target from Mull until its mutation coverage is expanded. tags = ["no-mull"], deps = [ ":socket", diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp index 201b438..028ae93 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/owned_file_descriptor.hpp @@ -11,7 +11,7 @@ class OwnedFileDescriptor OwnedFileDescriptor() noexcept; /** Closes the owned descriptor, if any. */ - ~OwnedFileDescriptor(); + ~OwnedFileDescriptor() noexcept; /** Takes ownership of an open native file descriptor. * diff --git a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp index 27ed48b..18a2373 100644 --- a/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp +++ b/udf-runner-cpp/v2/include/exasol/udf/v2/socket/unix_socket_listener.hpp @@ -14,7 +14,7 @@ class UnixSocketListener { public: UnixSocketListener() noexcept; - ~UnixSocketListener(); + ~UnixSocketListener() noexcept; UnixSocketListener(const UnixSocketListener&) = delete; UnixSocketListener& operator=(const UnixSocketListener&) = delete; @@ -43,8 +43,11 @@ class UnixSocketListener /** Releases the native descriptor and removes the listener's filesystem path. */ [[nodiscard]] OwnedFileDescriptor release_native_handle() noexcept; - /** Accepts one connection from the listener. */ - [[nodiscard]] UnixSocket accept() const; + /** Accepts one connection from the listener. + * + * @param flags Zero or a bitwise combination of SOCK_NONBLOCK and SOCK_CLOEXEC. + */ + [[nodiscard]] UnixSocket accept(int flags = SOCK_CLOEXEC) const; private: UnixSocketListener(OwnedFileDescriptor owned_fd, std::filesystem::path path) noexcept; diff --git a/udf-runner-cpp/v2/socket/owned_file_descriptor.cc b/udf-runner-cpp/v2/socket/owned_file_descriptor.cc new file mode 100644 index 0000000..bb864b9 --- /dev/null +++ b/udf-runner-cpp/v2/socket/owned_file_descriptor.cc @@ -0,0 +1,69 @@ +#include + +#include +#include +#include + +namespace exasol::udf::v2::socket +{ + +OwnedFileDescriptor::OwnedFileDescriptor() noexcept = default; + +OwnedFileDescriptor::~OwnedFileDescriptor() noexcept +{ + this->close(); +} + +OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int owned_fd) +{ + if (owned_fd < 0) + { + throw std::invalid_argument("adopt invalid file descriptor"); + } + return OwnedFileDescriptor(owned_fd); +} + +OwnedFileDescriptor::OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept + : file_descriptor(std::exchange(other.file_descriptor, -1)) +{ +} + +OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) noexcept +{ + if (this != &other) + { + this->close(); + this->file_descriptor = std::exchange(other.file_descriptor, -1); + } + return *this; +} + +int OwnedFileDescriptor::native_handle() const noexcept +{ + return this->file_descriptor; +} + +bool OwnedFileDescriptor::is_open() const noexcept +{ + return this->file_descriptor != -1; +} + +void OwnedFileDescriptor::close() noexcept +{ + const int closed_fd = std::exchange(this->file_descriptor, -1); + if (closed_fd != -1) + { + ::close(closed_fd); + } +} + +int OwnedFileDescriptor::release_native_handle() noexcept +{ + return std::exchange(this->file_descriptor, -1); +} + +OwnedFileDescriptor::OwnedFileDescriptor(const int owned_fd) noexcept : file_descriptor(owned_fd) +{ +} + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/socket/socket_test.cc b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc similarity index 91% rename from udf-runner-cpp/v2/socket/socket_test.cc rename to udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc index 4bb11ba..9beb4ae 100644 --- a/udf-runner-cpp/v2/socket/socket_test.cc +++ b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc @@ -14,7 +14,7 @@ namespace using exasol::udf::v2::socket::OwnedFileDescriptor; -TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) +TEST(OwnedFileDescriptorTest, ClosesExactlyOnceAfterMoveAndRelease) { std::array pipe_fds{}; ASSERT_EQ(::pipe(pipe_fds.data()), 0); @@ -32,13 +32,13 @@ TEST(SocketTest, OwnedDescriptorClosesExactlyOnceAfterMoveAndRelease) ASSERT_EQ(::close(pipe_fds[1]), 0); } -TEST(SocketTest, RejectsInvalidDescriptorAdoption) +TEST(OwnedFileDescriptorTest, RejectsInvalidDescriptorAdoption) { EXPECT_THROW(static_cast(OwnedFileDescriptor::adopt_native_handle(-1)), std::invalid_argument); } -TEST(SocketTest, MoveAssignmentClosesReplacedDescriptor) +TEST(OwnedFileDescriptorTest, MoveAssignmentClosesReplacedDescriptor) { std::array first_pipe{}; std::array second_pipe{}; diff --git a/udf-runner-cpp/v2/socket/socket.cc b/udf-runner-cpp/v2/socket/socket.cc index 9fddcbb..6111511 100644 --- a/udf-runner-cpp/v2/socket/socket.cc +++ b/udf-runner-cpp/v2/socket/socket.cc @@ -1,72 +1,10 @@ #include #include -#include -#include -#include namespace exasol::udf::v2::socket { -OwnedFileDescriptor::OwnedFileDescriptor() noexcept = default; - -OwnedFileDescriptor::~OwnedFileDescriptor() -{ - close(); -} - -OwnedFileDescriptor OwnedFileDescriptor::adopt_native_handle(const int owned_fd) -{ - if (owned_fd < 0) - { - throw std::invalid_argument("adopt invalid file descriptor"); - } - return OwnedFileDescriptor(owned_fd); -} - -OwnedFileDescriptor::OwnedFileDescriptor(OwnedFileDescriptor&& other) noexcept - : file_descriptor(std::exchange(other.file_descriptor, -1)) -{ -} - -OwnedFileDescriptor& OwnedFileDescriptor::operator=(OwnedFileDescriptor&& other) noexcept -{ - if (this != &other) - { - this->close(); - this->file_descriptor = std::exchange(other.file_descriptor, -1); - } - return *this; -} - -int OwnedFileDescriptor::native_handle() const noexcept -{ - return this->file_descriptor; -} - -bool OwnedFileDescriptor::is_open() const noexcept -{ - return this->file_descriptor != -1; -} - -void OwnedFileDescriptor::close() noexcept -{ - const int closed_fd = std::exchange(this->file_descriptor, -1); - if (closed_fd != -1) - { - ::close(closed_fd); - } -} - -int OwnedFileDescriptor::release_native_handle() noexcept -{ - return std::exchange(this->file_descriptor, -1); -} - -OwnedFileDescriptor::OwnedFileDescriptor(const int owned_fd) noexcept : file_descriptor(owned_fd) -{ -} - std::size_t Socket::read_some(const std::span buffer) { const std::array buffers{buffer}; diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index f8c9792..54a6e7e 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -1,18 +1,15 @@ -#include +#include + +#include "unix_socket_detail.hpp" -#include #include #include -#include #include -#include -#include #include #include #include #include -#include #include #include #include @@ -29,32 +26,6 @@ namespace using std::logic_error::logic_error; }; - sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) - { - const std::string path_string = path.string(); - if (path_string.empty() || path_string.contains('\0') || - path_string.size() >= sizeof(sockaddr_un::sun_path)) - { - throw std::invalid_argument("invalid Unix socket path: " + path_string); - } - - sockaddr_un address{}; - address.sun_family = AF_UNIX; - std::memcpy(address.sun_path, path_string.data(), path_string.size()); - length = static_cast(offsetof(sockaddr_un, sun_path) + path_string.size() + 1); - return address; - } - - [[nodiscard]] OwnedFileDescriptor create_socket() - { - const int socket_fd = ::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (socket_fd == -1) - { - throw std::system_error(errno, std::generic_category(), "create Unix socket"); - } - return OwnedFileDescriptor::adopt_native_handle(socket_fd); - } - void check_unix_stream_socket(const int file_descriptor) { int domain = 0; @@ -118,45 +89,6 @@ namespace throw InvalidShutdownDirection("invalid socket shutdown direction"); } - [[nodiscard]] const sockaddr* as_socket_address(const sockaddr_un& address) noexcept - { - return std::bit_cast(&address); - } - - void unlink_socket_path(std::filesystem::path& path) noexcept - { - std::filesystem::path path_to_unlink = std::exchange(path, {}); - if (!path_to_unlink.empty()) - { - static_cast(::unlink(path_to_unlink.c_str())); - } - } - - class SocketPathCleanup final - { - public: - explicit SocketPathCleanup(std::filesystem::path& path) noexcept : path(path) - { - } - - ~SocketPathCleanup() - { - if (cleanup) - { - unlink_socket_path(path); - } - } - - void dont_clean() noexcept - { - cleanup = false; - } - - private: - std::filesystem::path& path; - bool cleanup = true; - }; - } // namespace UnixSocket::UnixSocket() noexcept = default; @@ -173,9 +105,10 @@ UnixSocket& UnixSocket::operator=(UnixSocket&& other) noexcept = default; UnixSocket UnixSocket::connect(const std::filesystem::path& path) { socklen_t address_length = 0; - const sockaddr_un address = make_socket_address(path, address_length); - OwnedFileDescriptor socket_fd = create_socket(); - while (::connect(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) + const sockaddr_un address = detail::make_socket_address(path, address_length); + OwnedFileDescriptor socket_fd = detail::create_socket(); + while (::connect(socket_fd.native_handle(), detail::as_socket_address(address), + address_length) == -1) { if (errno != EINTR) { @@ -280,104 +213,4 @@ UnixSocket::UnixSocket(OwnedFileDescriptor owned_fd) noexcept : file_descriptor( { } -UnixSocketListener::UnixSocketListener() noexcept = default; - -UnixSocketListener::~UnixSocketListener() -{ - this->close(); -} - -UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept - : file_descriptor(std::move(other.file_descriptor)), - bound_path(std::exchange(other.bound_path, {})) -{ -} - -UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept -{ - if (this != &other) - { - this->close(); - this->file_descriptor = std::move(other.file_descriptor); - this->bound_path = std::exchange(other.bound_path, {}); - } - return *this; -} - -UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, const int backlog) -{ - socklen_t address_length = 0; - const sockaddr_un address = make_socket_address(path, address_length); - OwnedFileDescriptor socket_fd = create_socket(); - std::filesystem::path bound_path(path); - if (::bind(socket_fd.native_handle(), as_socket_address(address), address_length) == -1) - { - const int error = errno; - throw std::system_error(error, std::generic_category(), - "bind Unix socket listener '" + path.string() + "'"); - } - - SocketPathCleanup cleanup_path(bound_path); - if (::listen(socket_fd.native_handle(), backlog) == -1) - { - throw std::system_error(errno, std::generic_category(), "listen on Unix socket"); - } - UnixSocketListener listener(std::move(socket_fd), std::move(bound_path)); - cleanup_path.dont_clean(); - return listener; -} - -int UnixSocketListener::native_handle() const noexcept -{ - return file_descriptor.native_handle(); -} - -bool UnixSocketListener::is_open() const noexcept -{ - return file_descriptor.is_open(); -} - -void UnixSocketListener::close() noexcept -{ - this->file_descriptor.close(); - this->unlink_path(); -} - -OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept -{ - const int released_fd = this->file_descriptor.release_native_handle(); - this->unlink_path(); - if (released_fd == -1) - { - return {}; - } - return OwnedFileDescriptor::adopt_native_handle(released_fd); -} - -UnixSocket UnixSocketListener::accept() const -{ - int accepted_fd = -1; - do - { - accepted_fd = ::accept4(file_descriptor.native_handle(), nullptr, nullptr, SOCK_CLOEXEC); - } while (accepted_fd == -1 && errno == EINTR); - - if (accepted_fd == -1) - { - throw std::system_error(errno, std::generic_category(), "accept Unix socket connection"); - } - return UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(accepted_fd)); -} - -void UnixSocketListener::unlink_path() noexcept -{ - unlink_socket_path(this->bound_path); -} - -UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, - std::filesystem::path path) noexcept - : file_descriptor(std::move(owned_fd)), bound_path(std::move(path)) -{ -} - } // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/socket/unix_socket_detail.cc b/udf-runner-cpp/v2/socket/unix_socket_detail.cc new file mode 100644 index 0000000..32bf3a6 --- /dev/null +++ b/udf-runner-cpp/v2/socket/unix_socket_detail.cc @@ -0,0 +1,47 @@ +#include "unix_socket_detail.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace exasol::udf::v2::socket::detail +{ + +sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length) +{ + const std::string path_string = path.string(); + if (path_string.empty() || path_string.contains('\0') || + path_string.size() >= sizeof(sockaddr_un::sun_path)) + { + throw std::invalid_argument("invalid Unix socket path: " + path_string); + } + + sockaddr_un address{}; + address.sun_family = AF_UNIX; + std::memcpy(address.sun_path, path_string.data(), path_string.size()); + length = static_cast(offsetof(sockaddr_un, sun_path) + path_string.size() + 1); + return address; +} + +OwnedFileDescriptor create_socket() +{ + const int socket_fd = ::socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (socket_fd == -1) + { + throw std::system_error(errno, std::generic_category(), "create Unix socket"); + } + return OwnedFileDescriptor::adopt_native_handle(socket_fd); +} + +const sockaddr* as_socket_address(const sockaddr_un& address) noexcept +{ + return std::bit_cast(&address); +} + +} // namespace exasol::udf::v2::socket::detail diff --git a/udf-runner-cpp/v2/socket/unix_socket_detail.hpp b/udf-runner-cpp/v2/socket/unix_socket_detail.hpp new file mode 100644 index 0000000..4771b60 --- /dev/null +++ b/udf-runner-cpp/v2/socket/unix_socket_detail.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace exasol::udf::v2::socket::detail +{ + +[[nodiscard]] sockaddr_un make_socket_address(const std::filesystem::path& path, socklen_t& length); +[[nodiscard]] OwnedFileDescriptor create_socket(); +[[nodiscard]] const sockaddr* as_socket_address(const sockaddr_un& address) noexcept; + +} // namespace exasol::udf::v2::socket::detail diff --git a/udf-runner-cpp/v2/socket/unix_socket_listener.cc b/udf-runner-cpp/v2/socket/unix_socket_listener.cc new file mode 100644 index 0000000..d3853be --- /dev/null +++ b/udf-runner-cpp/v2/socket/unix_socket_listener.cc @@ -0,0 +1,158 @@ +#include + +#include "unix_socket_detail.hpp" + +#include +#include +#include + +#include + +namespace exasol::udf::v2::socket +{ + +namespace +{ + + void unlink_socket_path(std::filesystem::path& path) noexcept + { + std::filesystem::path path_to_unlink = std::exchange(path, {}); + if (!path_to_unlink.empty()) + { + static_cast(::unlink(path_to_unlink.c_str())); + } + } + + class SocketPathCleanup final + { + public: + explicit SocketPathCleanup(std::filesystem::path& path) noexcept : path(path) + { + } + + SocketPathCleanup(const SocketPathCleanup&) = delete; + SocketPathCleanup& operator=(const SocketPathCleanup&) = delete; + SocketPathCleanup(SocketPathCleanup&&) = delete; + SocketPathCleanup& operator=(SocketPathCleanup&&) = delete; + + ~SocketPathCleanup() + { + if (cleanup) + { + unlink_socket_path(path); + } + } + + void dont_clean() noexcept + { + cleanup = false; + } + + private: + std::filesystem::path& path; + bool cleanup = true; + }; + +} // namespace + +UnixSocketListener::UnixSocketListener() noexcept = default; + +UnixSocketListener::~UnixSocketListener() noexcept +{ + this->close(); +} + +UnixSocketListener::UnixSocketListener(UnixSocketListener&& other) noexcept + : file_descriptor(std::move(other.file_descriptor)), + bound_path(std::exchange(other.bound_path, {})) +{ +} + +UnixSocketListener& UnixSocketListener::operator=(UnixSocketListener&& other) noexcept +{ + if (this != &other) + { + this->close(); + this->file_descriptor = std::move(other.file_descriptor); + this->bound_path = std::exchange(other.bound_path, {}); + } + return *this; +} + +UnixSocketListener UnixSocketListener::bind(const std::filesystem::path& path, const int backlog) +{ + socklen_t address_length = 0; + const sockaddr_un address = detail::make_socket_address(path, address_length); + OwnedFileDescriptor socket_fd = detail::create_socket(); + std::filesystem::path bound_path(path); + if (::bind(socket_fd.native_handle(), detail::as_socket_address(address), address_length) == -1) + { + const int error = errno; + throw std::system_error(error, std::generic_category(), + "bind Unix socket listener '" + path.string() + "'"); + } + + SocketPathCleanup cleanup_path(bound_path); + if (::listen(socket_fd.native_handle(), backlog) == -1) + { + throw std::system_error(errno, std::generic_category(), "listen on Unix socket"); + } + UnixSocketListener listener(std::move(socket_fd), std::move(bound_path)); + cleanup_path.dont_clean(); + return listener; +} + +int UnixSocketListener::native_handle() const noexcept +{ + return this->file_descriptor.native_handle(); +} + +bool UnixSocketListener::is_open() const noexcept +{ + return this->file_descriptor.is_open(); +} + +void UnixSocketListener::close() noexcept +{ + this->file_descriptor.close(); + this->unlink_path(); +} + +OwnedFileDescriptor UnixSocketListener::release_native_handle() noexcept +{ + const int released_fd = this->file_descriptor.release_native_handle(); + this->unlink_path(); + if (released_fd == -1) + { + return {}; + } + return OwnedFileDescriptor::adopt_native_handle(released_fd); +} + +UnixSocket UnixSocketListener::accept(const int flags) const +{ + int accepted_fd = -1; + do + { + accepted_fd = ::accept4(this->file_descriptor.native_handle(), nullptr, nullptr, flags); + } while (accepted_fd == -1 && errno == EINTR); + + if (accepted_fd == -1) + { + throw std::system_error(errno, std::generic_category(), "accept Unix socket connection"); + } + return UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(accepted_fd)); +} + +void UnixSocketListener::unlink_path() noexcept +{ + unlink_socket_path(this->bound_path); +} + +UnixSocketListener::UnixSocketListener(OwnedFileDescriptor owned_fd, + std::filesystem::path path) noexcept + : file_descriptor(std::move(owned_fd)), bound_path(std::move(path)) +{ +} + +} // namespace exasol::udf::v2::socket diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 00f6d28..9b16e00 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -84,6 +84,29 @@ TEST_F(UnixSocketPathTest, ListenerConnectsAcceptsAndCleansUpPath) EXPECT_FALSE(std::filesystem::exists(path)); } +TEST_F(UnixSocketPathTest, AcceptUsesCallerProvidedFlags) +{ + UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); + + UnixSocket blocking_client = UnixSocket::connect(get_socket_path()); + UnixSocket blocking_server = listener.accept(0); + const int blocking_status_flags = ::fcntl(blocking_server.native_handle(), F_GETFL); + const int blocking_descriptor_flags = ::fcntl(blocking_server.native_handle(), F_GETFD); + ASSERT_NE(blocking_status_flags, -1); + ASSERT_NE(blocking_descriptor_flags, -1); + EXPECT_EQ(blocking_status_flags & O_NONBLOCK, 0); + EXPECT_EQ(blocking_descriptor_flags & FD_CLOEXEC, 0); + + UnixSocket nonblocking_client = UnixSocket::connect(get_socket_path()); + UnixSocket nonblocking_server = listener.accept(SOCK_NONBLOCK | SOCK_CLOEXEC); + const int nonblocking_status_flags = ::fcntl(nonblocking_server.native_handle(), F_GETFL); + const int nonblocking_descriptor_flags = ::fcntl(nonblocking_server.native_handle(), F_GETFD); + ASSERT_NE(nonblocking_status_flags, -1); + ASSERT_NE(nonblocking_descriptor_flags, -1); + EXPECT_NE(nonblocking_status_flags & O_NONBLOCK, 0); + EXPECT_NE(nonblocking_descriptor_flags & FD_CLOEXEC, 0); +} + TEST_F(UnixSocketPathTest, ConnectReportsAddressError) { const std::filesystem::path path = get_socket_path().parent_path() / "missing" / "socket"; @@ -108,9 +131,13 @@ TEST_F(UnixSocketPathTest, BindReportsAddressError) std::errc::no_such_file_or_directory); } -TEST_F(UnixSocketPathTest, ValidatesPathsAndCleansUpOnDestruction) +TEST_F(UnixSocketPathTest, RejectsEmptyPath) { EXPECT_THROW(static_cast(UnixSocketListener::bind({})), std::invalid_argument); +} + +TEST_F(UnixSocketPathTest, DestructionCleansUpPath) +{ const std::filesystem::path path = get_socket_path(); { UnixSocketListener listener = UnixSocketListener::bind(path); @@ -145,7 +172,7 @@ TEST_F(UnixSocketPathTest, MoveAssignmentCleansUpReplacedPath) EXPECT_FALSE(std::filesystem::exists(first_path)); } -TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) +TEST(UnixSocketTest, SupportsScatterGatherIo) { std::array sockets{}; ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); @@ -163,20 +190,30 @@ TEST(UnixSocketTest, SupportsScatterGatherIoAndPeerEof) ASSERT_EQ(reader.read_some(received), 4); EXPECT_EQ(received, (std::array{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}, std::byte{'d'}})); +} + +TEST(UnixSocketTest, ReadReturnsZeroAtPeerEof) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket writer = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket reader = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); writer.close(); std::array eof_buffer{}; // Closing the writer must make the reader observe peer EOF. EXPECT_EQ(reader.read_some(eof_buffer), 0); } -// A nonblocking read reports EAGAIN when no data is available. -TEST(UnixSocketTest, NonblockingReadWouldBlock) +// A nonblocking read maps EAGAIN to resource_unavailable_try_again. +TEST(UnixSocketTest, HandlesNonblockingRead) { std::array sockets{}; ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); UnixSocket reader = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); - // With no data and O_NONBLOCK enabled, read_some must fail without waiting. + // Enable nonblocking mode so read_some returns immediately when no data is available. ASSERT_NE(::fcntl(reader.native_handle(), F_SETFL, O_NONBLOCK), -1); std::array buffer{}; expect_system_error([&reader, &buffer] { static_cast(reader.read_some(buffer)); }, @@ -184,7 +221,7 @@ TEST(UnixSocketTest, NonblockingReadWouldBlock) ASSERT_EQ(::close(sockets[1]), 0); } -TEST(UnixSocketTest, SupportsShutdownAndDescriptorRelease) +TEST(UnixSocketTest, SendShutdownDisablesOnlyWrites) { std::array sockets{}; ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); @@ -192,13 +229,29 @@ TEST(UnixSocketTest, SupportsShutdownAndDescriptorRelease) UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); UnixSocket reader = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); - // Shutting down only the send direction should produce EOF on the peer. writer.shutdown(Shutdown::Send); - std::array buffer{}; - EXPECT_EQ(reader.read_some(buffer), 0); - OwnedFileDescriptor released = reader.release_native_handle(); + + const std::array data{std::byte{'x'}}; + expect_system_error([&writer, &data] { static_cast(writer.write_some(data)); }, + std::errc::broken_pipe); + + ASSERT_EQ(reader.write_some(data), data.size()); + std::array received{}; + ASSERT_EQ(writer.read_some(received), received.size()); + EXPECT_EQ(received, data); +} + +TEST(UnixSocketTest, ReleasesDescriptor) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket socket = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + OwnedFileDescriptor peer = OwnedFileDescriptor::adopt_native_handle(sockets[1]); + + OwnedFileDescriptor released = socket.release_native_handle(); // Releasing transfers the descriptor out without closing it. - EXPECT_FALSE(reader.is_open()); + EXPECT_FALSE(socket.is_open()); EXPECT_TRUE(released.is_open()); } From d0a919d136aa3660f393ac52e8ff747815f49b24 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 14 Oct 2026 11:54:04 +0530 Subject: [PATCH 29/32] fixing some clang-format --- udf-runner-cpp/v2/socket/unix_socket_test.cc | 26 +++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 9b16e00..359129c 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -88,23 +88,27 @@ TEST_F(UnixSocketPathTest, AcceptUsesCallerProvidedFlags) { UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); - UnixSocket blocking_client = UnixSocket::connect(get_socket_path()); - UnixSocket blocking_server = listener.accept(0); - const int blocking_status_flags = ::fcntl(blocking_server.native_handle(), F_GETFL); + UnixSocket blocking_client = UnixSocket::connect(get_socket_path()); + UnixSocket blocking_server = listener.accept(0); + const int blocking_status_flags = ::fcntl(blocking_server.native_handle(), F_GETFL); const int blocking_descriptor_flags = ::fcntl(blocking_server.native_handle(), F_GETFD); ASSERT_NE(blocking_status_flags, -1); ASSERT_NE(blocking_descriptor_flags, -1); - EXPECT_EQ(blocking_status_flags & O_NONBLOCK, 0); - EXPECT_EQ(blocking_descriptor_flags & FD_CLOEXEC, 0); - - UnixSocket nonblocking_client = UnixSocket::connect(get_socket_path()); - UnixSocket nonblocking_server = listener.accept(SOCK_NONBLOCK | SOCK_CLOEXEC); - const int nonblocking_status_flags = ::fcntl(nonblocking_server.native_handle(), F_GETFL); + const auto blocking_status = static_cast(blocking_status_flags); + const auto blocking_descriptor = static_cast(blocking_descriptor_flags); + EXPECT_EQ(blocking_status & static_cast(O_NONBLOCK), 0U); + EXPECT_EQ(blocking_descriptor & static_cast(FD_CLOEXEC), 0U); + + UnixSocket nonblocking_client = UnixSocket::connect(get_socket_path()); + UnixSocket nonblocking_server = listener.accept(SOCK_NONBLOCK | SOCK_CLOEXEC); + const int nonblocking_status_flags = ::fcntl(nonblocking_server.native_handle(), F_GETFL); const int nonblocking_descriptor_flags = ::fcntl(nonblocking_server.native_handle(), F_GETFD); ASSERT_NE(nonblocking_status_flags, -1); ASSERT_NE(nonblocking_descriptor_flags, -1); - EXPECT_NE(nonblocking_status_flags & O_NONBLOCK, 0); - EXPECT_NE(nonblocking_descriptor_flags & FD_CLOEXEC, 0); + const auto nonblocking_status = static_cast(nonblocking_status_flags); + const auto nonblocking_descriptor = static_cast(nonblocking_descriptor_flags); + EXPECT_NE(nonblocking_status & static_cast(O_NONBLOCK), 0U); + EXPECT_NE(nonblocking_descriptor & static_cast(FD_CLOEXEC), 0U); } TEST_F(UnixSocketPathTest, ConnectReportsAddressError) From e10eef883f7b7910c97ad381ad24794fd2a7f47b Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 14 Oct 2026 17:26:26 +0530 Subject: [PATCH 30/32] fixing sonar qube warning --- .../v2/socket/owned_file_descriptor_test.cc | 4 ++-- udf-runner-cpp/v2/socket/unix_socket.cc | 13 +++++++------ udf-runner-cpp/v2/socket/unix_socket_test.cc | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc index 9beb4ae..4f4e6d6 100644 --- a/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc +++ b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc @@ -22,7 +22,7 @@ TEST(OwnedFileDescriptorTest, ClosesExactlyOnceAfterMoveAndRelease) OwnedFileDescriptor moved(std::move(descriptor)); // NOLINTBEGIN(bugprone-use-after-move, clang-analyzer-cplusplus.Move): Inspecting the // specified moved-from state is intentional. - EXPECT_EQ(descriptor.release_native_handle(), -1); + EXPECT_EQ(descriptor.release_native_handle(), -1); // NOSONAR // NOLINTEND(bugprone-use-after-move, clang-analyzer-cplusplus.Move) const int released_fd = moved.release_native_handle(); EXPECT_FALSE(moved.is_open()); @@ -49,7 +49,7 @@ TEST(OwnedFileDescriptorTest, MoveAssignmentClosesReplacedDescriptor) destination = std::move(source); // NOLINTBEGIN(bugprone-use-after-move, clang-analyzer-cplusplus.Move): Inspecting the // specified moved-from state is intentional. - EXPECT_EQ(source.release_native_handle(), -1); + EXPECT_EQ(source.release_native_handle(), -1); // NOSONAR // NOLINTEND(bugprone-use-after-move, clang-analyzer-cplusplus.Move) EXPECT_EQ(destination.native_handle(), first_pipe[0]); EXPECT_EQ(::close(second_pipe[0]), -1); diff --git a/udf-runner-cpp/v2/socket/unix_socket.cc b/udf-runner-cpp/v2/socket/unix_socket.cc index 54a6e7e..0aac652 100644 --- a/udf-runner-cpp/v2/socket/unix_socket.cc +++ b/udf-runner-cpp/v2/socket/unix_socket.cc @@ -51,25 +51,26 @@ namespace template std::vector make_iovecs(const std::span buffers) { - if (buffers.size() > IOV_MAX) + if (std::size(buffers) > IOV_MAX) { throw std::system_error(EINVAL, std::generic_category(), "too many I/O buffers"); } std::size_t total_size = 0; std::vector iovecs; - iovecs.reserve(buffers.size()); - for (const Buffer buffer : buffers) + iovecs.reserve(std::size(buffers)); + for (const Buffer buffer : std::span(buffers)) { - if (buffer.size() > + if (std::size(buffer) > static_cast(std::numeric_limits::max()) - total_size) { throw std::system_error(EINVAL, std::generic_category(), "I/O buffer size is too large"); } - total_size += buffer.size(); + total_size += std::size(buffer); // POSIX declares iovec::iov_base as void* even for sendmsg(), which does not mutate it. - iovecs.push_back({const_cast(buffer.data()), buffer.size()}); // NOSONAR + iovecs.push_back( + {const_cast(std::data(buffer)), std::size(buffer)}); // NOSONAR } return iovecs; } diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 359129c..56153c7 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -43,7 +43,7 @@ class UnixSocketPathTest : public testing::Test { // Each test gets an isolated directory so socket paths cannot collide. auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); - char* directory = ::mkdtemp(directory_template.data()); + char* directory = ::mkdtemp(directory_template.data()); // NOSONAR ASSERT_NE(directory, nullptr); temporary_directory = directory; } From b3b50d5606b42ae69e9fca10502190de15996ef9 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 14 Oct 2026 19:02:33 +0530 Subject: [PATCH 31/32] Sonar Qube Code Coverage --- .../v2/socket/owned_file_descriptor_test.cc | 7 ++ udf-runner-cpp/v2/socket/unix_socket_test.cc | 104 +++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc index 4f4e6d6..d8c5b3f 100644 --- a/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc +++ b/udf-runner-cpp/v2/socket/owned_file_descriptor_test.cc @@ -38,6 +38,13 @@ TEST(OwnedFileDescriptorTest, RejectsInvalidDescriptorAdoption) std::invalid_argument); } +TEST(OwnedFileDescriptorTest, DefaultDescriptorIsClosed) +{ + const OwnedFileDescriptor descriptor; + EXPECT_FALSE(descriptor.is_open()); + EXPECT_EQ(descriptor.native_handle(), -1); +} + TEST(OwnedFileDescriptorTest, MoveAssignmentClosesReplacedDescriptor) { std::array first_pipe{}; diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 56153c7..6442e8b 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -6,9 +6,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -42,7 +44,7 @@ class UnixSocketPathTest : public testing::Test void SetUp() override { // Each test gets an isolated directory so socket paths cannot collide. - auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); + auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); // NOSONAR char* directory = ::mkdtemp(directory_template.data()); // NOSONAR ASSERT_NE(directory, nullptr); temporary_directory = directory; @@ -135,6 +137,29 @@ TEST_F(UnixSocketPathTest, BindReportsAddressError) std::errc::no_such_file_or_directory); } +TEST_F(UnixSocketPathTest, DefaultListenerCanBeInspectedAndReleased) +{ + UnixSocketListener listener; + EXPECT_FALSE(listener.is_open()); + EXPECT_EQ(listener.native_handle(), -1); + EXPECT_FALSE(listener.release_native_handle().is_open()); +} + +TEST_F(UnixSocketPathTest, AcceptOnClosedListenerReportsError) +{ + UnixSocketListener listener; + expect_system_error([&listener] { static_cast(listener.accept()); }, + std::errc::bad_file_descriptor); +} + +TEST_F(UnixSocketPathTest, SelfMoveAssignmentDoesNothing) +{ + UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); + listener = std::move(listener); + EXPECT_TRUE(listener.is_open()); + EXPECT_TRUE(std::filesystem::exists(get_socket_path())); +} + TEST_F(UnixSocketPathTest, RejectsEmptyPath) { EXPECT_THROW(static_cast(UnixSocketListener::bind({})), std::invalid_argument); @@ -259,4 +284,81 @@ TEST(UnixSocketTest, ReleasesDescriptor) EXPECT_TRUE(released.is_open()); } +TEST(UnixSocketTest, SupportsMoveConstructionAndAssignment) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket source = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + UnixSocket moved(std::move(source)); + EXPECT_FALSE(source.is_open()); + EXPECT_TRUE(moved.is_open()); + + UnixSocket destination = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); + destination = std::move(moved); + EXPECT_FALSE(moved.is_open()); + EXPECT_TRUE(destination.is_open()); +} + +TEST(UnixSocketTest, RejectsClosedDescriptor) +{ + EXPECT_THROW(static_cast(UnixSocket::adopt_native_handle({})), std::system_error); +} + +TEST(UnixSocketTest, RejectsNonUnixSocketDescriptor) +{ + const int descriptor = ::socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + ASSERT_NE(descriptor, -1); + OwnedFileDescriptor owned = OwnedFileDescriptor::adopt_native_handle(descriptor); + EXPECT_THROW(static_cast(UnixSocket::adopt_native_handle(std::move(owned))), + std::system_error); +} + +TEST(UnixSocketTest, HandlesEmptyIoBuffers) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket socket = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + const std::span> empty_read{}; + const std::span> empty_write{}; + EXPECT_EQ(socket.read_some(empty_read), 0U); + EXPECT_EQ(socket.write_some(empty_write), 0U); + ASSERT_EQ(::close(sockets[1]), 0); +} + +TEST(UnixSocketTest, RejectsTooManyIoBuffers) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket socket = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + std::array byte{}; + std::array, IOV_MAX + 1> buffers{}; + buffers.fill(byte); + EXPECT_THROW(static_cast(socket.read_some(buffers)), std::system_error); + ASSERT_EQ(::close(sockets[1]), 0); +} + +TEST(UnixSocketTest, HandlesAllShutdownDirectionsAndInvalidDirection) +{ + std::array sockets{}; + ASSERT_EQ(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets.data()), 0); + UnixSocket socket = + UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); + EXPECT_NO_THROW(socket.shutdown(Shutdown::Receive)); + EXPECT_NO_THROW(socket.shutdown(Shutdown::Send)); + EXPECT_NO_THROW(socket.shutdown(Shutdown::Both)); + EXPECT_THROW(socket.shutdown(static_cast(-1)), std::logic_error); + ASSERT_EQ(::close(sockets[1]), 0); +} + +TEST(UnixSocketTest, ReportsShutdownAndReleaseOnClosedSocket) +{ + UnixSocket socket; + EXPECT_THROW(socket.shutdown(Shutdown::Both), std::system_error); + EXPECT_FALSE(socket.release_native_handle().is_open()); +} + } // namespace From a8014d9fd83090829595754c8e7e2a831320d042 Mon Sep 17 00:00:00 2001 From: "Shankar.KV" Date: Wed, 14 Oct 2026 19:08:34 +0530 Subject: [PATCH 32/32] satisfy the clang-tidy checks on SelfMove --- udf-runner-cpp/v2/socket/unix_socket_test.cc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/udf-runner-cpp/v2/socket/unix_socket_test.cc b/udf-runner-cpp/v2/socket/unix_socket_test.cc index 6442e8b..9986bce 100644 --- a/udf-runner-cpp/v2/socket/unix_socket_test.cc +++ b/udf-runner-cpp/v2/socket/unix_socket_test.cc @@ -44,8 +44,9 @@ class UnixSocketPathTest : public testing::Test void SetUp() override { // Each test gets an isolated directory so socket paths cannot collide. - auto directory_template = std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); // NOSONAR - char* directory = ::mkdtemp(directory_template.data()); // NOSONAR + auto directory_template = + std::to_array(P_tmpdir "/udf-runner-cpp-socket-XXXXXX"); // NOSONAR + char* directory = ::mkdtemp(directory_template.data()); // NOSONAR ASSERT_NE(directory, nullptr); temporary_directory = directory; } @@ -152,14 +153,6 @@ TEST_F(UnixSocketPathTest, AcceptOnClosedListenerReportsError) std::errc::bad_file_descriptor); } -TEST_F(UnixSocketPathTest, SelfMoveAssignmentDoesNothing) -{ - UnixSocketListener listener = UnixSocketListener::bind(get_socket_path()); - listener = std::move(listener); - EXPECT_TRUE(listener.is_open()); - EXPECT_TRUE(std::filesystem::exists(get_socket_path())); -} - TEST_F(UnixSocketPathTest, RejectsEmptyPath) { EXPECT_THROW(static_cast(UnixSocketListener::bind({})), std::invalid_argument); @@ -291,13 +284,11 @@ TEST(UnixSocketTest, SupportsMoveConstructionAndAssignment) UnixSocket source = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[0])); UnixSocket moved(std::move(source)); - EXPECT_FALSE(source.is_open()); EXPECT_TRUE(moved.is_open()); UnixSocket destination = UnixSocket::adopt_native_handle(OwnedFileDescriptor::adopt_native_handle(sockets[1])); destination = std::move(moved); - EXPECT_FALSE(moved.is_open()); EXPECT_TRUE(destination.is_open()); }