From f1a7faac405d103fd6740b0b9691391b6590ff19 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Fri, 20 Feb 2026 18:44:35 +0100 Subject: [PATCH 01/13] feat: implement otel process ctxt update --- libdd-library-config/src/otel_process_ctx.rs | 49 ++++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 9a7489c4e1..da8782da0a 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -1,7 +1,8 @@ // Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 -//! Implementation of the publisher part of the [OTEL process context](https://github.com/open-telemetry/opentelemetry-specification/pull/4719) +//! Implementation of the publisher part of the [OTEL process +//! context](https://github.com/open-telemetry/opentelemetry-specification/pull/4719) //! //! # A note on race conditions //! @@ -16,7 +17,7 @@ pub mod linux { ffi::c_void, mem::ManuallyDrop, os::fd::AsFd as _, - ptr, + ptr::{self, addr_of_mut}, sync::{ atomic::{fence, AtomicU64, Ordering}, Mutex, MutexGuard, @@ -239,8 +240,9 @@ pub mod linux { .context("madvise MADVISE_DONTFORK failed")?; let published_at_ns = time_now_ns().ok_or_else(|| { - anyhow::anyhow!("fail to get current time for process context publication") + anyhow::anyhow!("failed to get current time for process context publication") })?; + let header = mapping.start_addr as *mut MappingHeader; unsafe { @@ -278,12 +280,41 @@ pub mod linux { Ok(ProcessContextHandle { mapping, payload }) } - /// Updates the context after initial publication. Currently unimplemented (always returns - /// `Err`). - fn update(&mut self, _payload: Vec) -> anyhow::Result<()> { - Err(anyhow::anyhow!( - "process context update isn't implemented yet" - )) + /// Updates the context after initial publication. + fn update(&mut self, payload: Vec) -> anyhow::Result<()> { + let header = self.mapping.start_addr as *mut MappingHeader; + + // Note that setting `published_at_ns` to zero doesn't entirely avoid data races with + // the reader in theory, which could have read a previous non-zero value just before we + // flipped it but still see subsequent writes. However, since the reader is totally + // unable to manifest itself to the updating process, we can't have a truly atomic + // update of the whole header, and is the best we can do. + let published_at_atomic = + unsafe { AtomicU64::from_ptr(addr_of_mut!((*header).published_at_ns)) }; + + // A process shouldn't try to concurrently update its own context, so this shouldn't + // really happen. + if published_at_atomic.swap(0, Ordering::Acquire) == 0 { + return Err(anyhow::anyhow!( + "concurrent update of the process context is not supported" + )); + } + + let published_at_ns = time_now_ns() + .ok_or_else(|| anyhow::anyhow!("could not get the current timestamp"))?; + + self.payload = payload; + + unsafe { + (*header).payload_ptr = self.payload.as_ptr(); + (*header).payload_size = self.payload.len().try_into().map_err(|_| { + anyhow::anyhow!("couldn't update process protocol: new payload too large") + })?; + } + + published_at_atomic.store(published_at_ns, Ordering::Release); + + Ok(()) } } From b678a81d7289a5b4b264c72a6edbe7d9492ffc12 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Mon, 23 Feb 2026 13:58:54 +0100 Subject: [PATCH 02/13] test: otel process context update --- libdd-library-config/src/otel_process_ctx.rs | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index da8782da0a..18225d0883 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -483,5 +483,36 @@ pub mod linux { super::unpublish().expect("couldn't unpublish the context"); } + + #[test] + #[cfg_attr(miri, ignore)] + fn update_process_context() { + let payload_v1 = "example process context payload"; + let payload_v2 = "another example process context payload of different size"; + + super::publish(payload_v1.as_bytes().to_vec()) + .expect("couldn't publish the process context"); + super::publish(payload_v2.as_bytes().to_vec()) + .expect("couldn't update the process contet"); + + let header = read_process_context().expect("couldn't read back the process contex"); + // Safety: the published context must have put valid bytes of size payload_size in the + // context if the signature check succeded. + let read_payload = unsafe { + std::slice::from_raw_parts(header.payload_ptr, header.payload_size as usize) + }; + + assert!(header.signature == *super::SIGNATURE, "wrong signature"); + assert!( + header.version == super::PROCESS_CTX_VERSION, + "wrong context version" + ); + assert!( + header.payload_size == payload_v2.len() as u32, + "wrong payload size" + ); + assert!(header.published_at_ns > 0, "published_at_ns is zero"); + assert!(read_payload == payload_v2.as_bytes(), "payload mismatch"); + } } } From cf843c8c34470e6d539ec2a6423e099b4bc7730d Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Mon, 23 Feb 2026 14:57:26 +0100 Subject: [PATCH 03/13] chore: strengthen the memory ordering --- libdd-library-config/src/otel_process_ctx.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 18225d0883..a2f2696008 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -294,12 +294,14 @@ pub mod linux { // A process shouldn't try to concurrently update its own context, so this shouldn't // really happen. - if published_at_atomic.swap(0, Ordering::Acquire) == 0 { + if published_at_atomic.swap(0, Ordering::Relaxed) == 0 { return Err(anyhow::anyhow!( "concurrent update of the process context is not supported" )); } + fence(Ordering::SeqCst); + let published_at_ns = time_now_ns() .ok_or_else(|| anyhow::anyhow!("could not get the current timestamp"))?; @@ -312,7 +314,8 @@ pub mod linux { })?; } - published_at_atomic.store(published_at_ns, Ordering::Release); + fence(Ordering::SeqCst); + published_at_atomic.store(published_at_ns, Ordering::Relaxed); Ok(()) } From af176c60b6fa08658cd3faee478ac5a19bb8f86d Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 24 Feb 2026 14:49:46 +0100 Subject: [PATCH 04/13] test: release otel process ctxt after update --- libdd-library-config/src/otel_process_ctx.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index a2f2696008..13b9aa90e4 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -516,6 +516,8 @@ pub mod linux { ); assert!(header.published_at_ns > 0, "published_at_ns is zero"); assert!(read_payload == payload_v2.as_bytes(), "payload mismatch"); + + super::unpublish().expect("couldn't unpublish the context"); } } } From ef585b23169a1533064e15f2b9627d7dd2f27969 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Mon, 2 Mar 2026 17:17:53 +0100 Subject: [PATCH 05/13] chore: factor align checks in a function --- libdd-library-config/src/otel_process_ctx.rs | 45 +++++++++++--------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 13b9aa90e4..4cebb13270 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -221,18 +221,7 @@ pub mod linux { let mut mapping = MemMapping::new()?; let size = mapping_size(); - // Checks that the layout allow us to access `signature` and `published_at_ns` as - // atomics u64. Page size is at minimum 4KB and will be always 8 bytes aligned even on - // exotic platforms. The respective offsets of `signature` and `published_at_ns` are - // 0 and 8 bytes, so it suffices for `AtomicU64` to require an alignment of at most 8 - // (which is the expected alignment anyway). - // - // Note that `align_of` is a `const fn`, so this is in fact a compile-time check and - // will be optimized away, hence the `allow(unreachable_code)`. - #[allow(unreachable_code)] - if std::mem::align_of::() > 8 { - return Err(anyhow::anyhow!("alignment constraints forbid the use of atomics for publishing the protocol context")); - } + check_atomic_u64_align_constraints()?; // Safety: the invariants of MemMapping ensures `start_addr` is not null and comes // from a previous call to `mmap` @@ -284,16 +273,14 @@ pub mod linux { fn update(&mut self, payload: Vec) -> anyhow::Result<()> { let header = self.mapping.start_addr as *mut MappingHeader; - // Note that setting `published_at_ns` to zero doesn't entirely avoid data races with - // the reader in theory, which could have read a previous non-zero value just before we - // flipped it but still see subsequent writes. However, since the reader is totally - // unable to manifest itself to the updating process, we can't have a truly atomic - // update of the whole header, and is the best we can do. + check_atomic_u64_align_constraints()?; + + // Safety: we checked the alignment constraints, and the header memory is valid for + // both read and writes. let published_at_atomic = unsafe { AtomicU64::from_ptr(addr_of_mut!((*header).published_at_ns)) }; - // A process shouldn't try to concurrently update its own context, so this shouldn't - // really happen. + // A process shouldn't try to concurrently update its own context if published_at_atomic.swap(0, Ordering::Relaxed) == 0 { return Err(anyhow::anyhow!( "concurrent update of the process context is not supported" @@ -307,6 +294,8 @@ pub mod linux { self.payload = payload; + // Safety: we own the mapping, which is live and valid for writes. The header is packed + // and thus has no alignment constraints. unsafe { (*header).payload_ptr = self.payload.as_ptr(); (*header).payload_size = self.payload.len().try_into().map_err(|_| { @@ -321,6 +310,24 @@ pub mod linux { } } + // Checks that the layout allows us to access `signature` and `published_at_ns` as + // atomics u64. + fn check_atomic_u64_align_constraints() -> anyhow::Result<()> { + // Page size is at minimum 4KB and will be always 8 bytes aligned even on + // exotic platforms. The respective offsets of `signature` and `published_at_ns` are + // 0 and 8 bytes, so it suffices for `AtomicU64` to require an alignment of at most 8 + // (which is the expected alignment anyway). + // + // Note that `align_of` is a `const fn`, so this is in fact a compile-time check and + // will be optimized away, hence the `allow(unreachable_code)`. + #[allow(unreachable_code)] + if std::mem::align_of::() <= 8 { + Ok(()) + } else { + Err(anyhow::anyhow!("alignment constraints forbid the use of atomics for publishing the protocol context")) + } + } + // Whether this size depends on the page size or not in the future, Rustix's `page_size()` // caches the value in a static atomic, so it's ok to call `mapping_size()` repeatedly; it // won't result in a syscall each time. From 46ba50d4cdf1593a433e91de618b47e5bec0f27c Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 11:32:01 +0100 Subject: [PATCH 06/13] chore: fix typo in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Antonsson --- libdd-library-config/src/otel_process_ctx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 4cebb13270..e26aad76ec 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -503,7 +503,7 @@ pub mod linux { super::publish(payload_v1.as_bytes().to_vec()) .expect("couldn't publish the process context"); super::publish(payload_v2.as_bytes().to_vec()) - .expect("couldn't update the process contet"); + .expect("couldn't update the process context"); let header = read_process_context().expect("couldn't read back the process contex"); // Safety: the published context must have put valid bytes of size payload_size in the From 92df91b79dfb56ef842491315f98922957c3438c Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 11:51:16 +0100 Subject: [PATCH 07/13] chore: fix wrong code comment --- libdd-library-config/src/otel_process_ctx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index e26aad76ec..679c778cfe 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -315,7 +315,7 @@ pub mod linux { fn check_atomic_u64_align_constraints() -> anyhow::Result<()> { // Page size is at minimum 4KB and will be always 8 bytes aligned even on // exotic platforms. The respective offsets of `signature` and `published_at_ns` are - // 0 and 8 bytes, so it suffices for `AtomicU64` to require an alignment of at most 8 + // 0 and 16 bytes, so it suffices for `AtomicU64` to require an alignment of at most 8 // (which is the expected alignment anyway). // // Note that `align_of` is a `const fn`, so this is in fact a compile-time check and From e95d687459722d77f3900dfa776b49ae8b3c8fff Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 13:25:55 +0100 Subject: [PATCH 08/13] test: merge publish and update otel process context tests Replace the separate `publish_then_read_context` and `update_process_context` tests with a single `publish_then_update_process_context` test that checks all header fields (signature, version, payload size, timestamp, payload content) after the initial publish of `payload_v1`, then again after the update to `payload_v2`. Co-Authored-By: Claude Sonnet 4.6 --- libdd-library-config/src/otel_process_ctx.rs | 25 +++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 679c778cfe..08bb089434 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -467,11 +467,13 @@ pub mod linux { #[test] #[cfg_attr(miri, ignore)] - fn publish_then_read_context() { - let payload = "example process context payload"; + fn publish_then_update_process_context() { + let payload_v1 = "example process context payload"; + let payload_v2 = "another example process context payload of different size"; - super::publish(payload.as_bytes().to_vec()) + super::publish(payload_v1.as_bytes().to_vec()) .expect("couldn't publish the process context"); + let header = read_process_context().expect("couldn't read back the process context"); // Safety: the published context must have put valid bytes of size payload_size in the // context if the signature check succeded. @@ -485,27 +487,16 @@ pub mod linux { "wrong context version" ); assert!( - header.payload_size == payload.len() as u32, + header.payload_size == payload_v1.len() as u32, "wrong payload size" ); assert!(header.published_at_ns > 0, "published_at_ns is zero"); - assert!(read_payload == payload.as_bytes(), "payload mismatch"); + assert!(read_payload == payload_v1.as_bytes(), "payload mismatch"); - super::unpublish().expect("couldn't unpublish the context"); - } - - #[test] - #[cfg_attr(miri, ignore)] - fn update_process_context() { - let payload_v1 = "example process context payload"; - let payload_v2 = "another example process context payload of different size"; - - super::publish(payload_v1.as_bytes().to_vec()) - .expect("couldn't publish the process context"); super::publish(payload_v2.as_bytes().to_vec()) .expect("couldn't update the process context"); - let header = read_process_context().expect("couldn't read back the process contex"); + let header = read_process_context().expect("couldn't read back the process context"); // Safety: the published context must have put valid bytes of size payload_size in the // context if the signature check succeded. let read_payload = unsafe { From c37ee4f6e97777819d1569ecfb5768e4a846b9c9 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 13:39:13 +0100 Subject: [PATCH 09/13] fix: avoid locking published_at on error --- libdd-library-config/src/otel_process_ctx.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 08bb089434..308c347179 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -275,12 +275,22 @@ pub mod linux { check_atomic_u64_align_constraints()?; + let published_at_ns = time_now_ns() + .ok_or_else(|| anyhow::anyhow!("could not get the current timestamp"))?; + let payload_size = payload.len().try_into().map_err(|_| { + anyhow::anyhow!("couldn't update process protocol: new payload too large") + })?; + // Safety: we checked the alignment constraints, and the header memory is valid for // both read and writes. let published_at_atomic = unsafe { AtomicU64::from_ptr(addr_of_mut!((*header).published_at_ns)) }; // A process shouldn't try to concurrently update its own context + // + // Note: be careful of early return while `published_at` is still zero, as this would + // effectively "lock" any future publishing. Move throwing code above this swap, or + // properly restore the previous value the former can't be done. if published_at_atomic.swap(0, Ordering::Relaxed) == 0 { return Err(anyhow::anyhow!( "concurrent update of the process context is not supported" @@ -288,19 +298,13 @@ pub mod linux { } fence(Ordering::SeqCst); - - let published_at_ns = time_now_ns() - .ok_or_else(|| anyhow::anyhow!("could not get the current timestamp"))?; - self.payload = payload; // Safety: we own the mapping, which is live and valid for writes. The header is packed // and thus has no alignment constraints. unsafe { (*header).payload_ptr = self.payload.as_ptr(); - (*header).payload_size = self.payload.len().try_into().map_err(|_| { - anyhow::anyhow!("couldn't update process protocol: new payload too large") - })?; + (*header).payload_size = payload_size; } fence(Ordering::SeqCst); From c041630d3132ab9d7438b973da88ef3028dbb79f Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 14:39:08 +0100 Subject: [PATCH 10/13] test: check published_at_ns is strictly greater after update Co-Authored-By: Claude Sonnet 4.6 --- libdd-library-config/src/otel_process_ctx.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 308c347179..31a76c4410 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -497,6 +497,9 @@ pub mod linux { assert!(header.published_at_ns > 0, "published_at_ns is zero"); assert!(read_payload == payload_v1.as_bytes(), "payload mismatch"); + let published_at_ns_v1 = header.published_at_ns; + // Ensure the clock advances so the updated timestamp is strictly greater + std::thread::sleep(std::time::Duration::from_nanos(10)); super::publish(payload_v2.as_bytes().to_vec()) .expect("couldn't update the process context"); @@ -516,7 +519,10 @@ pub mod linux { header.payload_size == payload_v2.len() as u32, "wrong payload size" ); - assert!(header.published_at_ns > 0, "published_at_ns is zero"); + assert!( + header.published_at_ns > published_at_ns_v1, + "published_at_ns should be strictly greater after update" + ); assert!(read_payload == payload_v2.as_bytes(), "payload mismatch"); super::unpublish().expect("couldn't unpublish the context"); From 4630c0fed2966cf2b83b1c151d14453a92086993 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 14:41:48 +0100 Subject: [PATCH 11/13] test: add unpublish_process_context test Co-Authored-By: Claude Sonnet 4.6 --- libdd-library-config/src/otel_process_ctx.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 31a76c4410..26ed86857e 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -527,5 +527,25 @@ pub mod linux { super::unpublish().expect("couldn't unpublish the context"); } + + #[test] + #[cfg_attr(miri, ignore)] + fn unpublish_process_context() { + let payload = "example process context payload"; + + super::publish(payload.as_bytes().to_vec()) + .expect("couldn't publish the process context"); + + // The mapping must be discoverable right after publishing + find_otel_mapping().expect("couldn't find the otel mapping after publishing"); + + super::unpublish().expect("couldn't unpublish the context"); + + // After unpublishing the name must no longer appear in /proc/self/maps + assert!( + find_otel_mapping().is_err(), + "otel mapping should not be visible after unpublish" + ); + } } } From 0cc730a75522546fa30a25350ec0005880b0b2f8 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Tue, 3 Mar 2026 14:53:36 +0100 Subject: [PATCH 12/13] test: use serial_test to serialize otel process context tests Replace the hand-rolled `TEST_SERIALIZER: Mutex<()>` with the `#[serial_test::serial]` attribute applied at the module level, which is the established pattern already used by other crates in this repo. Co-Authored-By: Claude Sonnet 4.6 --- Cargo.lock | 1 + libdd-library-config/Cargo.toml | 1 + libdd-library-config/src/otel_process_ctx.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f5e51d432b..519b6b857f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3170,6 +3170,7 @@ dependencies = [ "rustix 1.1.3", "serde", "serde_yaml", + "serial_test", "tempfile", ] diff --git a/libdd-library-config/Cargo.toml b/libdd-library-config/Cargo.toml index 2af7f2f373..d8a171e875 100644 --- a/libdd-library-config/Cargo.toml +++ b/libdd-library-config/Cargo.toml @@ -25,6 +25,7 @@ rmp-serde = "1.3.0" [dev-dependencies] tempfile = { version = "3.3" } +serial_test = "3.2" [target.'cfg(unix)'.dependencies] memfd = { version = "0.6" } diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 26ed86857e..44029fec85 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -388,6 +388,7 @@ pub mod linux { } #[cfg(test)] + #[serial_test::serial] mod tests { use super::MappingHeader; use anyhow::ensure; From 1b08e3967d4d7df3ccfb5ed4c7e512e52cdef82f Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Wed, 4 Mar 2026 15:28:41 +0100 Subject: [PATCH 13/13] chore: remove useless alignment check --- libdd-library-config/src/otel_process_ctx.rs | 36 ++++++-------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/libdd-library-config/src/otel_process_ctx.rs b/libdd-library-config/src/otel_process_ctx.rs index 44029fec85..636b92837d 100644 --- a/libdd-library-config/src/otel_process_ctx.rs +++ b/libdd-library-config/src/otel_process_ctx.rs @@ -221,8 +221,6 @@ pub mod linux { let mut mapping = MemMapping::new()?; let size = mapping_size(); - check_atomic_u64_align_constraints()?; - // Safety: the invariants of MemMapping ensures `start_addr` is not null and comes // from a previous call to `mmap` unsafe { madvise(mapping.start_addr, size, Advice::LinuxDontFork) } @@ -273,16 +271,20 @@ pub mod linux { fn update(&mut self, payload: Vec) -> anyhow::Result<()> { let header = self.mapping.start_addr as *mut MappingHeader; - check_atomic_u64_align_constraints()?; - let published_at_ns = time_now_ns() .ok_or_else(|| anyhow::anyhow!("could not get the current timestamp"))?; let payload_size = payload.len().try_into().map_err(|_| { anyhow::anyhow!("couldn't update process protocol: new payload too large") })?; - // Safety: we checked the alignment constraints, and the header memory is valid for - // both read and writes. + // Safety + // + // [^atomic-u64-alignment]: Page size is at minimum 4KB and will be always 8 bytes + // aligned even on exotic platforms. The respective offsets of `signature` and + // `published_at_ns` are 0 and 16 bytes, so they are 8-bytes aligned (`AtomicU64` has + // both a size and align of 8 bytes). + // + // The header memory is valid for both read and writes. let published_at_atomic = unsafe { AtomicU64::from_ptr(addr_of_mut!((*header).published_at_ns)) }; @@ -290,7 +292,7 @@ pub mod linux { // // Note: be careful of early return while `published_at` is still zero, as this would // effectively "lock" any future publishing. Move throwing code above this swap, or - // properly restore the previous value the former can't be done. + // properly restore the previous value if the former can't be done. if published_at_atomic.swap(0, Ordering::Relaxed) == 0 { return Err(anyhow::anyhow!( "concurrent update of the process context is not supported" @@ -314,24 +316,6 @@ pub mod linux { } } - // Checks that the layout allows us to access `signature` and `published_at_ns` as - // atomics u64. - fn check_atomic_u64_align_constraints() -> anyhow::Result<()> { - // Page size is at minimum 4KB and will be always 8 bytes aligned even on - // exotic platforms. The respective offsets of `signature` and `published_at_ns` are - // 0 and 16 bytes, so it suffices for `AtomicU64` to require an alignment of at most 8 - // (which is the expected alignment anyway). - // - // Note that `align_of` is a `const fn`, so this is in fact a compile-time check and - // will be optimized away, hence the `allow(unreachable_code)`. - #[allow(unreachable_code)] - if std::mem::align_of::() <= 8 { - Ok(()) - } else { - Err(anyhow::anyhow!("alignment constraints forbid the use of atomics for publishing the protocol context")) - } - } - // Whether this size depends on the page size or not in the future, Rustix's `page_size()` // caches the value in a static atomic, so it's ok to call `mapping_size()` repeatedly; it // won't result in a syscall each time. @@ -427,7 +411,7 @@ pub mod linux { // we found in /proc/self/maps. This should be safe as long as the // mapping exists and has read permissions. // - // The atomic alignment constraints are checked during publication. + // For the alignment constraint of `AtomicU64`, see [atomic-u64-alignment]. let signature = unsafe { AtomicU64::from_ptr(ptr).load(Ordering::Relaxed) }; fence(Ordering::SeqCst); &signature.to_ne_bytes() == super::SIGNATURE