From 3fbbd84a299d087e94637f1f0ce1973136185232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Thu, 19 Mar 2026 18:36:12 +0000 Subject: [PATCH 01/13] Add standalone datadog-ipc-helper binary Extract the sidecar into a dedicated `datadog-ipc-helper` binary (new `components-rs/sidecar-bin` crate) that is located at runtime alongside the `ddtrace.so` path, removing the `php_shared_build` cfg flag and the old `php_sidecar_mockgen` crate entirely. Build script gains platform specific RUSTFLAGS for the sidecar binary: musl disables static CRT (needed for dlopen of the AppSec helper) and mac os switches fat LTO to thin LTO to work around problems with the llvm mac os linker. --- Cargo.lock | 93 ++++++------------- Cargo.toml | 4 +- Makefile | 20 +++- appsec/tests/integration/build.gradle | 5 +- compile_rust.sh | 37 ++++++-- components-rs/Cargo.toml | 2 +- components-rs/ddtrace.h | 2 - components-rs/php_sidecar_mockgen/Cargo.toml | 12 --- .../src/bin/php_sidecar_mockgen.rs | 90 ------------------ components-rs/sidecar-bin/Cargo.toml | 21 +++++ components-rs/sidecar-bin/build.rs | 53 +++++++++++ components-rs/sidecar-bin/src/main.rs | 40 ++++++++ components-rs/sidecar.h | 6 -- components-rs/sidecar.rs | 54 +++++------ config.m4 | 24 +---- ddtrace.sym | 3 - libdatadog | 2 +- 17 files changed, 217 insertions(+), 251 deletions(-) delete mode 100644 components-rs/php_sidecar_mockgen/Cargo.toml delete mode 100644 components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs create mode 100644 components-rs/sidecar-bin/Cargo.toml create mode 100644 components-rs/sidecar-bin/build.rs create mode 100644 components-rs/sidecar-bin/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index b2ea6a67fcb..14195f72f53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,7 +320,7 @@ dependencies = [ "cfg-if", "libc 0.2.177", "miniz_oxide", - "object 0.36.7", + "object", "rustc-demangle", "windows-targets 0.52.6", ] @@ -666,14 +666,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cc_utils" -version = "0.1.0" -dependencies = [ - "anyhow", - "cc", -] - [[package]] name = "cesu8" version = "1.1.0" @@ -1105,12 +1097,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "current_platform" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74858bcfe44b22016cb49337d7b6f04618c58e5dbfdef61b06b8c434324a0bc" - [[package]] name = "cxx" version = "1.0.194" @@ -1262,6 +1248,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "datadog-ipc-helper" +version = "0.0.1" +dependencies = [ + "datadog-sidecar", + "datadog-sidecar-appsec-ffi", + "libc 0.2.177", + "libdd-common-ffi", + "libdd-crashtracker", + "prctl", + "tracing", +] + [[package]] name = "datadog-ipc-macros" version = "0.0.1" @@ -1431,6 +1430,19 @@ dependencies = [ "zwohash", ] +[[package]] +name = "datadog-sidecar-appsec-ffi" +version = "0.0.1" +dependencies = [ + "datadog-remote-config", + "datadog-sidecar", + "libc 0.2.177", + "libdd-common 1.1.0", + "libdd-common-ffi", + "libdd-telemetry", + "libdd-telemetry-ffi", +] + [[package]] name = "datadog-sidecar-ffi" version = "0.0.1" @@ -3811,17 +3823,6 @@ dependencies = [ "objc2-foundation", ] -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "flate2", - "memchr", - "ruzstd", -] - [[package]] name = "object" version = "0.36.7" @@ -4092,15 +4093,6 @@ dependencies = [ "siphasher 0.3.11", ] -[[package]] -name = "php_sidecar_mockgen" -version = "0.1.0" -dependencies = [ - "cc_utils", - "current_platform", - "sidecar_mockgen", -] - [[package]] name = "pin-project" version = "1.1.8" @@ -5111,17 +5103,6 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" -[[package]] -name = "ruzstd" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a15e661f0f9dac21f3494fe5d23a6338c0ac116a2d22c2b63010acd89467ffe" -dependencies = [ - "byteorder", - "thiserror 1.0.69", - "twox-hash", -] - [[package]] name = "ryu" version = "1.0.18" @@ -5429,13 +5410,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "sidecar_mockgen" -version = "0.1.0" -dependencies = [ - "object 0.31.1", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -5530,15 +5504,12 @@ name = "spawn_worker" version = "0.0.1" dependencies = [ "anyhow", - "cc_utils", "fastrand", "io-lifetimes", "kernel32-sys", "libc 0.2.177", - "memfd", "nix 0.29.0", "rlimit", - "tempfile", "winapi 0.2.8", "windows 0.51.1", ] @@ -6368,16 +6339,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - [[package]] name = "typeable" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index c671078e95b..737b061dd0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["components-rs", "components-rs/php_sidecar_mockgen", "profiling"] +members = ["components-rs", "components-rs/sidecar-bin", "profiling"] +exclude = ["tmp"] resolver = "2" [workspace.package] @@ -25,6 +26,7 @@ codegen-units = 1 panic = "abort" inherits = "release" + [profile.profiler-release] panic = "abort" inherits = "release" diff --git a/Makefile b/Makefile index a659308034d..2a75739a7f9 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,9 @@ BENCHMARK_EXTRA ?= COMPONENTS_BUILD_DIR = $(PROJECT_ROOT)/tmp/build_components SO_FILE = $(BUILD_DIR)/modules/ddtrace.so AR_FILE = $(BUILD_DIR)/modules/ddtrace.a +SIDECAR_BIN_DEBUG = $(BUILD_DIR)/target/debug/datadog-ipc-helper +SIDECAR_BIN_RELEASE = $(BUILD_DIR)/target/tracer-release/datadog-ipc-helper +SIDECAR_BIN = $(if $(RUST_DEBUG_BUILD),$(SIDECAR_BIN_DEBUG),$(SIDECAR_BIN_RELEASE)) WALL_FLAGS = -Wall -Wextra CFLAGS ?= $(shell [ -n "${DD_TRACE_DOCKER_DEBUG}" ] && echo -O0 || echo -O2) -g $(WALL_FLAGS) LDFLAGS ?= @@ -48,7 +51,7 @@ RUN_TESTS_CMD := DD_SERVICE= DD_ENV= REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJ C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name '*.yaml' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) -RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/{cc_utils,sidecar_mockgen},libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs") +RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/cc_utils,libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs") ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) @@ -63,7 +66,9 @@ else SED_I = sed -i '' endif -all: $(BUILD_DIR)/configure $(SO_FILE) +SIDECAR_BIN_IN_MODULES = $(BUILD_DIR)/modules/datadog-ipc-helper + +all: $(BUILD_DIR)/configure $(SO_FILE) $(SIDECAR_BIN_IN_MODULES) # The following differentiation exists so we can build only (but always) the relevant files while executing tests # - when a `.phpt` changes, we only copy the file to the build dir and we DO NOT rebuild @@ -104,8 +109,6 @@ $(BUILD_DIR)/%: % JUNIT_RESULTS_DIR := $(shell pwd) -all: $(BUILD_DIR)/configure $(SO_FILE) - $(BUILD_DIR)/configure: $(M4_FILES) $(BUILD_DIR)/ddtrace.sym $(BUILD_DIR)/VERSION $(Q) (cd $(BUILD_DIR); phpize && $(SED_I) 's/\/FAILED/\/\\bFAILED/' $(BUILD_DIR)/run-tests.php) # Fix PHP 5.4 exit code bug when running selected tests (FAILED vs XFAILED) @@ -116,16 +119,23 @@ $(BUILD_DIR)/run-tests.php: $(if $(ASSUME_COMPILED),, $(BUILD_DIR)/configure) $(BUILD_DIR)/Makefile: $(BUILD_DIR)/configure $(Q) (cd $(BUILD_DIR); $(if $(ASAN),CFLAGS="${CFLAGS} -DZEND_TRACK_ARENA_ALLOC") ./configure --$(if $(RUST_DEBUG_BUILD),enable,disable)-ddtrace-rust-debug $(if $(ASAN), --enable-ddtrace-sanitize) $(EXTRA_CONFIGURE_OPTIONS)) + $(SO_FILE): $(if $(ASSUME_COMPILED),, $(ALL_OBJECT_FILES) $(BUILD_DIR)/compile_rust.sh) $(if $(ASSUME_COMPILED),,$(Q) $(MAKE) -C $(BUILD_DIR) -j) +$(SIDECAR_BIN_IN_MODULES): $(SO_FILE) + $(Q) cp $(SIDECAR_BIN) $@ + $(AR_FILE): $(ALL_OBJECT_FILES) $(Q) $(MAKE) -C $(BUILD_DIR) -j ./modules/ddtrace.a all $(PHP_EXTENSION_DIR)/ddtrace.so: $(SO_FILE) $(Q) $(SUDO) $(if $(ASSUME_COMPILED),cp $(BUILD_DIR)/modules/ddtrace.so $(PHP_EXTENSION_DIR)/ddtrace.so,$(MAKE) -C $(BUILD_DIR) install) -install: $(PHP_EXTENSION_DIR)/ddtrace.so +$(PHP_EXTENSION_DIR)/datadog-ipc-helper: $(SIDECAR_BIN) + $(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper + +install: $(PHP_EXTENSION_DIR)/ddtrace.so $(PHP_EXTENSION_DIR)/datadog-ipc-helper set_static_option: $(eval EXTRA_CONFIGURE_OPTIONS := --enable-ddtrace-rust-library-split) diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 3235b4b1c27..09e424f4176 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -345,13 +345,14 @@ def buildTracerTask = { String version, String variant, altBaseTag = null -> ], outputs: [ volume: 'php-tracer', - files: ['build_extension/modules/ddtrace.so'], + files: ['build_extension/modules/ddtrace.so', + 'build_extension/modules/datadog-ipc-helper'], ], command: [ '-e', '-c', ''' cd /project - PHPRC= RUST_DEBUG_BUILD=1 make /project/tmp/build_extension/modules/ddtrace.so + PHPRC= RUST_DEBUG_BUILD=1 make ''' ] ) diff --git a/compile_rust.sh b/compile_rust.sh index fe62f8c304e..d6d540c220a 100755 --- a/compile_rust.sh +++ b/compile_rust.sh @@ -3,17 +3,13 @@ cd components-rs RUSTFLAGS="${RUSTFLAGS:-} --cfg tokio_unstable" -if test -n "$SHARED"; then - RUSTFLAGS="$RUSTFLAGS --cfg php_shared_build" -fi - case "${host_os}" in darwin*) RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"; ;; esac -set -x +set -ex if test -n "$COMPILE_ASAN"; then # We need -lresolv due to https://github.com/llvm/llvm-project/issues/59007 @@ -21,4 +17,33 @@ if test -n "$COMPILE_ASAN"; then export CFLAGS="$LDFLAGS -fno-omit-frame-pointer" # the cc buildtools will only pick up CFLAGS it seems fi -SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$RUSTFLAGS" RUSTC_BOOTSTRAP=1 "${DDTRACE_CARGO:-cargo}" build $(test "${PROFILE:-debug}" = "debug" || echo --profile "$PROFILE") "$@" +# Choose the cargo profile. +if test "${PROFILE:-debug}" = "debug"; then + CARGO_PROFILE_ARG="" +else + CARGO_PROFILE_ARG="--profile $PROFILE" +fi + +CARGO_TARGET_DIR="${CARGO_TARGET_DIR:?CARGO_TARGET_DIR must be set}" + +# Sidecar-specific RUSTFLAGS. +# - musl: disable static CRT so dlopen works for loading the AppSec helper. +# - macOS: override fat LTO (from tracer-release) to thin LTO, because the +# Xcode system linker ships an older LLVM that cannot parse the bitcode +# produced by the Rust toolchain's fat-LTO mode. +SIDECAR_RUSTFLAGS="$RUSTFLAGS" +case "${host_os}" in + *musl*) + SIDECAR_RUSTFLAGS="$SIDECAR_RUSTFLAGS -C target-feature=-crt-static" + ;; + darwin*) + SIDECAR_RUSTFLAGS="$SIDECAR_RUSTFLAGS -C lto=thin" + ;; +esac + +SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$RUSTFLAGS" RUSTC_BOOTSTRAP=1 \ + "${DDTRACE_CARGO:-cargo}" build -p ddtrace-php $CARGO_PROFILE_ARG "$@" + +SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$SIDECAR_RUSTFLAGS" RUSTC_BOOTSTRAP=1 \ + "${DDTRACE_CARGO:-cargo}" build -p datadog-ipc-helper $CARGO_PROFILE_ARG "$@" + diff --git a/components-rs/Cargo.toml b/components-rs/Cargo.toml index 90b6851e1e4..453ea17af02 100644 --- a/components-rs/Cargo.toml +++ b/components-rs/Cargo.toml @@ -55,4 +55,4 @@ hashbrown = "0.15" cbindgen = "0.27" [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(php_shared_build)'] } +unexpected_cfgs = { level = "warn", check-cfg = [] } diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index c0bab2a9eab..74bdb778fb3 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -17,8 +17,6 @@ extern ddog_VecRemoteConfigProduct DDTRACE_REMOTE_CONFIG_PRODUCTS; extern ddog_VecRemoteConfigCapabilities DDTRACE_REMOTE_CONFIG_CAPABILITIES; -extern const uint8_t *DDOG_PHP_FUNCTION; - extern struct ddog_SidecarTransport *ddtrace_sidecar; /** diff --git a/components-rs/php_sidecar_mockgen/Cargo.toml b/components-rs/php_sidecar_mockgen/Cargo.toml deleted file mode 100644 index 4b010850c28..00000000000 --- a/components-rs/php_sidecar_mockgen/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -edition = "2021" -name = "php_sidecar_mockgen" -version = "0.1.0" - -[dependencies] -cc_utils = { path = "../../libdatadog/tools/cc_utils" } -current_platform = "0.2.0" -sidecar_mockgen = { path = "../../libdatadog/tools/sidecar_mockgen" } - -[[bin]] -name = "php_sidecar_mockgen" diff --git a/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs b/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs deleted file mode 100644 index a31c034e07e..00000000000 --- a/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present Datadog, Inc. - -pub use cc_utils::cc; -pub use sidecar_mockgen::generate_mock_symbols; -use std::path::Path; -use std::{env, fs, process}; - -fn main() { - // This script is necessary to avoid the linker puking when the sidecar tries to load our ddtrace.so - // As php itself is not available within the sidecar, it needs to make sure that all symbols ddtrace.so depends on, are available. - // The mock_generator takes care of generating these symbols. - let args: Vec<_> = env::args_os().collect(); - if args.len() < 3 { - eprintln!( - "Needs at least 3 args: the output file, the shared object file followed by at least one object file" - ); - process::exit(1); - } - - let output_path = Path::new(&args[1]); - let binary_path = Path::new(&args[2]); - let object_paths: Vec<_> = args.iter().skip(3).map(Path::new).collect(); - let mock_symbols = match generate_mock_symbols(binary_path, object_paths.as_slice()) { - Ok(symbols) => symbols, - Err(err) => { - eprintln!("Failed generating mock_php_syms.c: {}", err); - process::exit(1); - } - }; - - if fs::read("mock_php_syms.c") - .ok() - .map(|contents| contents == mock_symbols.as_str().as_bytes()) - != Some(true) - { - if let Err(err) = fs::write("mock_php_syms.c", mock_symbols) { - eprintln!("Failed generating mock_php_syms.c: {}", err); - process::exit(1); - } - } - - let source_modified = fs::metadata("mock_php_syms.c").unwrap().modified().unwrap(); - if fs::metadata("mock_php.shared_lib").map_or(true, |m| m.modified().unwrap() < source_modified) { - env::set_var("OPT_LEVEL", "2"); - - let mut cc_build = cc::Build::new(); - - // The 'host' and 'target' options are required to compile. - // They can be provided using the HOST and TARGET environment variables. - // On Linux, these environment variables can be empty strings, but it's not the case on macOS. - let host = std::env::var("HOST").unwrap_or("".to_string()); - if host == "" { - cc_build.host(current_platform::CURRENT_PLATFORM); - } - let target = std::env::var("TARGET").unwrap_or("".to_string()); - if target == "" { - cc_build.target(current_platform::CURRENT_PLATFORM); - } - - cc_utils::ImprovedBuild::new() - .set_cc_builder(cc_build) - .file("mock_php_syms.c") - .link_dynamically("dl") - .warnings(true) - .warnings_into_errors(true) - .emit_rerun_if_env_changed(false) - .try_compile_shared_lib("mock_php.shared_lib") - .unwrap(); - - let bin = match fs::read("mock_php.shared_lib") { - Ok(bin) => bin, - Err(err) => { - eprintln!("Failed opening generated mock_php.shared_lib: {}", err); - process::exit(1); - } - }; - - let comma_separated = bin.iter().map(|byte| format!("{byte:#X}")).collect::>().join(","); - let out = format!(r#" - const unsigned char DDTRACE_MOCK_PHP[] = {{{comma_separated}}}; - const void *DDTRACE_MOCK_PHP_SIZE = (void *) sizeof(DDTRACE_MOCK_PHP); - "#); - - if let Err(err) = fs::write(output_path, out) { - eprintln!("Failed generating {:?}: {}", output_path, err); - process::exit(1); - } - } -} diff --git a/components-rs/sidecar-bin/Cargo.toml b/components-rs/sidecar-bin/Cargo.toml new file mode 100644 index 00000000000..7945446f824 --- /dev/null +++ b/components-rs/sidecar-bin/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "datadog-ipc-helper" +version = "0.0.1" +edition = "2021" + +[[bin]] +name = "datadog-ipc-helper" +path = "src/main.rs" + +[dependencies] +datadog-sidecar = { path = "../../libdatadog/datadog-sidecar" } +datadog-sidecar-appsec-ffi = { path = "../../libdatadog/datadog-sidecar-appsec-ffi" } +libdd-common-ffi = { path = "../../libdatadog/libdd-common-ffi", default-features = false } +libdd-crashtracker = { path = "../../libdatadog/libdd-crashtracker" } +libc = "0.2" +tracing = { version = "0.1", default-features = false, features = ["std"] } + + + +[target.'cfg(target_os = "linux")'.dependencies] +prctl = "1.0" diff --git a/components-rs/sidecar-bin/build.rs b/components-rs/sidecar-bin/build.rs new file mode 100644 index 00000000000..4b7710918bc --- /dev/null +++ b/components-rs/sidecar-bin/build.rs @@ -0,0 +1,53 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +/// Symbols the AppSec helper resolves via dlsym(RTLD_DEFAULT, …). +/// These are the only symbols that need to be in the binary's dynamic symbol table. +const EXPORTED_SYMBOLS: &[&str] = &[ + "ddog_Error_drop", + "ddog_Error_message", + "ddog_remote_config_path", + "ddog_remote_config_path_free", + "ddog_set_rc_notify_fn", + "ddog_sidecar_connect", + "ddog_sidecar_enqueue_telemetry_log", + "ddog_sidecar_enqueue_telemetry_metric", + "ddog_sidecar_enqueue_telemetry_point", + "ddog_sidecar_ping", + "ddog_sidecar_transport_drop", +]; + +fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set"); + + match target_os.as_str() { + "linux" => { + // GNU ld --dynamic-list: { sym; }; syntax, no leading underscore. + let mut content = "{\n".to_owned(); + for sym in EXPORTED_SYMBOLS { + content.push_str(&format!(" {sym};\n")); + } + content.push_str("};\n"); + + let path = std::path::Path::new(&out_dir).join("datadog-ipc-helper.sym"); + std::fs::write(&path, content).expect("failed to write dynamic-list file"); + println!("cargo:rustc-link-arg=-Wl,--dynamic-list={}", path.display()); + } + "macos" => { + // macOS ld64 -exported_symbols_list: one symbol per line, with leading _. + let content: String = EXPORTED_SYMBOLS + .iter() + .map(|s| format!("_{s}\n")) + .collect(); + + let path = std::path::Path::new(&out_dir).join("datadog-ipc-helper-exports.txt"); + std::fs::write(&path, content).expect("failed to write exported_symbols_list file"); + println!( + "cargo:rustc-link-arg=-Wl,-exported_symbols_list,{}", + path.display() + ); + } + _ => {} + } +} diff --git a/components-rs/sidecar-bin/src/main.rs b/components-rs/sidecar-bin/src/main.rs new file mode 100644 index 00000000000..dcbdbd6e17a --- /dev/null +++ b/components-rs/sidecar-bin/src/main.rs @@ -0,0 +1,40 @@ +// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +// Force-link the appsec FFI symbols so dlsym(RTLD_DEFAULT, …) can find them. +// The `use` is sufficient to prevent dead-code elimination; the functions +// themselves are `#[no_mangle]` and therefore kept by the linker. +use datadog_sidecar_appsec_ffi as _; +use libdd_common_ffi as _; // provides ddog_Error_drop / ddog_Error_message + +fn main() { + let args: Vec = std::env::args().collect(); + match args.get(1).map(|s| s.as_str()) { + Some("ipc-helper") => { + #[cfg(unix)] + datadog_sidecar::run_daemon_from_passed_fd(); + #[cfg(windows)] + datadog_sidecar::run_daemon_from_passed_handle(); + } + Some("crashtracker") => crashtracker_receiver_main(), + Some(other) => { + eprintln!("datadog-ipc-helper: unknown subcommand '{other}'"); + eprintln!("usage: datadog-ipc-helper "); + unsafe { libc::exit(1) }; + } + None => { + eprintln!("datadog-ipc-helper: subcommand required"); + eprintln!("usage: datadog-ipc-helper "); + unsafe { libc::exit(1) }; + } + } +} + +fn crashtracker_receiver_main() { + unsafe { + if let Err(e) = libdd_crashtracker::receiver_entry_point_stdin() { + eprintln!("{e}"); + libc::exit(1); + } + } +} diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 2d694f91507..66f2d0a039c 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -86,12 +86,6 @@ void ddog_remote_config_reader_drop(struct ddog_RemoteConfigReader*); void ddog_sidecar_transport_drop(struct ddog_SidecarTransport*); -/** - * # Safety - * Caller must ensure the process is safe to fork, at the time when this method is called - */ -ddog_MaybeError ddog_sidecar_connect(struct ddog_SidecarTransport **connection); - ddog_MaybeError ddog_sidecar_ping(struct ddog_SidecarTransport **transport); ddog_MaybeError ddog_sidecar_flush_traces(struct ddog_SidecarTransport **transport); diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index 0b94deeeab9..0086750965d 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -18,45 +18,33 @@ use libdd_common::Endpoint; use libdd_common_ffi::slice::AsBytes; use libdd_common_ffi::{CharSlice, self as ffi, MaybeError}; use libdd_telemetry_ffi::try_c; -#[cfg(any(windows, php_shared_build))] -use spawn_worker::LibDependency; -#[cfg(windows)] -use spawn_worker::get_trampoline_target_data; - - -#[cfg(php_shared_build)] -extern "C" { - #[linkage="extern_weak"] - static DDTRACE_MOCK_PHP: *mut u8; - #[linkage="extern_weak"] - static DDTRACE_MOCK_PHP_SIZE: *mut usize; -} -#[cfg(php_shared_build)] -fn run_sidecar(mut cfg: config::Config) -> anyhow::Result { - if !unsafe { DDTRACE_MOCK_PHP_SIZE }.is_null() { - let mock = unsafe { std::slice::from_raw_parts(DDTRACE_MOCK_PHP, *DDTRACE_MOCK_PHP_SIZE) }; - cfg.library_dependencies - .push(LibDependency::Binary(mock)); - } - datadog_sidecar::start_or_connect_to_sidecar(cfg) +/// Locate the `datadog-ipc-helper` binary in the same directory as `ddtrace.so`. +#[cfg(unix)] +fn find_sidecar_binary() -> anyhow::Result { + let (ddtrace_path, _) = unsafe { + spawn_worker::get_dl_path_raw(ddog_sidecar_connect_php as *const libc::c_void) + }; + let ddtrace_path = ddtrace_path.ok_or_else(|| { + anyhow::format_err!("could not resolve ddtrace.so path via dladdr") + })?; + let dir = std::path::PathBuf::from( + ddtrace_path + .to_str() + .map_err(|_| anyhow::format_err!("ddtrace.so path is not valid UTF-8"))?, + ); + let dir = dir + .parent() + .ok_or_else(|| anyhow::format_err!("ddtrace.so has no parent directory"))?; + Ok(dir.join("datadog-ipc-helper")) } -#[cfg(not(any(windows, php_shared_build)))] +#[cfg(unix)] fn run_sidecar(cfg: config::Config) -> anyhow::Result { - datadog_sidecar::start_or_connect_to_sidecar(cfg) + let binary_path = find_sidecar_binary()?; + datadog_sidecar::start_or_connect_with_exec_binary(binary_path, cfg) } -#[no_mangle] -#[cfg(windows)] -pub static mut DDOG_PHP_FUNCTION: *const u8 = std::ptr::null(); - -#[cfg(windows)] -fn run_sidecar(mut cfg: config::Config) -> anyhow::Result { - let php_dll = get_trampoline_target_data(unsafe { DDOG_PHP_FUNCTION })?; - cfg.library_dependencies.push(LibDependency::Path(php_dll.into())); - datadog_sidecar::start_or_connect_to_sidecar(cfg) -} lazy_static! { static ref APPSEC_CONFIG: Mutex> = Mutex::new(None); diff --git a/config.m4 b/config.m4 index 9eb5aaca874..571d646efdb 100644 --- a/config.m4 +++ b/config.m4 @@ -7,8 +7,6 @@ PHP_ARG_ENABLE(ddtrace-sanitize, whether to enable AddressSanitizer for ddtrace, PHP_ARG_WITH(ddtrace-rust-library, the rust library is located; i.e. to compile without cargo, [ --with-ddtrace-rust-library Location to rust library for linking against], -, will be compiled) -PHP_ARG_WITH(ddtrace-sidecar-mockgen, binary to generate mock_php.c, - [ --with-ddtrace-sidecar-library Location to cargo binary produced by components-rs/php_sidecar_mockgen], -, will be compiled) PHP_ARG_WITH(ddtrace-cargo, where cargo is located for rust code compilation, [ --with-ddtrace-cargo Location to cargo binary for rust compilation], cargo, not found) @@ -351,7 +349,7 @@ EOT pushdef([PHP_GEN_GLOBAL_MAKEFILE], [ popdef([PHP_GEN_GLOBAL_MAKEFILE]) PHP_GEN_GLOBAL_MAKEFILE - [sed -i $({ sed --version 2>&1 || echo ''; } | grep GNU >/dev/null || echo "''") -e '/.*\.[ao] /{s/| xargs rm -f/! -path ".\/target*\/*" | xargs rm -f/'$'\n}' -e '/^distclean:/a\'$'\n\t''rm -rf target/ target_mockgen/' Makefile] + [sed -i $({ sed --version 2>&1 || echo ''; } | grep GNU >/dev/null || echo "''") -e '/.*\.[ao] /{s/| xargs rm -f/! -path ".\/target*\/*" | xargs rm -f/'$'\n}' -e '/^distclean:/a\'$'\n\t''rm -rf target/' Makefile] DDTRACE_GEN_GLOBAL_MAKEFILE_WRAP ]) ]) @@ -377,26 +375,6 @@ $ddtrace_rust_lib: $( (find "$ext_srcdir/components-rs" -name "*.rs" -o -name "C EOT fi - if test "$ext_shared" = "yes"; then - all_object_files=$(for src in $DD_TRACE_PHP_SOURCES $ZAI_SOURCES; do printf ' %s' "${src%?}lo"; done) - all_object_files_absolute=$(for src in $DD_TRACE_PHP_SOURCES $ZAI_SOURCES; do printf ' $(builddir)/%s' "$(dirname "$src")/$objdir/$(basename "${src%?}o")"; done) - php_binary=$("$PHP_CONFIG" --php-binary) - if test "$PHP_DDTRACE_SIDECAR_MOCKGEN" != "-"; then - ddtrace_mockgen_invocation="HOST= TARGET= $PHP_DDTRACE_SIDECAR_MOCKGEN" - else - ddtrace_mockgen_invocation="cd \"$ext_srcdir/components-rs/php_sidecar_mockgen\"; HOST= TARGET= CARGO_TARGET_DIR=\$(builddir)/target_mockgen/ \$(DDTRACE_CARGO) run" - fi - cat <> Makefile.fragments - -/\$(builddir)/components-rs/mock_php.c: $all_object_files - ($ddtrace_mockgen_invocation \$(builddir)/components-rs/mock_php.c $php_binary $all_object_files_absolute) - -# avoid cargo running simultaneously for libddtrace_php and php_sidecar_mockgen -/\$(builddir)/components-rs/mock_php.c: | \$(filter-out \$(builddir)/components-rs/mock_php.lo,\$(shared_objects_ddtrace)) -EOT - - PHP_ADD_SOURCES_X("/$ext_dir", "\$(builddir)/components-rs/mock_php.c", $ac_extra, shared_objects_ddtrace, yes) - fi if test "$ext_shared" = "shared" || test "$ext_shared" = "yes"; then shared_objects_ddtrace="$ddtrace_rust_lib $shared_objects_ddtrace" diff --git a/ddtrace.sym b/ddtrace.sym index 854f3f5bf7b..208298560db 100644 --- a/ddtrace.sym +++ b/ddtrace.sym @@ -22,8 +22,6 @@ ddog_remote_config_reader_for_path ddog_remote_config_read ddog_remote_config_reader_drop get_module -ddog_crashtracker_entry_point -ddog_daemon_entry_point ddog_set_rc_notify_fn ddog_remote_config_path ddog_remote_config_path_free @@ -40,6 +38,5 @@ ddog_library_configurator_drop ddog_sidecar_enqueue_telemetry_log ddog_sidecar_enqueue_telemetry_point ddog_sidecar_enqueue_telemetry_metric -ddog_sidecar_connect ddog_sidecar_ping ddog_sidecar_transport_drop diff --git a/libdatadog b/libdatadog index a0cef26b024..b5c1ed41cfd 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit a0cef26b0240f19dd994d471d5679e8c426adfc8 +Subproject commit b5c1ed41cfd6c20b95d2b34c89d1fb554e1c0250 From f85a1a0217ffbf8b9df55ae75b8dca641ccb0a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 10:36:28 +0000 Subject: [PATCH 02/13] dlsym to load sidecar symbols not necessary any+ --- Makefile | 9 +- appsec/helper-rust/build.rs | 35 +-- .../scripts/generate-sidecar-ffi.sh | 10 +- appsec/helper-rust/src/ffi.rs | 251 +++++------------- appsec/helper-rust/src/ffi/sidecar_ffi.rs | 69 +++-- appsec/helper-rust/src/rc_notify.rs | 85 ++---- appsec/helper-rust/src/telemetry/sidecar.rs | 96 +------ appsec/helper-rust/test_sidecar_lib.c | 3 - appsec/src/helper/ffi.hpp | 90 +------ appsec/src/helper/remote_config/client.cpp | 15 +- appsec/src/helper/runner.cpp | 19 +- appsec/src/helper/service.cpp | 50 ++-- appsec/tests/helper/service_test.cpp | 18 ++ appsec/tests/integration/build.gradle | 47 +--- components-rs/common.h | 1 + components-rs/sidecar-appsec.h | 76 ++++++ components-rs/sidecar-bin/build.rs | 3 + libdatadog | 2 +- 18 files changed, 292 insertions(+), 587 deletions(-) delete mode 100644 appsec/helper-rust/test_sidecar_lib.c create mode 100644 components-rs/sidecar-appsec.h diff --git a/Makefile b/Makefile index 2a75739a7f9..aa79be95d2e 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ RUN_TESTS_CMD := DD_SERVICE= DD_ENV= REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJ C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name '*.yaml' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) -RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/cc_utils,libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs") +RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-appsec-ffi,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/cc_utils,libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs") ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) @@ -426,7 +426,7 @@ clang_format_fix: cbindgen: remove_cbindgen generate_cbindgen remove_cbindgen: - rm -f components-rs/ddtrace.h components-rs/live-debugger.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h components-rs/library-config.h + rm -f components-rs/ddtrace.h components-rs/live-debugger.h components-rs/telemetry.h components-rs/sidecar.h components-rs/sidecar-appsec.h components-rs/common.h components-rs/crashtracker.h components-rs/library-config.h generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h components-rs/live-debugger.h components-rs/telemetry.h components-rs/sidecar.h components-rs/common.h components-rs/crashtracker.h components-rs/library-config.h ( \ @@ -446,6 +446,9 @@ generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h componen $(command rustup && echo run nightly --) cbindgen --crate datadog-sidecar-ffi \ --config datadog-sidecar-ffi/cbindgen.toml \ --output $(PROJECT_ROOT)/components-rs/sidecar.h; \ + $(command rustup && echo run nightly --) cbindgen --crate datadog-sidecar-appsec-ffi \ + --config datadog-sidecar-appsec-ffi/cbindgen.toml \ + --output $(PROJECT_ROOT)/components-rs/sidecar-appsec.h; \ $(command rustup && echo run nightly --) cbindgen --crate libdd-crashtracker-ffi \ --config libdd-crashtracker-ffi/cbindgen.toml \ --output $(PROJECT_ROOT)/components-rs/crashtracker.h; \ @@ -456,7 +459,7 @@ generate_cbindgen: cbindgen_binary # Regenerate components-rs/ddtrace.h componen mkdir -pv "$(BUILD_DIR)"; \ export CARGO_TARGET_DIR="$(BUILD_DIR)/target"; \ fi; \ - cargo run -p tools --bin dedup_headers -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/live-debugger.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h $(PROJECT_ROOT)/components-rs/crashtracker.h $(PROJECT_ROOT)/components-rs/library-config.h \ + cargo run -p tools --bin dedup_headers -- $(PROJECT_ROOT)/components-rs/common.h $(PROJECT_ROOT)/components-rs/ddtrace.h $(PROJECT_ROOT)/components-rs/live-debugger.h $(PROJECT_ROOT)/components-rs/telemetry.h $(PROJECT_ROOT)/components-rs/sidecar.h $(PROJECT_ROOT)/components-rs/sidecar-appsec.h $(PROJECT_ROOT)/components-rs/crashtracker.h $(PROJECT_ROOT)/components-rs/library-config.h \ ) cbindgen_binary: diff --git a/appsec/helper-rust/build.rs b/appsec/helper-rust/build.rs index d6ef8abbec2..a1b9e1d6efa 100644 --- a/appsec/helper-rust/build.rs +++ b/appsec/helper-rust/build.rs @@ -52,8 +52,13 @@ fn main() { // in the same directory as the binary/library if target.contains("linux") { println!("cargo::rustc-link-arg=-Wl,-rpath,$ORIGIN"); + // Allow symbols to be resolved from the parent process at dlopen time. + println!("cargo::rustc-link-arg=-Wl,--allow-shlib-undefined"); } else if target.contains("darwin") || target.contains("apple") { println!("cargo::rustc-link-arg=-Wl,-rpath,@loader_path"); + // Allow undefined symbols to be resolved at dlopen time from the parent process. + println!("cargo::rustc-link-arg=-undefined"); + println!("cargo::rustc-link-arg=dynamic_lookup"); } // If LIBDDWAF_PREFIX is set, add that library path to rpath as well @@ -66,7 +71,6 @@ fn main() { println!("cargo::rerun-if-env-changed=LIBDDWAF_PREFIX"); set_ddappsec_version(); - build_test_sidecar_lib(); } fn set_ddappsec_version() { @@ -81,32 +85,3 @@ fn set_ddappsec_version() { println!("cargo::rustc-env=DDAPPSEC_VERSION={}", version); println!("cargo::rerun-if-changed={}", version_path.display()); } - -fn build_test_sidecar_lib() { - let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set"); - let target = env::var("TARGET").expect("TARGET not set"); - let host = env::var("HOST").expect("HOST not set"); - - let (lib_name, shared_flag) = if target.contains("darwin") || target.contains("apple") { - ("libtest_sidecar.dylib", "-dynamiclib") - } else { - ("libtest_sidecar.so", "-shared") - }; - - let src = "test_sidecar_lib.c"; - let out = PathBuf::from(&out_dir).join(lib_name); - - let status = cc::Build::new() - .target(&target) - .host(&host) - .get_compiler() - .to_command() - .args([shared_flag, "-fPIC", "-o"]) - .arg(&out) - .arg(src) - .status() - .expect("Failed to run C compiler for test fixture"); - - assert!(status.success(), "Failed to compile test sidecar library"); - println!("cargo::rerun-if-changed={}", src); -} diff --git a/appsec/helper-rust/scripts/generate-sidecar-ffi.sh b/appsec/helper-rust/scripts/generate-sidecar-ffi.sh index 8a7f499da58..97efa49e9de 100755 --- a/appsec/helper-rust/scripts/generate-sidecar-ffi.sh +++ b/appsec/helper-rust/scripts/generate-sidecar-ffi.sh @@ -14,7 +14,7 @@ OUTPUT_FILE="$PROJECT_DIR/src/ffi/sidecar_ffi.rs" cd "$COMPONENTS_RS_DIR" -bindgen sidecar.h \ +bindgen sidecar-appsec.h \ --allowlist-function 'ddog_sidecar_enqueue_telemetry_log' \ --allowlist-function 'ddog_sidecar_enqueue_telemetry_point' \ --allowlist-function 'ddog_sidecar_enqueue_telemetry_metric' \ @@ -24,6 +24,9 @@ bindgen sidecar.h \ --allowlist-function 'ddog_Error_drop' \ --allowlist-function 'ddog_Error_message' \ --allowlist-function 'ddog_MaybeError_drop' \ + --allowlist-function 'ddog_remote_config_path' \ + --allowlist-function 'ddog_remote_config_path_free' \ + --allowlist-function 'ddog_set_rc_notify_fn' \ --allowlist-type 'ddog_SidecarTransport' \ --allowlist-type 'ddog_LogLevel' \ --allowlist-type 'ddog_CharSlice' \ @@ -34,14 +37,15 @@ bindgen sidecar.h \ --allowlist-type 'ddog_Error' \ --allowlist-type 'ddog_Vec_U8' \ --allowlist-type 'ddog_MetricNamespace' \ + --allowlist-type 'ddog_MetricType' \ --no-layout-tests \ --no-doc-comments \ --use-core \ - --raw-line '//! Auto-generated FFI bindings from components-rs/sidecar.h' \ + --raw-line '//! Auto-generated FFI bindings from components-rs/sidecar-appsec.h' \ --raw-line '//!' \ --raw-line '//! Regenerate with: ./scripts/generate-sidecar-ffi.sh' \ --raw-line '//!' \ - --raw-line '//! Only includes types/functions needed for helper-rust as telemetry sender.' \ + --raw-line '//! Only includes types/functions needed by helper-rust.' \ --raw-line '#![allow(non_camel_case_types, non_upper_case_globals, dead_code)]' \ --output "$OUTPUT_FILE" \ -- -I. diff --git a/appsec/helper-rust/src/ffi.rs b/appsec/helper-rust/src/ffi.rs index 12a8bfadf18..98ef6fa253a 100644 --- a/appsec/helper-rust/src/ffi.rs +++ b/appsec/helper-rust/src/ffi.rs @@ -1,198 +1,83 @@ -use std::{ - cell::UnsafeCell, - ffi::CStr, - marker::PhantomData, - ops::Deref, - sync::atomic::{AtomicBool, Ordering}, -}; - -use crate::client::log::error; - pub mod sidecar_ffi; -#[macro_export] -macro_rules! sidecar_symbol { - // form 1: inline function signature - ( - static $static:ident = - fn($($arg:ty),* $(, ...)? $(,)?) $(-> $ret:ty)? : - $name:ident - ) => { - type $name = unsafe extern "C" fn( - $($arg),* - $(, ...)? - ) $(-> $ret)?; - - static $static: SidecarSymbol<$name> = - SidecarSymbol::new(::std::concat!(::std::stringify!($name), "\0")); - - const _: () = { - let _: $name = $name; - }; - }; - - // form 2: type alias - ( - static $static:ident = - $ty:ty : - $name:ident - ) => { - static $static: SidecarSymbol<$ty> = - SidecarSymbol::new(unsafe {::std::ffi::CStr::from_bytes_with_nul_unchecked(::std::concat!(::std::stringify!($name), "\0").as_bytes())}); - - const _: () = { - let _: $ty = $name; - }; - }; -} - -pub struct SidecarSymbol { - // In order to implement Deref, we need to have a sort of rvalue for the function - // So do not use an AtomicPtr that we check for null to determine if we're initialized - func_ptr: UnsafeCell<*mut libc::c_void>, - initialized: AtomicBool, - name: &'static CStr, - _phantom: PhantomData, -} -impl SidecarSymbol { - pub const fn new(name: &'static CStr) -> Self { - assert!( - std::mem::size_of::() == std::mem::size_of::<*mut libc::c_void>(), - "Func must be pointer-sized" - ); - Self { - func_ptr: UnsafeCell::new(std::ptr::null_mut()), - initialized: AtomicBool::new(false), - name, - _phantom: PhantomData, - } +// Stub implementations of the sidecar symbols for `cargo test`. +// In production the real symbols are provided by datadog-ipc-helper at +// dlopen time. The cdylib has no stubs (--allow-shlib-undefined covers it); +// the test executable needs concrete definitions at link time because lld on +// musl does not allow undefined symbols in executables. +#[cfg(test)] +mod test_stubs { + use super::sidecar_ffi::*; + + #[no_mangle] + extern "C" fn ddog_Error_drop(_: *mut ddog_Error) {} + #[no_mangle] + extern "C" fn ddog_Error_message(_: *const ddog_Error) -> ddog_CharSlice { + ddog_CharSlice { ptr: std::ptr::null(), len: 0 } } - - pub fn resolve(&self) -> anyhow::Result<()> { - if self.initialized.load(Ordering::Acquire) { - return Ok(()); - } - - let func_ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr()) }; - if func_ptr.is_null() { - return Err(anyhow::anyhow!("Failed to resolve symbol: {:?}", self.name)); - } - unsafe { std::ptr::write(self.func_ptr.get(), func_ptr) }; - self.initialized.store(true, Ordering::Release); - Ok(()) + #[no_mangle] + extern "C" fn ddog_MaybeError_drop(_: ddog_MaybeError) {} + #[no_mangle] + extern "C" fn ddog_set_rc_notify_fn(_: ddog_InProcNotifyFn) {} + #[no_mangle] + unsafe extern "C" fn ddog_remote_config_path( + _: *const ddog_ConfigInvariants, + _: *const ddog_Arc_Target, + ) -> *mut std::ffi::c_char { + std::ptr::null_mut() } - - fn get(&self) -> Option<&Func> { - if !self.initialized.load(Ordering::Acquire) { - None - } else { - Some(unsafe { &*self.func_ptr.get().cast() }) + #[no_mangle] + extern "C" fn ddog_remote_config_path_free(_: *mut std::ffi::c_char) {} + #[no_mangle] + unsafe extern "C" fn ddog_sidecar_connect( + _: *mut *mut ddog_SidecarTransport, + ) -> ddog_MaybeError { + ddog_MaybeError { + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, + __bindgen_anon_1: unsafe { std::mem::zeroed() }, } } -} -unsafe impl Sync for SidecarSymbol {} -impl Deref for SidecarSymbol { - type Target = Func; - - fn deref(&self) -> &Self::Target { - match self.get() { - Some(func) => func, - None => { - error!("Symbol is not resolved, will panic: {:?}", self); - panic!("Symbol is not resolved: {:?}", self.name); - } + #[no_mangle] + extern "C" fn ddog_sidecar_transport_drop(_: *mut ddog_SidecarTransport) {} + #[no_mangle] + unsafe extern "C" fn ddog_sidecar_ping( + _: *mut *mut ddog_SidecarTransport, + ) -> ddog_MaybeError { + ddog_MaybeError { + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, + __bindgen_anon_1: unsafe { std::mem::zeroed() }, } } -} -impl std::fmt::Debug for SidecarSymbol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let initialized = self.initialized.load(Ordering::Acquire); - let mut ds = f.debug_struct("SidecarSymbol"); - ds.field("name", &self.name) - .field("name", &self.name) - .field("initialized", &initialized); - if initialized { - ds.field("func_ptr", &self.func_ptr.get()); + #[no_mangle] + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_log( + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, + _: ddog_CharSlice, _: ddog_LogLevel, _: ddog_CharSlice, + _: *mut ddog_CharSlice, _: *mut ddog_CharSlice, _: bool, + ) -> ddog_MaybeError { + ddog_MaybeError { + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, + __bindgen_anon_1: unsafe { std::mem::zeroed() }, } - ds.finish() } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::ffi::CStr; - use std::path::PathBuf; - - fn load_library(path: &std::path::Path) -> *mut libc::c_void { - let path_cstr = std::ffi::CString::new(path.to_str().unwrap()).expect("Invalid path"); - - let handle = - unsafe { libc::dlopen(path_cstr.as_ptr(), libc::RTLD_NOW | libc::RTLD_GLOBAL) }; - - if handle.is_null() { - let error = unsafe { libc::dlerror() }; - if !error.is_null() { - let error_str = unsafe { CStr::from_ptr(error) }; - panic!("dlopen failed: {:?}", error_str); - } - panic!("dlopen failed with unknown error"); + #[no_mangle] + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_point( + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, + _: ddog_CharSlice, _: f64, _: *mut ddog_CharSlice, + ) -> ddog_MaybeError { + ddog_MaybeError { + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, + __bindgen_anon_1: unsafe { std::mem::zeroed() }, } - - handle } - - fn unload_library(handle: *mut libc::c_void) { - if !handle.is_null() { - unsafe { - libc::dlclose(handle); - } + #[no_mangle] + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_metric( + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, + _: ddog_CharSlice, _: ddog_MetricType, _: ddog_MetricNamespace, + ) -> ddog_MaybeError { + ddog_MaybeError { + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, + __bindgen_anon_1: unsafe { std::mem::zeroed() }, } } - - type TestAddFn = unsafe extern "C" fn(i32, i32) -> i32; - - #[test] - fn test_sidecar_symbol_resolve_and_call() { - #[cfg(target_os = "macos")] - let lib_name = "libtest_sidecar.dylib"; - #[cfg(target_os = "linux")] - let lib_name = "libtest_sidecar.so"; - let lib_path = PathBuf::from(env!("OUT_DIR")).join(lib_name); - let handle = load_library(&lib_path); - - static TEST_ADD_SYMBOL: SidecarSymbol = SidecarSymbol::new(c"test_add"); - TEST_ADD_SYMBOL - .resolve() - .expect("Failed to resolve test_add symbol"); - - let result = unsafe { TEST_ADD_SYMBOL(3, 4) }; - assert_eq!(result, 7, "test_add(3, 4) should return 7"); - - unload_library(handle); - } - - #[test] - fn test_sidecar_symbol_unresolved_symbol_fails() { - static NONEXISTENT_SYMBOL: SidecarSymbol = - SidecarSymbol::new(c"nonexistent_function_12345"); - - let result = NONEXISTENT_SYMBOL.resolve(); - assert!(result.is_err(), "Resolving nonexistent symbol should fail"); - } - - #[test] - fn test_sidecar_symbol_debug_format() { - static DEBUG_TEST_SYMBOL: SidecarSymbol = SidecarSymbol::new(c"debug_test_func"); - - let debug_str = format!("{:?}", DEBUG_TEST_SYMBOL); - assert!( - debug_str.contains("initialized: false"), - "Unresolved symbol should show initialized: false" - ); - assert!( - debug_str.contains("debug_test_func"), - "Debug output should contain the symbol name" - ); - } } + diff --git a/appsec/helper-rust/src/ffi/sidecar_ffi.rs b/appsec/helper-rust/src/ffi/sidecar_ffi.rs index 202e11de288..ab451f22dff 100644 --- a/appsec/helper-rust/src/ffi/sidecar_ffi.rs +++ b/appsec/helper-rust/src/ffi/sidecar_ffi.rs @@ -1,10 +1,10 @@ /* automatically generated by rust-bindgen 0.72.1 */ -//! Auto-generated FFI bindings from components-rs/sidecar.h +//! Auto-generated FFI bindings from components-rs/sidecar-appsec.h //! //! Regenerate with: ./scripts/generate-sidecar-ffi.sh //! -//! Only includes types/functions needed for helper-rust as telemetry sender. +//! Only includes types/functions needed by helper-rust. #![allow(non_camel_case_types, non_upper_case_globals, dead_code)] #[repr(C)] @@ -71,6 +71,19 @@ pub const ddog_LogLevel_DDOG_LOG_LEVEL_ERROR: ddog_LogLevel = 0; pub const ddog_LogLevel_DDOG_LOG_LEVEL_WARN: ddog_LogLevel = 1; pub const ddog_LogLevel_DDOG_LOG_LEVEL_DEBUG: ddog_LogLevel = 2; pub type ddog_LogLevel = ::core::ffi::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ddog_Arc_Target { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ddog_ConfigInvariants { + _unused: [u8; 0], +} +pub type ddog_InProcNotifyFn = ::core::option::Option< + unsafe extern "C" fn(arg1: *const ddog_ConfigInvariants, arg2: *const ddog_Arc_Target), +>; unsafe extern "C" { pub fn ddog_Error_drop(error: *mut ddog_Error); } @@ -81,46 +94,58 @@ unsafe extern "C" { pub fn ddog_MaybeError_drop(arg1: ddog_MaybeError); } unsafe extern "C" { - pub fn ddog_sidecar_transport_drop(arg1: *mut ddog_SidecarTransport); + pub fn ddog_set_rc_notify_fn(notify_fn: ddog_InProcNotifyFn); } unsafe extern "C" { pub fn ddog_sidecar_connect(connection: *mut *mut ddog_SidecarTransport) -> ddog_MaybeError; } +unsafe extern "C" { + pub fn ddog_sidecar_transport_drop(arg1: *mut ddog_SidecarTransport); +} unsafe extern "C" { pub fn ddog_sidecar_ping(transport: *mut *mut ddog_SidecarTransport) -> ddog_MaybeError; } +unsafe extern "C" { + pub fn ddog_remote_config_path( + id: *const ddog_ConfigInvariants, + target: *const ddog_Arc_Target, + ) -> *mut ::core::ffi::c_char; +} +unsafe extern "C" { + pub fn ddog_remote_config_path_free(path: *mut ::core::ffi::c_char); +} unsafe extern "C" { pub fn ddog_sidecar_enqueue_telemetry_log( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - identifier_ffi: ddog_CharSlice, + session_id: ddog_CharSlice, + runtime_id: ddog_CharSlice, + service_name: ddog_CharSlice, + env_name: ddog_CharSlice, + identifier: ddog_CharSlice, level: ddog_LogLevel, - message_ffi: ddog_CharSlice, - stack_trace_ffi: *mut ddog_CharSlice, - tags_ffi: *mut ddog_CharSlice, + message: ddog_CharSlice, + stack_trace: *mut ddog_CharSlice, + tags: *mut ddog_CharSlice, is_sensitive: bool, ) -> ddog_MaybeError; } unsafe extern "C" { pub fn ddog_sidecar_enqueue_telemetry_point( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - metric_name_ffi: ddog_CharSlice, + session_id: ddog_CharSlice, + runtime_id: ddog_CharSlice, + service_name: ddog_CharSlice, + env_name: ddog_CharSlice, + metric_name: ddog_CharSlice, value: f64, - tags_ffi: *mut ddog_CharSlice, + tags: *mut ddog_CharSlice, ) -> ddog_MaybeError; } unsafe extern "C" { pub fn ddog_sidecar_enqueue_telemetry_metric( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - metric_name_ffi: ddog_CharSlice, + session_id: ddog_CharSlice, + runtime_id: ddog_CharSlice, + service_name: ddog_CharSlice, + env_name: ddog_CharSlice, + metric_name: ddog_CharSlice, metric_type: ddog_MetricType, metric_namespace: ddog_MetricNamespace, ) -> ddog_MaybeError; diff --git a/appsec/helper-rust/src/rc_notify.rs b/appsec/helper-rust/src/rc_notify.rs index 42c10abdf12..e897a5c5c62 100644 --- a/appsec/helper-rust/src/rc_notify.rs +++ b/appsec/helper-rust/src/rc_notify.rs @@ -1,21 +1,20 @@ -use std::ffi::{c_char, c_void, CStr, OsStr}; +use std::ffi::{CStr, OsStr}; use std::os::unix::ffi::OsStrExt; use std::path::Path; use std::sync::atomic::{AtomicPtr, Ordering}; +use crate::ffi::sidecar_ffi::{ + ddog_Arc_Target, ddog_ConfigInvariants, ddog_remote_config_path, + ddog_remote_config_path_free, ddog_set_rc_notify_fn, +}; use crate::service::ServiceManager; -type InProcNotifyFn = extern "C" fn(*const c_void, *const c_void); -type DdogRemoteConfigPathFn = extern "C" fn(*const c_void, *const c_void) -> *mut c_char; -type DdogRemoteConfigPathFreeFn = extern "C" fn(*mut c_char); - -static mut DDOG_SET_RC_NOTIFY_FN: Option)> = None; -static mut DDOG_REMOTE_CONFIG_PATH: Option = None; -static mut DDOG_REMOTE_CONFIG_PATH_FREE: Option = None; - static SERVICE_MANAGER: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); -extern "C" fn rc_notify_callback(invariants: *const c_void, target: *const c_void) { +unsafe extern "C" fn rc_notify_callback( + invariants: *const ddog_ConfigInvariants, + target: *const ddog_Arc_Target, +) { let service_manager = SERVICE_MANAGER.load(Ordering::Acquire); if service_manager.is_null() { log::warn!("No service manager to notify of remote config updates"); @@ -37,36 +36,6 @@ extern "C" fn rc_notify_callback(invariants: *const c_void, target: *const c_voi } pub fn resolve_symbols() -> Result<(), String> { - unsafe { - let set_fn = libc::dlsym(libc::RTLD_DEFAULT, c"ddog_set_rc_notify_fn".as_ptr()); - if set_fn.is_null() { - return Err("Failed to resolve ddog_set_rc_notify_fn".to_string()); - } - DDOG_SET_RC_NOTIFY_FN = Some(std::mem::transmute::< - *mut libc::c_void, - extern "C" fn(Option), - >(set_fn)); - - let path_fn = libc::dlsym(libc::RTLD_DEFAULT, c"ddog_remote_config_path".as_ptr()); - if path_fn.is_null() { - return Err("Failed to resolve ddog_remote_config_path".to_string()); - } - DDOG_REMOTE_CONFIG_PATH = Some(std::mem::transmute::< - *mut libc::c_void, - DdogRemoteConfigPathFn, - >(path_fn)); - - let path_free_fn = - libc::dlsym(libc::RTLD_DEFAULT, c"ddog_remote_config_path_free".as_ptr()); - if path_free_fn.is_null() { - return Err("Failed to resolve ddog_remote_config_path_free".to_string()); - } - DDOG_REMOTE_CONFIG_PATH_FREE = Some(std::mem::transmute::< - *mut libc::c_void, - DdogRemoteConfigPathFreeFn, - >(path_free_fn)); - } - Ok(()) } @@ -75,54 +44,36 @@ pub fn register_for_rc_notifications(service_manager: &'static ServiceManager) { SERVICE_MANAGER.store(service_manager as *const _ as *mut _, Ordering::Release); - if let Some(set_fn) = unsafe { DDOG_SET_RC_NOTIFY_FN } { - set_fn(Some(rc_notify_callback)); - } else { - log::warn!("ddog_set_rc_notify_fn not available, RC notifications will not work"); + unsafe { + ddog_set_rc_notify_fn(Some(rc_notify_callback)); } } pub fn unregister_for_rc_notifications() { log::info!("Unregistering for RC update callbacks"); - if let Some(set_fn) = unsafe { DDOG_SET_RC_NOTIFY_FN } { - set_fn(None); + unsafe { + ddog_set_rc_notify_fn(None); } SERVICE_MANAGER.store(std::ptr::null_mut(), Ordering::Release); } struct RemoteConfigPath { - buf: *mut c_char, - path_free_fn: DdogRemoteConfigPathFreeFn, + buf: *mut std::ffi::c_char, } impl RemoteConfigPath { - fn new(invariants: *const c_void, target: *const c_void) -> anyhow::Result { - let path_fn = match unsafe { DDOG_REMOTE_CONFIG_PATH } { - Some(f) => f, - None => { - return Err(anyhow::anyhow!("ddog_remote_config_path not resolved")); - } - }; - - let path_free_fn = match unsafe { DDOG_REMOTE_CONFIG_PATH_FREE } { - Some(f) => f, - None => { - return Err(anyhow::anyhow!("ddog_remote_config_path_free not resolved")); - } - }; - - let buf = path_fn(invariants, target); + fn new(invariants: *const ddog_ConfigInvariants, target: *const ddog_Arc_Target) -> anyhow::Result { + let buf = unsafe { ddog_remote_config_path(invariants, target) }; if buf.is_null() { return Err(anyhow::anyhow!("ddog_remote_config_path returned null")); } - - Ok(Self { buf, path_free_fn }) + Ok(Self { buf }) } } impl Drop for RemoteConfigPath { fn drop(&mut self) { - (self.path_free_fn)(self.buf); + unsafe { ddog_remote_config_path_free(self.buf) }; } } impl AsRef for RemoteConfigPath { diff --git a/appsec/helper-rust/src/telemetry/sidecar.rs b/appsec/helper-rust/src/telemetry/sidecar.rs index 4dc3b34d7c6..1fa12635ce6 100644 --- a/appsec/helper-rust/src/telemetry/sidecar.rs +++ b/appsec/helper-rust/src/telemetry/sidecar.rs @@ -12,60 +12,21 @@ use futures::task::Context; use crate::client::log::{debug, info, warning}; use crate::client::protocol::{SidecarSettings, TelemetrySettings}; use crate::ffi::sidecar_ffi::{ - ddog_CharSlice, ddog_Error, ddog_Error_drop, ddog_Error_message, ddog_LogLevel, + ddog_CharSlice, ddog_Error_drop, ddog_Error_message, ddog_LogLevel, ddog_LogLevel_DDOG_LOG_LEVEL_DEBUG, ddog_LogLevel_DDOG_LOG_LEVEL_ERROR, - ddog_LogLevel_DDOG_LOG_LEVEL_WARN, ddog_MaybeError, ddog_MetricNamespace, - ddog_MetricNamespace_DDOG_METRIC_NAMESPACE_APPSEC, ddog_MetricType, + ddog_LogLevel_DDOG_LOG_LEVEL_WARN, ddog_MaybeError, + ddog_MetricNamespace_DDOG_METRIC_NAMESPACE_APPSEC, ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR, ddog_SidecarTransport, ddog_sidecar_connect, ddog_sidecar_enqueue_telemetry_log, ddog_sidecar_enqueue_telemetry_metric, ddog_sidecar_enqueue_telemetry_point, ddog_sidecar_ping, ddog_sidecar_transport_drop, }; -use crate::ffi::SidecarSymbol; -use crate::sidecar_symbol; use crate::telemetry::{ KnownMetric, TelemetryLogSubmitter, TelemetryMetricSubmitter, TelemetryTags, }; use super::{LogLevel, MetricName, TelemetryLog}; -type DdogSidecarEnqueueTelemetryLogFn = unsafe extern "C" fn( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - identifier_ffi: ddog_CharSlice, - level: ddog_LogLevel, - message_ffi: ddog_CharSlice, - stack_trace_ffi: *mut ddog_CharSlice, - tags_ffi: *mut ddog_CharSlice, - is_sensitive: bool, -) -> ddog_MaybeError; -type DdogSidecarEnqueueTelemetryPointFn = unsafe extern "C" fn( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - metric_name_ffi: ddog_CharSlice, - value: f64, - tags_ffi: *mut ddog_CharSlice, -) -> ddog_MaybeError; -type DdogSidecarEnqueueTelemetryMetricFn = unsafe extern "C" fn( - session_id_ffi: ddog_CharSlice, - runtime_id_ffi: ddog_CharSlice, - service_name_ffi: ddog_CharSlice, - env_name_ffi: ddog_CharSlice, - metric_name_ffi: ddog_CharSlice, - metric_type: ddog_MetricType, - metric_namespace: ddog_MetricNamespace, -) -> ddog_MaybeError; -type DdogErrorDropFn = unsafe extern "C" fn(*mut ddog_Error); -type DdogErrorMessageFn = unsafe extern "C" fn(*const ddog_Error) -> ddog_CharSlice; -type DdogSidecarConnectFn = - unsafe extern "C" fn(*mut *mut ddog_SidecarTransport) -> ddog_MaybeError; -type DdogSidecarPingFn = unsafe extern "C" fn(*mut *mut ddog_SidecarTransport) -> ddog_MaybeError; -type DdogSidecarTransportDropFn = unsafe extern "C" fn(*mut ddog_SidecarTransport); - static RESOLUTION_STATUS: AtomicBool = AtomicBool::new(false); #[repr(u8)] @@ -78,31 +39,6 @@ pub enum SidecarStatus { static SIDECAR_STATUS: AtomicU8 = AtomicU8::new(SidecarStatus::Unknown as u8); -sidecar_symbol!( - static ENQUEUE_TELEMETRY_LOG = DdogSidecarEnqueueTelemetryLogFn : ddog_sidecar_enqueue_telemetry_log -); -sidecar_symbol!( - static ENQUEUE_TELEMETRY_POINT = DdogSidecarEnqueueTelemetryPointFn : ddog_sidecar_enqueue_telemetry_point -); -sidecar_symbol!( - static ENQUEUE_TELEMETRY_METRIC = DdogSidecarEnqueueTelemetryMetricFn : ddog_sidecar_enqueue_telemetry_metric -); -sidecar_symbol!( - static ERROR_DROP = DdogErrorDropFn : ddog_Error_drop -); -sidecar_symbol!( - static ERROR_MESSAGE = DdogErrorMessageFn : ddog_Error_message -); -sidecar_symbol!( - static SIDECAR_CONNECT = DdogSidecarConnectFn : ddog_sidecar_connect -); -sidecar_symbol!( - static SIDECAR_PING = DdogSidecarPingFn : ddog_sidecar_ping -); -sidecar_symbol!( - static SIDECAR_TRANSPORT_DROP = DdogSidecarTransportDropFn : ddog_sidecar_transport_drop -); - pub struct TelemetrySidecarLogSubmitter<'a> { session_id: &'a str, runtime_id: &'a str, @@ -165,7 +101,7 @@ impl Drop for MaybeErrorRAII { fn drop(&mut self) { unsafe { if self.maybe_error.tag == ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR { - ERROR_DROP(&mut self.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some); + ddog_Error_drop(&mut self.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some); } } } @@ -174,7 +110,7 @@ impl From for Option { fn from(value: MaybeErrorRAII) -> Self { if value.maybe_error.tag == ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR { let msg = - unsafe { ERROR_MESSAGE(&value.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some) }; + unsafe { ddog_Error_message(&value.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some) }; if msg.ptr.is_null() || msg.len == 0 { return Some(String::new()); } @@ -216,7 +152,7 @@ impl TelemetryLogSubmitter for TelemetrySidecarLogSubmitter<'_> { let stack_trace_slice = log.stack_trace.as_ref().map(|st| char_slice_from_str(st)); let result: ddog_MaybeError = unsafe { - ENQUEUE_TELEMETRY_LOG( + ddog_sidecar_enqueue_telemetry_log( session_id, runtime_id, service_name, @@ -242,14 +178,6 @@ impl TelemetryLogSubmitter for TelemetrySidecarLogSubmitter<'_> { } pub fn resolve_symbols() -> anyhow::Result<()> { - ENQUEUE_TELEMETRY_LOG.resolve()?; - ENQUEUE_TELEMETRY_POINT.resolve()?; - ENQUEUE_TELEMETRY_METRIC.resolve()?; - ERROR_DROP.resolve()?; - ERROR_MESSAGE.resolve()?; - SIDECAR_CONNECT.resolve()?; - SIDECAR_PING.resolve()?; - SIDECAR_TRANSPORT_DROP.resolve()?; RESOLUTION_STATUS.store(true, Ordering::Release); Ok(()) } @@ -274,15 +202,15 @@ impl SidecarReadyFuture { fn try_connect_and_ping(&self) -> bool { let mut transport: *mut ddog_SidecarTransport = std::ptr::null_mut(); - let mut connect_result = unsafe { SIDECAR_CONNECT(&mut transport as *mut _) }; + let mut connect_result = unsafe { ddog_sidecar_connect(&mut transport as *mut _) }; if connect_result.tag == ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR { - unsafe { ERROR_DROP(&mut connect_result.__bindgen_anon_1.__bindgen_anon_1.some) }; + unsafe { ddog_Error_drop(&mut connect_result.__bindgen_anon_1.__bindgen_anon_1.some) }; return false; } - let ping_result = unsafe { SIDECAR_PING(&mut transport as *mut _) }; - unsafe { SIDECAR_TRANSPORT_DROP(transport) }; + let ping_result = unsafe { ddog_sidecar_ping(&mut transport as *mut _) }; + unsafe { ddog_sidecar_transport_drop(transport) }; ping_result.tag != ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR } @@ -348,7 +276,7 @@ pub(super) fn register_metric_ffi( let metric_name_slice = char_slice_from_str(metric.name.0); let result: ddog_MaybeError = unsafe { - ENQUEUE_TELEMETRY_METRIC( + ddog_sidecar_enqueue_telemetry_metric( session_id, runtime_id, service_name, @@ -447,7 +375,7 @@ impl TelemetryMetricSubmitter for TelemetrySidecarMetricSubmitter<'_> { let tags_slice = char_slice_from_str(&tags_string); let result: ddog_MaybeError = unsafe { - ENQUEUE_TELEMETRY_POINT( + ddog_sidecar_enqueue_telemetry_point( session_id, runtime_id, service_name, diff --git a/appsec/helper-rust/test_sidecar_lib.c b/appsec/helper-rust/test_sidecar_lib.c deleted file mode 100644 index 99883017166..00000000000 --- a/appsec/helper-rust/test_sidecar_lib.c +++ /dev/null @@ -1,3 +0,0 @@ -int test_add(int a, int b) { - return a + b; -} diff --git a/appsec/src/helper/ffi.hpp b/appsec/src/helper/ffi.hpp index 6b98cf3c04b..9d30a519800 100644 --- a/appsec/src/helper/ffi.hpp +++ b/appsec/src/helper/ffi.hpp @@ -1,105 +1,17 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - extern "C" { -#include // push -Wno-nested-anon-types and -Wno-gnu-anonymous-struct on clang #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wnested-anon-types" # pragma clang diagnostic ignored "-Wgnu-anonymous-struct" #endif +#include #include #if defined(__clang__) # pragma clang diagnostic pop #endif } -#define SIDECAR_FFI_SYMBOL(symbol_name) \ - namespace dds::ffi { \ - constinit inline ::dds::ffi::sidecar_function \ - symbol_name = {} /* NOLINT(misc-use-internal-linkage, \ - cert-err58-cpp) */ \ - ; \ - } // namespace ::dds::ffi - -namespace dds::ffi { - -template struct fixed_string { - std::array value{}; - - // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) - explicit consteval fixed_string(const char (&str)[N]) - { - for (std::size_t i = 0; i < N; i++) { value.at(i) = str[i]; } - } - - [[nodiscard]] constexpr const char *c_str() const { return value.data(); } -}; - -template - requires std::is_function_v> -class sidecar_function { -public: - using function_type = std::remove_reference_t; - - constexpr sidecar_function() noexcept = default; - - template - requires std::invocable *, Args...> - decltype(auto) operator()(Args &&...args) const - { - resolve(); - return std::invoke(get_fn(), std::forward(args)...); - } - - [[nodiscard]] function_type *get_fn() const { return resolve(); } - -private: - static function_type *failed_sentinel() noexcept - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return reinterpret_cast( - static_cast(-1)); - } - - mutable std::atomic resolved_fn_{nullptr}; - - function_type *resolve() const - { - function_type *fn = resolved_fn_.load(std::memory_order_relaxed); - if (fn == failed_sentinel()) { - throw std::runtime_error{ - std::string{"Failed to resolve symbol: "} + SymbolName.c_str()}; - } - if (fn != nullptr) { - return fn; - } - - void *found = dlsym(RTLD_DEFAULT, SymbolName.c_str()); - if (found == nullptr) { - fn = failed_sentinel(); - } else { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - fn = reinterpret_cast(found); - } - - (void)resolved_fn_.store(fn, std::memory_order_relaxed); - - return fn; - } -}; - -} // namespace dds::ffi - namespace dds { inline std::string_view to_sv(ddog_CharSlice &slice) noexcept { diff --git a/appsec/src/helper/remote_config/client.cpp b/appsec/src/helper/remote_config/client.cpp index 8582df920d1..eeb51f98858 100644 --- a/appsec/src/helper/remote_config/client.cpp +++ b/appsec/src/helper/remote_config/client.cpp @@ -12,14 +12,6 @@ #include #include -extern "C" { -#include -} - -SIDECAR_FFI_SYMBOL(ddog_remote_config_reader_for_path); -SIDECAR_FFI_SYMBOL(ddog_remote_config_read); -SIDECAR_FFI_SYMBOL(ddog_remote_config_reader_drop); - namespace { bool sets_are_indentical_for_subbed_products( @@ -48,9 +40,8 @@ namespace dds::remote_config { client::client(remote_config::settings settings, std::vector> listeners, std::shared_ptr msubmitter) - : reader_{ffi::ddog_remote_config_reader_for_path( - settings.shmem_path.c_str()), - ffi::ddog_remote_config_reader_drop.get_fn()}, + : reader_{ddog_remote_config_reader_for_path(settings.shmem_path.c_str()), + ddog_remote_config_reader_drop}, settings_{std::move(settings)}, listeners_{std::move(listeners)}, msubmitter_{std::move(msubmitter)} { @@ -82,7 +73,7 @@ bool client::poll() SPDLOG_DEBUG("Polling remote config"); ddog_CharSlice slice{}; - const bool has_update = ffi::ddog_remote_config_read(reader_.get(), &slice); + const bool has_update = ddog_remote_config_read(reader_.get(), &slice); if (!has_update) { SPDLOG_DEBUG("No update available for {}", settings_.shmem_path); return false; diff --git a/appsec/src/helper/runner.cpp b/appsec/src/helper/runner.cpp index 32e0ddd60d6..82de1c4d5e9 100644 --- a/appsec/src/helper/runner.cpp +++ b/appsec/src/helper/runner.cpp @@ -19,14 +19,7 @@ extern "C" { #include } -using in_proc_notify_fn = void (*)( - const ddog_ConfigInvariants *invariants, const ddog_Arc_Target *target); - -extern "C" void ddog_set_rc_notify_fn(in_proc_notify_fn notify_fn); - -SIDECAR_FFI_SYMBOL(ddog_set_rc_notify_fn); -SIDECAR_FFI_SYMBOL(ddog_remote_config_path); -SIDECAR_FFI_SYMBOL(ddog_remote_config_path_free); +using in_proc_notify_fn = ddog_InProcNotifyFn; namespace dds { @@ -109,16 +102,16 @@ void runner::register_for_rc_notifications() SPDLOG_INFO("Register RC update callback"); std::atomic_store(&runner::RUNNER_FOR_NOTIFICATIONS, shared_from_this()); - ffi::ddog_set_rc_notify_fn([](const ddog_ConfigInvariants *invariants, - const ddog_Arc_Target *target) { - char *path = ffi::ddog_remote_config_path(invariants, target); + ddog_set_rc_notify_fn([](const ddog_ConfigInvariants *invariants, + const ddog_Arc_Target *target) { + char *path = ddog_remote_config_path(invariants, target); const std::shared_ptr runner = std::atomic_load(&RUNNER_FOR_NOTIFICATIONS); if (!runner) { // NOLINTNEXTLINE(bugprone-lambda-function-name) SPDLOG_WARN("No runner to notify of remote config updates"); - ffi::ddog_remote_config_path_free(path); + ddog_remote_config_path_free(path); return; } @@ -126,7 +119,7 @@ void runner::register_for_rc_notifications() SPDLOG_INFO("Remote config updated notification for {}", path); // TODO: move the updates to a separate thread runner->service_manager_->notify_of_rc_updates(path); - ffi::ddog_remote_config_path_free(path); + ddog_remote_config_path_free(path); }); } diff --git a/appsec/src/helper/service.cpp b/appsec/src/helper/service.cpp index 2aa81d14e39..47674854f38 100644 --- a/appsec/src/helper/service.cpp +++ b/appsec/src/helper/service.cpp @@ -12,23 +12,8 @@ #include -extern "C" { -#include -} - using CharSlice = ddog_Slice_CChar; -SIDECAR_FFI_SYMBOL(ddog_sidecar_connect); -SIDECAR_FFI_SYMBOL(ddog_sidecar_ping); -SIDECAR_FFI_SYMBOL(ddog_sidecar_transport_drop); - -SIDECAR_FFI_SYMBOL(ddog_sidecar_enqueue_telemetry_log); -SIDECAR_FFI_SYMBOL(ddog_sidecar_enqueue_telemetry_point); -SIDECAR_FFI_SYMBOL(ddog_sidecar_enqueue_telemetry_metric); - -SIDECAR_FFI_SYMBOL(ddog_Error_message); -SIDECAR_FFI_SYMBOL(ddog_Error_drop); - namespace { inline CharSlice to_ffi_string(std::string_view sv) { @@ -138,16 +123,15 @@ void service::metrics_impl::submit_log(const sidecar_settings &sc_settings, tags_ffi_ptr = &tags_ffi_struct; } - ddog_MaybeError result = - ffi::ddog_sidecar_enqueue_telemetry_log(session_id_ffi, runtime_id_ffi, - service_name_ffi, env_name_ffi, identifier_ffi, c_level, - message_ffi, stack_trace_ffi_ptr, tags_ffi_ptr, log.is_sensitive); + ddog_MaybeError result = ddog_sidecar_enqueue_telemetry_log(session_id_ffi, + runtime_id_ffi, service_name_ffi, env_name_ffi, identifier_ffi, c_level, + message_ffi, stack_trace_ffi_ptr, tags_ffi_ptr, log.is_sensitive); if (result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { - ddog_CharSlice const error_msg = ffi::ddog_Error_message(&result.some); + ddog_CharSlice const error_msg = ddog_Error_message(&result.some); SPDLOG_INFO("Failed to enqueue telemetry log, error: {}", std::string_view{error_msg.ptr, error_msg.len}); - ffi::ddog_Error_drop(&result.some); + ddog_Error_drop(&result.some); } else { SPDLOG_DEBUG("Sent telemetry log via sidecar-ffi: {}: {}", log.identifier, log.message); @@ -166,7 +150,7 @@ void service::metrics_impl::register_metric_ffi( return; } - ddog_MaybeError result = ffi::ddog_sidecar_enqueue_telemetry_metric( + ddog_MaybeError result = ddog_sidecar_enqueue_telemetry_metric( to_ffi_string(sc_settings.session_id), to_ffi_string(sc_settings.runtime_id), to_ffi_string(telemetry_settings.service_name), @@ -174,10 +158,10 @@ void service::metrics_impl::register_metric_ffi( DDOG_METRIC_NAMESPACE_APPSEC); if (result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { - ddog_CharSlice const error_msg = ffi::ddog_Error_message(&result.some); + ddog_CharSlice const error_msg = ddog_Error_message(&result.some); SPDLOG_INFO("Failed to register telemetry metric, error: {}", std::string_view{error_msg.ptr, error_msg.len}); - ffi::ddog_Error_drop(&result.some); + ddog_Error_drop(&result.some); } else { SPDLOG_DEBUG( "Registered telemetry metric via sidecar-ffi: {} of type {}", name, @@ -204,7 +188,7 @@ void service::metrics_impl::submit_metric_ffi( tags_ffi = to_ffi_string(*tags); tags_ffi_ptr = &tags_ffi; } - ddog_MaybeError result = ffi::ddog_sidecar_enqueue_telemetry_point( + ddog_MaybeError result = ddog_sidecar_enqueue_telemetry_point( to_ffi_string(sc_settings.session_id), to_ffi_string(sc_settings.runtime_id), to_ffi_string(telemetry_settings.service_name), @@ -212,10 +196,10 @@ void service::metrics_impl::submit_metric_ffi( tags_ffi_ptr); if (result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { - ddog_CharSlice const error_msg = ffi::ddog_Error_message(&result.some); + ddog_CharSlice const error_msg = ddog_Error_message(&result.some); SPDLOG_INFO("Failed to enqueue telemetry point, error: {}", std::string_view{error_msg.ptr, error_msg.len}); - ffi::ddog_Error_drop(&result.some); + ddog_Error_drop(&result.some); } else { SPDLOG_DEBUG("Sent telemetry point via sidecar-ffi: {} of value {}", name, value); @@ -302,8 +286,7 @@ bool wait_for_sidecar_ready() for (int attempt = 0; attempt < max_attempts; ++attempt) { ddog_SidecarTransport *transport = nullptr; - ddog_MaybeError const connect_result = - dds::ffi::ddog_sidecar_connect(&transport); + ddog_MaybeError const connect_result = ddog_sidecar_connect(&transport); if (connect_result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { SPDLOG_DEBUG( @@ -312,17 +295,16 @@ bool wait_for_sidecar_ready() continue; } - ddog_MaybeError ping_result = dds::ffi::ddog_sidecar_ping(&transport); - dds::ffi::ddog_sidecar_transport_drop(transport); + ddog_MaybeError ping_result = ddog_sidecar_ping(&transport); + ddog_sidecar_transport_drop(transport); if (ping_result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { - auto error_message = - dds::ffi::ddog_Error_message(&ping_result.some); + auto error_message = ddog_Error_message(&ping_result.some); SPDLOG_DEBUG( "Sidecar ping failed with error {} (attempt {}), waiting...", dds::to_sv(error_message), attempt + 1); std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); - dds::ffi::ddog_Error_drop(&ping_result.some); + ddog_Error_drop(&ping_result.some); continue; } diff --git a/appsec/tests/helper/service_test.cpp b/appsec/tests/helper/service_test.cpp index b9449f199c0..a9a868ac561 100644 --- a/appsec/tests/helper/service_test.cpp +++ b/appsec/tests/helper/service_test.cpp @@ -100,4 +100,22 @@ __attribute__((visibility("default"))) void ddog_sidecar_transport_drop( { // do nothing } + +using in_proc_notify_fn = void (*)( + const ddog_ConfigInvariants *invariants, const ddog_Arc_Target *target); +__attribute__((visibility("default"))) void ddog_set_rc_notify_fn( + in_proc_notify_fn /*notify_fn*/) +{ + // do nothing +} +__attribute__((visibility("default"))) char *ddog_remote_config_path( + const ddog_ConfigInvariants * /*id*/, const ddog_Arc_Target * /*target*/) +{ + return nullptr; +} +__attribute__((visibility("default"))) void ddog_remote_config_path_free( + char * /*path*/) +{ + // do nothing +} } diff --git a/appsec/tests/integration/build.gradle b/appsec/tests/integration/build.gradle index 09e424f4176..94f8a87fc35 100644 --- a/appsec/tests/integration/build.gradle +++ b/appsec/tests/integration/build.gradle @@ -189,10 +189,10 @@ def buildRunInDockerTask = { Map options -> } if (options.get('needsBoostCache', true)) { if (project.hasProperty('localBoostCache')) { - binds[project.getProperty('localBoostCache')] = '/root/.boost' + binds[project.getProperty('localBoostCache')] = '/var/boost-cache' } else { volumes['php-appsec-boost-cache'] = [ - mountPoint: '/root/.boost', + mountPoint: '/var/boost-cache', task: createVolumeTask('php-appsec-boost-cache'), ] } @@ -340,6 +340,7 @@ def buildTracerTask = { String version, String variant, altBaseTag = null -> '../../../ext', '../../../zend_abstract_interface', '../../../libdatadog', + '../../../components-rs', '../../../ddtrace.sym', ], ], @@ -383,7 +384,6 @@ def buildAppSecTask = { String version, String variant, altBaseTag = null -> command: [ '-e', '-c', """ - git config --global --add safe.directory '*' cd /appsec test -f CMakeCache.txt || \\ cmake -DCMAKE_BUILD_TYPE=$buildType \\ @@ -391,7 +391,7 @@ def buildAppSecTask = { String version, String variant, altBaseTag = null -> -DDD_APPSEC_ENABLE_PATCHELF_LIBC=ON \\ -DGIT_COMMIT=${libddwafCommit()} \\ -DDD_APPSEC_TESTING=ON \\ - -DBOOST_CACHE_PREFIX=/root/.boost /project/appsec + -DBOOST_CACHE_PREFIX=/var/boost-cache /project/appsec make -j extension ddappsec-helper && \\ touch ddappsec.so libddappsec-helper.so """ @@ -567,42 +567,6 @@ task saveCaches(type: Exec) { chown \$UUID /build/php-appsec-volume-caches-${arch}.tar.gz""" } -// libddwaf shared library build (for helper-rust) -buildRunInDockerTask( - baseName: 'buildLibddwaf', - imageTag: 'php-deps', - description: 'Build libddwaf shared library', - needsBoostCache: false, - needsCargoCache: false, - inputs: [ - dirs: ['../../third_party/libddwaf'], - ], - outputs: [ - volume: 'php-libddwaf', - files: ['lib/libddwaf.so', 'include/ddwaf.h'], - ], - volumes: [ - 'php-libddwaf': [mountPoint: '/libddwaf-prefix'], - ], - command: [ - '-e', '-c', - """ - git config --global --add safe.directory '*' - - mkdir -p /tmp/libddwaf-build - cd /tmp/libddwaf-build - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \\ - -DCMAKE_INSTALL_PREFIX=/libddwaf-prefix \\ - -DLIBDDWAF_BUILD_SHARED=ON \\ - -DLIBDDWAF_BUILD_STATIC=OFF \\ - -DLIBDDWAF_TESTING=OFF \\ - /project/appsec/third_party/libddwaf - make -j install - touch /libddwaf-prefix/lib/libddwaf.so /libddwaf-prefix/include/ddwaf.h - """ - ] -) - // Shared configuration for helper-rust tasks def helperRustInputs = [ dirs: ['../../helper-rust/src', '../../third_party/libddwaf-rust'], @@ -618,7 +582,6 @@ def helperRustInputs = [ ] def helperRustEnvSetup = ''' -git config --global --add safe.directory '*' export PATH="/root/.cargo/bin:$PATH" export RUSTUP_HOME=/root/.rustup export CARGO_HOME=/root/.cargo @@ -649,8 +612,6 @@ buildRunInDockerTask( export CARGO_TARGET_DIR=/helper-rust-build/cargo-target RUST_TARGET=$(uname -m)-unknown-linux-musl - git config --global --add safe.directory '*' - cd /project/appsec/helper-rust # Build using nightly toolchain with unstable features diff --git a/components-rs/common.h b/components-rs/common.h index e53c4ef311d..6f3de959102 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -1765,6 +1765,7 @@ typedef struct ddog_Result_TracerMemfdHandle { }; } ddog_Result_TracerMemfdHandle; +typedef void (*ddog_InProcNotifyFn)(const struct ddog_ConfigInvariants*, const struct ddog_Arc_Target*); #ifdef __cplusplus extern "C" { #endif // __cplusplus diff --git a/components-rs/sidecar-appsec.h b/components-rs/sidecar-appsec.h new file mode 100644 index 00000000000..5dd71f7d62a --- /dev/null +++ b/components-rs/sidecar-appsec.h @@ -0,0 +1,76 @@ +#ifndef DDOG_SIDECAR_APPSEC_H +#define DDOG_SIDECAR_APPSEC_H + +#include +#include +#include +#include "common.h" +void ddog_set_rc_notify_fn(ddog_InProcNotifyFn notify_fn); + + +/** + * Connect to an already-running sidecar. **Never** tries to spawn one. + * Used by the AppSec helper's `SidecarReadyFuture` from within the sidecar + * process to verify the sidecar is accepting connections. + */ +ddog_MaybeError ddog_sidecar_connect(struct ddog_SidecarTransport **connection); + +void ddog_sidecar_transport_drop(struct ddog_SidecarTransport*); + +ddog_MaybeError ddog_sidecar_ping(struct ddog_SidecarTransport **transport); + +char *ddog_remote_config_path(const struct ddog_ConfigInvariants *id, + const struct ddog_Arc_Target *target); + +void ddog_remote_config_path_free(char *path); + +/** + * # Safety + * Pointers must be valid; strings must be non-null. + */ +ddog_MaybeError ddog_sidecar_enqueue_telemetry_log(ddog_CharSlice session_id, + ddog_CharSlice runtime_id, + ddog_CharSlice service_name, + ddog_CharSlice env_name, + ddog_CharSlice identifier, + enum ddog_LogLevel level, + ddog_CharSlice message, + ddog_CharSlice *stack_trace, + ddog_CharSlice *tags, + bool is_sensitive); + +/** + * # Safety + * Pointers must be valid; strings must be non-null. + */ +ddog_MaybeError ddog_sidecar_enqueue_telemetry_point(ddog_CharSlice session_id, + ddog_CharSlice runtime_id, + ddog_CharSlice service_name, + ddog_CharSlice env_name, + ddog_CharSlice metric_name, + double value, + ddog_CharSlice *tags); + +/** + * # Safety + * Pointers must be valid; strings must be non-null. + */ +ddog_MaybeError ddog_sidecar_enqueue_telemetry_metric(ddog_CharSlice session_id, + ddog_CharSlice runtime_id, + ddog_CharSlice service_name, + ddog_CharSlice env_name, + ddog_CharSlice metric_name, + enum ddog_MetricType metric_type, + enum ddog_MetricNamespace metric_namespace); + +/** + * Open a remote-config shared-memory reader at `path`. + * Used by the AppSec helper to read rule updates pushed by the sidecar. + */ +struct ddog_RemoteConfigReader *ddog_remote_config_reader_for_path(const char *path); + +bool ddog_remote_config_read(struct ddog_RemoteConfigReader *reader, ddog_CharSlice *data); + +void ddog_remote_config_reader_drop(struct ddog_RemoteConfigReader*); + +#endif /* DDOG_SIDECAR_APPSEC_H */ diff --git a/components-rs/sidecar-bin/build.rs b/components-rs/sidecar-bin/build.rs index 4b7710918bc..8a001dbbb49 100644 --- a/components-rs/sidecar-bin/build.rs +++ b/components-rs/sidecar-bin/build.rs @@ -8,6 +8,9 @@ const EXPORTED_SYMBOLS: &[&str] = &[ "ddog_Error_message", "ddog_remote_config_path", "ddog_remote_config_path_free", + "ddog_remote_config_read", + "ddog_remote_config_reader_drop", + "ddog_remote_config_reader_for_path", "ddog_set_rc_notify_fn", "ddog_sidecar_connect", "ddog_sidecar_enqueue_telemetry_log", diff --git a/libdatadog b/libdatadog index b5c1ed41cfd..d8159d2deb7 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit b5c1ed41cfd6c20b95d2b34c89d1fb554e1c0250 +Subproject commit d8159d2deb7ae8268e0ff1f7927f6a98f327cbab From 09d3df137a61299c131cf1290325a665d69873dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 13:02:53 +0000 Subject: [PATCH 03/13] Attempt build fix --- compile_rust.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compile_rust.sh b/compile_rust.sh index d6d540c220a..2db2d380003 100755 --- a/compile_rust.sh +++ b/compile_rust.sh @@ -24,7 +24,7 @@ else CARGO_PROFILE_ARG="--profile $PROFILE" fi -CARGO_TARGET_DIR="${CARGO_TARGET_DIR:?CARGO_TARGET_DIR must be set}" +CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}" # Sidecar-specific RUSTFLAGS. # - musl: disable static CRT so dlopen works for loading the AppSec helper. @@ -44,6 +44,10 @@ esac SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$RUSTFLAGS" RUSTC_BOOTSTRAP=1 \ "${DDTRACE_CARGO:-cargo}" build -p ddtrace-php $CARGO_PROFILE_ARG "$@" +if test -n "$COMPILE_ASAN"; then + SIDECAR_RUSTFLAGS="$SIDECAR_RUSTFLAGS -Clink-arg=-fsanitize=address" +fi + SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$SIDECAR_RUSTFLAGS" RUSTC_BOOTSTRAP=1 \ "${DDTRACE_CARGO:-cargo}" build -p datadog-ipc-helper $CARGO_PROFILE_ARG "$@" From 546200ca72076e86f1f0e08ab5ff8c328d201f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 13:46:10 +0000 Subject: [PATCH 04/13] compile_rust.sh: copy datadog-ipc-helper to modules/ after build find_sidecar_binary() locates datadog-ipc-helper relative to ddtrace.so via dladdr. The appsec extension xtest build (cmake) only runs the inner PHP Makefile, never the outer repo Makefile's all target, so the outer Makefile's $(SIDECAR_BIN_IN_MODULES) copy rule never fires. Copying in compile_rust.sh ensures the binary lands next to ddtrace.so in every build path. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- compile_rust.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compile_rust.sh b/compile_rust.sh index 2db2d380003..0050df7907b 100755 --- a/compile_rust.sh +++ b/compile_rust.sh @@ -51,3 +51,11 @@ fi SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$SIDECAR_RUSTFLAGS" RUSTC_BOOTSTRAP=1 \ "${DDTRACE_CARGO:-cargo}" build -p datadog-ipc-helper $CARGO_PROFILE_ARG "$@" +# Place datadog-ipc-helper next to where ddtrace.so will be installed so that +# find_sidecar_binary() can locate it via dladdr at runtime. +_ipc_profile="${PROFILE:-debug}" +_ipc_src="${CARGO_TARGET_DIR}/${_ipc_profile}/datadog-ipc-helper" +_ipc_dst="$(dirname "${CARGO_TARGET_DIR%/}")/modules/datadog-ipc-helper" +mkdir -p "$(dirname "$_ipc_dst")" +cp "$_ipc_src" "$_ipc_dst" + From 3bf9942e7c7973c7ea6c2f34b7c5a1adca6bad57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 14:13:10 +0000 Subject: [PATCH 05/13] sidecar.rs: canonicalize ddtrace.so path before finding ipc-helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the cmake appsec test build, extension_dir contains a symlink ddtrace.so → tmp/build_extension/modules/ddtrace.so. dladdr may return the symlink path rather than the real path. Canonicalizing ensures find_sidecar_binary() always looks in the directory where the real ddtrace.so lives, where we already copy datadog-ipc-helper. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- components-rs/sidecar.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index 0086750965d..5248834dceb 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -28,15 +28,30 @@ fn find_sidecar_binary() -> anyhow::Result { let ddtrace_path = ddtrace_path.ok_or_else(|| { anyhow::format_err!("could not resolve ddtrace.so path via dladdr") })?; - let dir = std::path::PathBuf::from( - ddtrace_path - .to_str() - .map_err(|_| anyhow::format_err!("ddtrace.so path is not valid UTF-8"))?, - ); - let dir = dir - .parent() - .ok_or_else(|| anyhow::format_err!("ddtrace.so has no parent directory"))?; - Ok(dir.join("datadog-ipc-helper")) + let ddtrace_str = ddtrace_path + .to_str() + .map_err(|_| anyhow::format_err!("ddtrace.so path is not valid UTF-8"))?; + let ddtrace_full = std::path::PathBuf::from(ddtrace_str); + // Search in both the directory dladdr returned and, if it differs after + // symlink resolution, the directory of the real file. + let ddtrace_real = std::fs::canonicalize(&ddtrace_full).unwrap_or_else(|_| ddtrace_full.clone()); + for candidate_dir in [ddtrace_real.parent(), ddtrace_full.parent()] + .into_iter() + .flatten() + .collect::>() + .into_iter() + .collect::>() + { + let candidate = candidate_dir.join("datadog-ipc-helper"); + if candidate.exists() { + return Ok(candidate); + } + } + Err(anyhow::format_err!( + "datadog-ipc-helper not found next to ddtrace.so (tried {:?} and {:?})", + ddtrace_real.parent(), + ddtrace_full.parent(), + )) } #[cfg(unix)] From 93594211b8805df01d3d83ecc95e5a99a982c953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 16:30:05 +0000 Subject: [PATCH 06/13] Makefile: make datadog-ipc-helper install conditional CI test jobs download pre-built ddtrace.so without running cargo, so SIDECAR_BIN doesn't exist in those contexts. Making the ipc-helper copy in the install target conditional on the binary existing prevents make from failing when it's not present. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index aa79be95d2e..0057f5ffd31 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,8 @@ $(PHP_EXTENSION_DIR)/ddtrace.so: $(SO_FILE) $(PHP_EXTENSION_DIR)/datadog-ipc-helper: $(SIDECAR_BIN) $(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper -install: $(PHP_EXTENSION_DIR)/ddtrace.so $(PHP_EXTENSION_DIR)/datadog-ipc-helper +install: $(PHP_EXTENSION_DIR)/ddtrace.so + $(if $(wildcard $(SIDECAR_BIN)),$(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper) set_static_option: $(eval EXTRA_CONFIGURE_OPTIONS := --enable-ddtrace-rust-library-split) From 18e4a0c550f38330af80fa7901332ea280d5a1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 16:45:11 +0000 Subject: [PATCH 07/13] check-ci-jobs.py: don't exit on first failure; wait for all jobs Previously the script called sys.exit(1) as soon as any job failed, leaving hundreds of still-running jobs unreported. Now it waits for running==0 before exiting so the final failure list is complete. Also document in CLAUDE.md that conclusions must not be drawn until running==0, and that Haiku agents need a long enough Bash timeout. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- appsec/helper-rust/CLAUDE.md | 10 ++++++++++ appsec/helper-rust/scripts/check-ci-jobs.py | 16 ++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/appsec/helper-rust/CLAUDE.md b/appsec/helper-rust/CLAUDE.md index 73ccea369ee..95c448f1941 100644 --- a/appsec/helper-rust/CLAUDE.md +++ b/appsec/helper-rust/CLAUDE.md @@ -351,6 +351,11 @@ pipeline by walking parent pipelines and their child bridges to find one contain that match the filter. It polls every 30 seconds with a default 60-minute timeout. Exit codes: 0 = all passed, 1 = failures, 2 = timed out. +**IMPORTANT**: The script waits for ALL matched jobs to finish before reporting the final +result. Failed jobs are printed as they appear, but exit(1) is only issued once +`running == 0`. Never conclude that CI passed or failed from an intermediate snapshot +while jobs are still running — always wait for `running=0` before drawing conclusions. + To monitor a pipeline and get a spoken notification when done, spawn a background agent (model: Haiku) with this prompt: @@ -366,6 +371,11 @@ Once it exits, use the speak_when_done MCP tool to say: - Exit 2: "Pipeline monitoring timed out" ``` +**Note on the Haiku agent**: Give it a Bash timeout of at least 600000 ms (10 min) when +pipelines are large. The default 2-minute tool timeout causes it to exit before all jobs +complete, producing a misleadingly optimistic "1 failure" when hundreds more are still +running. Use `--timeout 90` for large pipelines. + ## Misc Notes - Always ask for confirmation before reverting, committing, or pushing anything with git diff --git a/appsec/helper-rust/scripts/check-ci-jobs.py b/appsec/helper-rust/scripts/check-ci-jobs.py index 24afb28329a..353ad052bb7 100755 --- a/appsec/helper-rust/scripts/check-ci-jobs.py +++ b/appsec/helper-rust/scripts/check-ci-jobs.py @@ -303,16 +303,20 @@ def _monitor_loop(jobs_getter, label: str, timeout_sec: float) -> None: f"passed={len(passed)} failed={len(failed)}" ) - if failed: - print("\nFailed jobs:") - for j in failed: - print(f" [{j['id']}] {j['name']}") - sys.exit(1) - if not running: + if failed: + print("\nFailed jobs:") + for j in failed: + print(f" [{j['id']}] {j['name']}") + sys.exit(1) print(f"\nAll {len(passed)} jobs passed.") sys.exit(0) + if failed: + print("\nFailed jobs so far (pipeline still running):") + for j in failed: + print(f" [{j['id']}] {j['name']}") + remaining = deadline - time.monotonic() if remaining <= 0: print( From 46aad4b5caa356b1ab81bba1d4b4ad54436cc160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 17:08:05 +0000 Subject: [PATCH 08/13] Fix compile_rust.sh copy and Windows build compile_rust.sh: only copy datadog-ipc-helper when CARGO_TARGET_DIR is absolute. build-sidecar.sh uses a relative 'target' dir and doesn't need the binary placed in modules/; the unconditional cp was failing because cargo's output is relative to the workspace root (one level above components-rs/) while cp was called from inside components-rs/. libdatadog: add IntoRawHandle to spawn_worker Windows imports so that into_raw_handle() is in scope on Windows builds. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- compile_rust.sh | 17 ++++++++++++----- libdatadog | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compile_rust.sh b/compile_rust.sh index 0050df7907b..f6d5ecce166 100755 --- a/compile_rust.sh +++ b/compile_rust.sh @@ -53,9 +53,16 @@ SIDECAR_VERSION=$(cat ../VERSION) RUSTFLAGS="$SIDECAR_RUSTFLAGS" RUSTC_BOOTSTRAP # Place datadog-ipc-helper next to where ddtrace.so will be installed so that # find_sidecar_binary() can locate it via dladdr at runtime. -_ipc_profile="${PROFILE:-debug}" -_ipc_src="${CARGO_TARGET_DIR}/${_ipc_profile}/datadog-ipc-helper" -_ipc_dst="$(dirname "${CARGO_TARGET_DIR%/}")/modules/datadog-ipc-helper" -mkdir -p "$(dirname "$_ipc_dst")" -cp "$_ipc_src" "$_ipc_dst" +# Only do this when CARGO_TARGET_DIR is an absolute path (test/cmake builds). +# Distribution builds (build-sidecar.sh) use a relative target dir and don't +# need the binary placed in modules/. +case "$CARGO_TARGET_DIR" in + /*) + _ipc_profile="${PROFILE:-debug}" + _ipc_src="${CARGO_TARGET_DIR}/${_ipc_profile}/datadog-ipc-helper" + _ipc_dst="$(dirname "${CARGO_TARGET_DIR%/}")/modules/datadog-ipc-helper" + mkdir -p "$(dirname "$_ipc_dst")" + cp "$_ipc_src" "$_ipc_dst" + ;; +esac diff --git a/libdatadog b/libdatadog index d8159d2deb7..8561e703dbf 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit d8159d2deb7ae8268e0ff1f7927f6a98f327cbab +Subproject commit 8561e703dbf27646232a101f79cada16c1650ad9 From 87d24c9246528d6e2e2ee4f44bccf4154c9006d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 18:12:41 +0000 Subject: [PATCH 09/13] Propagate datadog-ipc-helper through CI artifact pipeline compile extension: move datadog-ipc-helper to modules/{version}/ in after_script so it travels with ddtrace.so as a CI artifact. test jobs: also mv datadog-ipc-helper to tmp/build_extension/modules/ so make install can find it at SIDECAR_BIN_IN_MODULES. Makefile install: try SIDECAR_BIN_IN_MODULES first (populated by both compile_rust.sh and the artifact mv step), falling back to SIDECAR_BIN (built from source). Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .gitlab/generate-tracer.php | 2 ++ Makefile | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab/generate-tracer.php b/.gitlab/generate-tracer.php index 6a46bf480ca..3fde2cb4f03 100644 --- a/.gitlab/generate-tracer.php +++ b/.gitlab/generate-tracer.php @@ -44,6 +44,7 @@ function before_script_steps($with_docker_auth = false) { - git config --global --add safe.directory "${CI_PROJECT_DIR}/*" - mkdir -p tmp/build_extension/modules artifacts - mv "modules/${PHP_MAJOR_MINOR}-${SWITCH_PHP_VERSION}-${host_os}-${ARCH}/ddtrace.so" "tmp/build_extension/modules/" + - '[ -f "modules/${PHP_MAJOR_MINOR}-${SWITCH_PHP_VERSION}-${host_os}-${ARCH}/datadog-ipc-helper" ] && mv "modules/${PHP_MAJOR_MINOR}-${SWITCH_PHP_VERSION}-${host_os}-${ARCH}/datadog-ipc-helper" "tmp/build_extension/modules/" || true' @@ -86,6 +87,7 @@ function before_script_steps($with_docker_auth = false) { export out_dir="modules/${PHP_MAJOR_MINOR}-${SWITCH_PHP_VERSION}-${host_os}-${ARCH}/" mkdir -p "${out_dir}" mv "tmp/build_extension/modules/ddtrace.so" "${out_dir}" + [ -f "tmp/build_extension/modules/datadog-ipc-helper" ] && mv "tmp/build_extension/modules/datadog-ipc-helper" "${out_dir}" || true cache: - key: prefix: $CI_JOB_NAME diff --git a/Makefile b/Makefile index 0057f5ffd31..8386719b6c5 100644 --- a/Makefile +++ b/Makefile @@ -136,7 +136,8 @@ $(PHP_EXTENSION_DIR)/datadog-ipc-helper: $(SIDECAR_BIN) $(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper install: $(PHP_EXTENSION_DIR)/ddtrace.so - $(if $(wildcard $(SIDECAR_BIN)),$(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper) + $(if $(wildcard $(SIDECAR_BIN_IN_MODULES)),$(Q) $(SUDO) cp $(SIDECAR_BIN_IN_MODULES) $(PHP_EXTENSION_DIR)/datadog-ipc-helper,\ + $(if $(wildcard $(SIDECAR_BIN)),$(Q) $(SUDO) cp $(SIDECAR_BIN) $(PHP_EXTENSION_DIR)/datadog-ipc-helper)) set_static_option: $(eval EXTRA_CONFIGURE_OPTIONS := --enable-ddtrace-rust-library-split) From 7b936b4a7d47094748cf9000cd83dd5307a45b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 19:59:50 +0000 Subject: [PATCH 10/13] Propagate datadog-ipc-helper through the full package pipeline build-sidecar.sh: produce datadog-ipc-helper${arch}[${suffix}] alongside libddtrace_php artifacts. generate-package.php: include datadog-ipc-helper* in build-sidecar artifact paths so link-tracing-extension can pick it up. link-tracing-extension.sh: copy datadog-ipc-helper into extensions_arch/ so it travels with ddtrace.so into installer packages (.deb/.rpm/.apk) and the verify tests (/opt/datadog-php/extensions/). generate-ssi-package.sh: copy datadog-ipc-helper into the SSI loader/ directory alongside libddtrace_php.so. generate-final-artifact.sh: copy datadog-ipc-helper into each ext/$php_api/ directory so it sits next to ddtrace.so in the dd-library-php bundle. ffi.rs: run rustfmt to fix formatting check failure. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .gitlab/build-sidecar.sh | 1 + .gitlab/generate-package.php | 1 + .gitlab/link-tracing-extension.sh | 4 +++ appsec/helper-rust/src/ffi.rs | 41 ++++++++++++++++++-------- tooling/bin/generate-final-artifact.sh | 2 ++ tooling/bin/generate-ssi-package.sh | 3 ++ 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/.gitlab/build-sidecar.sh b/.gitlab/build-sidecar.sh index 6040ef36026..e45d1ffda6c 100755 --- a/.gitlab/build-sidecar.sh +++ b/.gitlab/build-sidecar.sh @@ -22,3 +22,4 @@ fi SHARED=1 PROFILE=tracer-release host_os="${HOST_OS}" ./compile_rust.sh cp -v "${CARGO_TARGET_DIR:-target}/tracer-release/libddtrace_php.a" "libddtrace_php_$(uname -m)${suffix}.a" objcopy --compress-debug-sections "${CARGO_TARGET_DIR:-target}/tracer-release/libddtrace_php.so" "libddtrace_php_$(uname -m)${suffix}.so" +cp -v "${CARGO_TARGET_DIR:-target}/tracer-release/datadog-ipc-helper" "datadog-ipc-helper$(uname -m)${suffix}" diff --git a/.gitlab/generate-package.php b/.gitlab/generate-package.php index 669ccb8d4b6..ce9d3f5d09f 100644 --- a/.gitlab/generate-package.php +++ b/.gitlab/generate-package.php @@ -429,6 +429,7 @@ artifacts: paths: - "libddtrace_php_*.*" + - "datadog-ipc-helper*" diff --git a/.gitlab/link-tracing-extension.sh b/.gitlab/link-tracing-extension.sh index f84f943cca4..8d792845159 100755 --- a/.gitlab/link-tracing-extension.sh +++ b/.gitlab/link-tracing-extension.sh @@ -15,3 +15,7 @@ done for pid in "${pids[@]}"; do wait $pid done + +# Place datadog-ipc-helper in the extensions directory so it travels with ddtrace.so +# through the artifact pipeline and gets installed alongside it. +cp "datadog-ipc-helper$(uname -m)${suffix}" "extensions_$(uname -m)/datadog-ipc-helper" diff --git a/appsec/helper-rust/src/ffi.rs b/appsec/helper-rust/src/ffi.rs index 98ef6fa253a..aaea8cc3edf 100644 --- a/appsec/helper-rust/src/ffi.rs +++ b/appsec/helper-rust/src/ffi.rs @@ -13,7 +13,10 @@ mod test_stubs { extern "C" fn ddog_Error_drop(_: *mut ddog_Error) {} #[no_mangle] extern "C" fn ddog_Error_message(_: *const ddog_Error) -> ddog_CharSlice { - ddog_CharSlice { ptr: std::ptr::null(), len: 0 } + ddog_CharSlice { + ptr: std::ptr::null(), + len: 0, + } } #[no_mangle] extern "C" fn ddog_MaybeError_drop(_: ddog_MaybeError) {} @@ -40,9 +43,7 @@ mod test_stubs { #[no_mangle] extern "C" fn ddog_sidecar_transport_drop(_: *mut ddog_SidecarTransport) {} #[no_mangle] - unsafe extern "C" fn ddog_sidecar_ping( - _: *mut *mut ddog_SidecarTransport, - ) -> ddog_MaybeError { + unsafe extern "C" fn ddog_sidecar_ping(_: *mut *mut ddog_SidecarTransport) -> ddog_MaybeError { ddog_MaybeError { tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, __bindgen_anon_1: unsafe { std::mem::zeroed() }, @@ -50,9 +51,16 @@ mod test_stubs { } #[no_mangle] unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_log( - _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, - _: ddog_CharSlice, _: ddog_LogLevel, _: ddog_CharSlice, - _: *mut ddog_CharSlice, _: *mut ddog_CharSlice, _: bool, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_LogLevel, + _: ddog_CharSlice, + _: *mut ddog_CharSlice, + _: *mut ddog_CharSlice, + _: bool, ) -> ddog_MaybeError { ddog_MaybeError { tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, @@ -61,8 +69,13 @@ mod test_stubs { } #[no_mangle] unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_point( - _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, - _: ddog_CharSlice, _: f64, _: *mut ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: f64, + _: *mut ddog_CharSlice, ) -> ddog_MaybeError { ddog_MaybeError { tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, @@ -71,8 +84,13 @@ mod test_stubs { } #[no_mangle] unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_metric( - _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, - _: ddog_CharSlice, _: ddog_MetricType, _: ddog_MetricNamespace, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_CharSlice, + _: ddog_MetricType, + _: ddog_MetricNamespace, ) -> ddog_MaybeError { ddog_MaybeError { tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, @@ -80,4 +98,3 @@ mod test_stubs { } } } - diff --git a/tooling/bin/generate-final-artifact.sh b/tooling/bin/generate-final-artifact.sh index 1c233c0e628..194d6079ff8 100755 --- a/tooling/bin/generate-final-artifact.sh +++ b/tooling/bin/generate-final-artifact.sh @@ -140,11 +140,13 @@ for architecture in "${architectures[@]}"; do cp ./extensions_${architecture}/ddtrace-$php_api.so ${tmp_folder_final_gnu_trace}/ext/$php_api/ddtrace.so; cp ./extensions_${architecture}/ddtrace-$php_api-zts.so ${tmp_folder_final_gnu_trace}/ext/$php_api/ddtrace-zts.so; cp ./extensions_${architecture}/ddtrace-$php_api-debug.so ${tmp_folder_final_gnu_trace}/ext/$php_api/ddtrace-debug.so; + cp ./extensions_${architecture}/datadog-ipc-helper ${tmp_folder_final_gnu_trace}/ext/$php_api/datadog-ipc-helper 2>/dev/null || true; fi if [[ $target == "linux-musl" ]]; then mkdir -p ${tmp_folder_final_musl_trace}/ext/$php_api; cp ./extensions_${architecture}/ddtrace-$php_api-alpine.so ${tmp_folder_final_musl_trace}/ext/$php_api/ddtrace.so; cp ./extensions_${architecture}/ddtrace-$php_api-alpine-zts.so ${tmp_folder_final_musl_trace}/ext/$php_api/ddtrace-zts.so; + cp ./extensions_${architecture}/datadog-ipc-helper ${tmp_folder_final_musl_trace}/ext/$php_api/datadog-ipc-helper 2>/dev/null || true; fi if [[ $target == "windows" && ${php_api} -ge 20170718 && $architecture == "x86_64" ]]; then # Windows support starts on 7.2 mkdir -p ${tmp_folder_final_windows_trace}/ext/$php_api; diff --git a/tooling/bin/generate-ssi-package.sh b/tooling/bin/generate-ssi-package.sh index 4cac5053341..88571d0a1ca 100755 --- a/tooling/bin/generate-ssi-package.sh +++ b/tooling/bin/generate-ssi-package.sh @@ -57,6 +57,9 @@ for architecture in "${architectures[@]}"; do stripto libddtrace_php_${architecture}.so ${gnu}/loader/libddtrace_php.so stripto libddtrace_php_${architecture}-alpine.so ${musl}/loader/libddtrace_php.so + # Place datadog-ipc-helper next to libddtrace_php.so so find_sidecar_binary() can locate it. + cp datadog-ipc-helper${architecture} ${gnu}/loader/datadog-ipc-helper + cp datadog-ipc-helper${architecture}-alpine ${musl}/loader/datadog-ipc-helper stripto dd_library_loader-${architecture}-linux-gnu.so ${gnu}/loader/dd_library_loader.so stripto dd_library_loader-${architecture}-linux-musl.so ${musl}/loader/dd_library_loader.so From 3d5e9fa18810c5f8b53f8a233d436fbebb9eafa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 21:37:34 +0000 Subject: [PATCH 11/13] Fix cargo fmt and installer ipc-helper propagation rc_notify.rs: rustfmt to fix cargo fmt check. datadog-setup.php: copy datadog-ipc-helper to the PHP extension directory alongside ddtrace.so. The installer copies ddtrace.so from trace/ext/{api}/ to the system PHP ext dir; find_sidecar_binary() then looks for datadog-ipc-helper in that same directory. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- appsec/helper-rust/src/rc_notify.rs | 9 ++++++--- datadog-setup.php | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/appsec/helper-rust/src/rc_notify.rs b/appsec/helper-rust/src/rc_notify.rs index e897a5c5c62..9b74ff16d04 100644 --- a/appsec/helper-rust/src/rc_notify.rs +++ b/appsec/helper-rust/src/rc_notify.rs @@ -4,8 +4,8 @@ use std::path::Path; use std::sync::atomic::{AtomicPtr, Ordering}; use crate::ffi::sidecar_ffi::{ - ddog_Arc_Target, ddog_ConfigInvariants, ddog_remote_config_path, - ddog_remote_config_path_free, ddog_set_rc_notify_fn, + ddog_Arc_Target, ddog_ConfigInvariants, ddog_remote_config_path, ddog_remote_config_path_free, + ddog_set_rc_notify_fn, }; use crate::service::ServiceManager; @@ -63,7 +63,10 @@ struct RemoteConfigPath { buf: *mut std::ffi::c_char, } impl RemoteConfigPath { - fn new(invariants: *const ddog_ConfigInvariants, target: *const ddog_Arc_Target) -> anyhow::Result { + fn new( + invariants: *const ddog_ConfigInvariants, + target: *const ddog_Arc_Target, + ) -> anyhow::Result { let buf = unsafe { ddog_remote_config_path(invariants, target) }; if buf.is_null() { return Err(anyhow::anyhow!("ddog_remote_config_path returned null")); diff --git a/datadog-setup.php b/datadog-setup.php index a86432fde7a..823278999a1 100644 --- a/datadog-setup.php +++ b/datadog-setup.php @@ -650,6 +650,13 @@ function install($options) $extensionDestination = $extDir . '/' . EXTENSION_PREFIX . 'ddtrace.' . EXTENSION_SUFFIX; safe_copy_extension($extensionRealPath, $extensionDestination); + // Sidecar helper: must live next to ddtrace.so so find_sidecar_binary() can locate it. + $ipcHelperRealPath = "$tmpArchiveTraceRoot/ext/$extensionVersion/datadog-ipc-helper"; + if (file_exists($ipcHelperRealPath)) { + $ipcHelperDestination = $extDir . '/datadog-ipc-helper'; + safe_copy_extension($ipcHelperRealPath, $ipcHelperDestination); + } + // Profiling $profilingExtensionRealPath = "$tmpArchiveProfilingRoot/ext/$extensionVersion/" . EXTENSION_PREFIX . "datadog-profiling$extensionSuffix." . EXTENSION_SUFFIX; From e472642d4e9d443cdb3e36954bc30c0258f09358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 22:57:37 +0000 Subject: [PATCH 12/13] Fix cargo fmt in telemetry/sidecar.rs and absolute CARGO_TARGET_DIR in compile_extension.sh Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .gitlab/compile_extension.sh | 2 +- appsec/helper-rust/src/telemetry/sidecar.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab/compile_extension.sh b/.gitlab/compile_extension.sh index f52a043d41f..cd3e605c138 100755 --- a/.gitlab/compile_extension.sh +++ b/.gitlab/compile_extension.sh @@ -3,8 +3,8 @@ set -eo pipefail SWITCH_PHP_VERSION=${SWITCH_PHP_VERSION:-} WITH_ASAN=${WITH_ASAN:-} -CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-target} EXTENSION_DIR=${EXTENSION_DIR:-tmp/build_extension} +CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-${PWD}/${EXTENSION_DIR}/target} MODULES_DIR=${MODULES_DIR:-${EXTENSION_DIR}/modules} # Generate VERSION with build id diff --git a/appsec/helper-rust/src/telemetry/sidecar.rs b/appsec/helper-rust/src/telemetry/sidecar.rs index 1fa12635ce6..7e380ece610 100644 --- a/appsec/helper-rust/src/telemetry/sidecar.rs +++ b/appsec/helper-rust/src/telemetry/sidecar.rs @@ -109,8 +109,9 @@ impl Drop for MaybeErrorRAII { impl From for Option { fn from(value: MaybeErrorRAII) -> Self { if value.maybe_error.tag == ddog_Option_Error_Tag_DDOG_OPTION_ERROR_SOME_ERROR { - let msg = - unsafe { ddog_Error_message(&value.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some) }; + let msg = unsafe { + ddog_Error_message(&value.maybe_error.__bindgen_anon_1.__bindgen_anon_1.some) + }; if msg.ptr.is_null() || msg.len == 0 { return Some(String::new()); } From 051246c6be1068b93755362f302907927e8e2096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 20 Mar 2026 23:42:35 +0000 Subject: [PATCH 13/13] Export CARGO_TARGET_DIR so compile_rust.sh inherits and uses consistent paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without export, compile_rust.sh never sees the variable and cargo defaults to the workspace-root target/, but compile_extension.sh's link step used the local variable value — path mismatch broke the link step. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .gitlab/compile_extension.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/compile_extension.sh b/.gitlab/compile_extension.sh index cd3e605c138..c4446cf5b66 100755 --- a/.gitlab/compile_extension.sh +++ b/.gitlab/compile_extension.sh @@ -4,7 +4,7 @@ set -eo pipefail SWITCH_PHP_VERSION=${SWITCH_PHP_VERSION:-} WITH_ASAN=${WITH_ASAN:-} EXTENSION_DIR=${EXTENSION_DIR:-tmp/build_extension} -CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-${PWD}/${EXTENSION_DIR}/target} +export CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-${PWD}/${EXTENSION_DIR}/target} MODULES_DIR=${MODULES_DIR:-${EXTENSION_DIR}/modules} # Generate VERSION with build id