diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e751c5..db1e538 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - config: [default, llvm, gnu32, sanitize] + config: [default, llvm, gnu32, sanitize, olddeps] name: build • ${{ matrix.config }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 284eba2..ef69441 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,29 @@ endif() include("cmake/compat_find.cmake") -find_package(CapnProto 0.7.0 REQUIRED) find_package(Threads REQUIRED) +find_package(CapnProto 0.7 REQUIRED) + +# Check for list-of-pointers memory access bug from Nov 2022 +# https://nvd.nist.gov/vuln/detail/CVE-2022-46149 +# https://github.com/advisories/GHSA-qqff-4vw4-f6hx +# https://github.com/capnproto/capnproto/security/advisories/GHSA-qqff-4vw4-f6hx +# https://github.com/capnproto/capnproto/blob/master/security-advisories/2022-11-30-0-pointer-list-bounds.md +# https://capnproto.org/news/2022-11-30-CVE-2022-46149-security-advisory.html +# https://dwrensha.github.io/capnproto-rust/2022/11/30/out_of_bounds_memory_access_bug.html +if(CapnProto_VERSION STREQUAL "0.7.0" + OR CapnProto_VERSION STREQUAL "0.8.0" + OR CapnProto_VERSION STREQUAL "0.9.0" + OR CapnProto_VERSION STREQUAL "0.9.1" + OR CapnProto_VERSION STREQUAL "0.10.0" + OR CapnProto_VERSION STREQUAL "0.10.1" + OR CapnProto_VERSION STREQUAL "0.10.2") + message(FATAL_ERROR + "Cap'n Proto ${CapnProto_VERSION} is affected by CVE-2022-46149.\n" + "Please install an updated package.\n" + "Details: https://github.com/advisories/GHSA-qqff-4vw4-f6hx + ") +endif() set(MPGEN_EXECUTABLE "" CACHE FILEPATH "If specified, should be full path to an external mpgen binary to use rather than the one built internally.") diff --git a/ci/README.md b/ci/README.md index 85eb467..2ddaa9e 100644 --- a/ci/README.md +++ b/ci/README.md @@ -20,6 +20,7 @@ CI_CONFIG=ci/configs/default.bash ci/scripts/run.sh CI_CONFIG=ci/configs/llvm.bash ci/scripts/run.sh CI_CONFIG=ci/configs/gnu32.bash ci/scripts/run.sh CI_CONFIG=ci/configs/sanitize.bash ci/scripts/run.sh +CI_CONFIG=ci/configs/olddeps.bash ci/scripts/run.sh ``` By default CI jobs will reuse their build directories. `CI_CLEAN=1` can be specified to delete them before running instead. diff --git a/ci/configs/olddeps.bash b/ci/configs/olddeps.bash new file mode 100644 index 0000000..4684bd5 --- /dev/null +++ b/ci/configs/olddeps.bash @@ -0,0 +1,5 @@ +CI_DESC="CI job using old Cap'n Proto version" +CI_DIR=build-olddeps +export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-error=array-bounds" +NIX_ARGS=(--argstr capnprotoVersion "0.7.1") +BUILD_ARGS=(-k) diff --git a/ci/patches/spaceship.patch b/ci/patches/spaceship.patch new file mode 100644 index 0000000..08a885d --- /dev/null +++ b/ci/patches/spaceship.patch @@ -0,0 +1,29 @@ +commit e3da7da967b94f373c29a198ce45f30fb9f0e517 +Author: Ed Catmur +Date: Tue Jan 31 16:27:04 2023 +0000 + + Remove operator!= synthesized by spaceship + + An operator!= suppresses the reversed equality comparison candidate generation. + + This is visible in clang 16 (rc0 just released) and in gcc trunk (so probably gcc 13). + +diff --git a/c++/src/kj/string.h b/c++/src/kj/string.h +index 193442aa..17835892 100644 +--- a/c++/src/kj/string.h ++++ b/c++/src/kj/string.h +@@ -122,10 +122,14 @@ public: + inline constexpr const char* end() const { return content.end() - 1; } + + inline constexpr bool operator==(decltype(nullptr)) const { return content.size() <= 1; } ++#if !__cpp_impl_three_way_comparison + inline constexpr bool operator!=(decltype(nullptr)) const { return content.size() > 1; } ++#endif + + inline bool operator==(const StringPtr& other) const; ++#if !__cpp_impl_three_way_comparison + inline bool operator!=(const StringPtr& other) const { return !(*this == other); } ++#endif + inline bool operator< (const StringPtr& other) const; + inline bool operator> (const StringPtr& other) const { return other < *this; } + inline bool operator<=(const StringPtr& other) const { return !(other < *this); } diff --git a/doc/install.md b/doc/install.md index 3fe7c7b..ac2f7f6 100644 --- a/doc/install.md +++ b/doc/install.md @@ -1,6 +1,6 @@ # libmultiprocess Installation -Installation currently requires Cap'n Proto: +Installation currently requires Cap'n Proto 0.7 or higher: ```sh apt install libcapnp-dev capnproto diff --git a/shell.nix b/shell.nix index eacfdc2..0b7dce8 100644 --- a/shell.nix +++ b/shell.nix @@ -2,12 +2,39 @@ , crossPkgs ? import {} , enableLibcxx ? false # Whether to use libc++ toolchain and libraries instead of libstdc++ , minimal ? false # Whether to create minimal shell without extra tools (faster when cross compiling) +, capnprotoVersion ? null }: let lib = pkgs.lib; llvm = crossPkgs.llvmPackages_20; - capnproto = crossPkgs.capnproto.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; }); + capnprotoHashes = { + "0.7.0" = "sha256-Y/7dUOQPDHjniuKNRw3j8dG1NI9f/aRWpf8V0WzV9k8="; + "0.7.1" = "sha256-3cBpVmpvCXyqPUXDp12vCFCk32ZXWpkdOliNH37UwWE="; + "0.8.0" = "sha256-rfiqN83begjJ9eYjtr21/tk1GJBjmeVfa3C3dZBJ93w="; + "0.8.1" = "sha256-OZqNVYdyszro5rIe+w6YN00g6y8U/1b8dKYc214q/2o="; + "0.9.0" = "sha256-yhbDcWUe6jp5PbIXzn5EoKabXiWN8lnS08hyfxUgEQ0="; + "0.9.2" = "sha256-BspWOPZcP5nCTvmsDE62Zutox+aY5pw42d6hpH3v4cM="; + "0.10.0" = "sha256-++F4l54OMTDnJ+FO3kV/Y/VLobKVRk461dopanuU3IQ="; + "0.10.4" = "sha256-45sxnVyyYIw9i3sbFZ1naBMoUzkpP21WarzR5crg4X8="; + "1.0.0" = "sha256-NLTFJdeOzqhk4ATvkc17Sh6g/junzqYBBEoXYGH/czo="; + "1.0.2" = "sha256-LVdkqVBTeh8JZ1McdVNtRcnFVwEJRNjt0JV2l7RkuO8="; + "1.1.0" = "sha256-gxkko7LFyJNlxpTS+CWOd/p9x/778/kNIXfpDGiKM2A="; + "1.2.0" = "sha256-aDcn4bLZGq8915/NPPQsN5Jv8FRWd8cAspkG3078psc="; + }; + capnprotoBase = if capnprotoVersion == null then crossPkgs.capnproto else crossPkgs.capnproto.overrideAttrs (old: { + version = capnprotoVersion; + src = crossPkgs.fetchFromGitHub { + owner = "capnproto"; + repo = "capnproto"; + rev = "v${capnprotoVersion}"; + hash = lib.attrByPath [capnprotoVersion] "" capnprotoHashes; + }; + patches = lib.optionals (lib.versionAtLeast capnprotoVersion "0.9.0" && lib.versionOlder capnprotoVersion "0.10.4") [ ./ci/patches/spaceship.patch ]; + } // (lib.optionalAttrs (lib.versionOlder capnprotoVersion "0.10") { + env = { }; # Drop -std=c++20 flag forced by nixpkgs + })); + capnproto = capnprotoBase.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; }); clang = if enableLibcxx then llvm.libcxxClang else llvm.clang; clang-tools = llvm.clang-tools.override { inherit enableLibcxx; }; in crossPkgs.mkShell { diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index 21a4d93..1840071 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -146,7 +146,7 @@ static void Generate(kj::StringPtr src_prefix, const std::vector>& import_dirs) { std::string output_path; - if (src_prefix == ".") { + if (src_prefix == kj::StringPtr{"."}) { output_path = src_file; } else if (!src_file.startsWith(src_prefix) || src_file.size() <= src_prefix.size() || src_file[src_prefix.size()] != '/') { @@ -156,7 +156,7 @@ static void Generate(kj::StringPtr src_prefix, } std::string include_path; - if (include_prefix == ".") { + if (include_prefix == kj::StringPtr{"."}) { include_path = src_file; } else if (!src_file.startsWith(include_prefix) || src_file.size() <= include_prefix.size() || src_file[include_prefix.size()] != '/') { @@ -425,8 +425,8 @@ static void Generate(kj::StringPtr src_prefix, const std::string method_prefix = Format() << message_namespace << "::" << method_interface.getShortDisplayName() << "::" << Cap(method_name); - const bool is_construct = method_name == "construct"; - const bool is_destroy = method_name == "destroy"; + const bool is_construct = method_name == kj::StringPtr{"construct"}; + const bool is_destroy = method_name == kj::StringPtr{"destroy"}; struct Field { @@ -465,7 +465,7 @@ static void Generate(kj::StringPtr src_prefix, field.result_is_set = true; } - if (!param && field_name == "result") { + if (!param && field_name == kj::StringPtr{"result"}) { field.retval = true; has_result = true; }