diff --git a/libdd-trace-utils/src/msgpack_decoder/decode/buffer.rs b/libdd-trace-utils/src/msgpack_decoder/decode/buffer.rs index 4ec889155a..c027eb1c1b 100644 --- a/libdd-trace-utils/src/msgpack_decoder/decode/buffer.rs +++ b/libdd-trace-utils/src/msgpack_decoder/decode/buffer.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::msgpack_decoder::decode::error::DecodeError; -use crate::span::DeserializableTraceData; +use crate::span::{DeserializableTraceData, DeserializableTraceDataLt}; use rmp::decode; use rmp::decode::DecodeStringError; @@ -72,6 +72,17 @@ impl Buffer { } } +/// Prototype: additive counterpart to `as_mut_slice`, using [`DeserializableTraceDataLt`]'s +/// honest lifetime `'x` instead of an erased `'static`. Kept in a separate `impl` block so the +/// original struct definition and `impl Buffer` above — and every +/// existing caller of `as_mut_slice` — are untouched. +impl<'x, T: DeserializableTraceDataLt<'x>> Buffer { + /// Returns a mutable reference to the underlying slice, with its genuine lifetime `'x`. + pub fn as_mut_slice_lt(&mut self) -> &mut &'x [u8] { + T::get_mut_slice_lt(&mut self.0) + } +} + impl Deref for Buffer { type Target = [u8]; diff --git a/libdd-trace-utils/src/msgpack_decoder/v1/mod.rs b/libdd-trace-utils/src/msgpack_decoder/v1/mod.rs index 05e49cd29c..c21c3fcebb 100644 --- a/libdd-trace-utils/src/msgpack_decoder/v1/mod.rs +++ b/libdd-trace-utils/src/msgpack_decoder/v1/mod.rs @@ -6,7 +6,7 @@ pub(super) mod span; use crate::msgpack_decoder::decode::buffer::Buffer; use crate::msgpack_decoder::decode::error::DecodeError; use crate::span::v1::{TraceChunk, TracerPayload, TracerPayloadBytes, TracerPayloadSlice}; -use crate::span::DeserializableTraceData; +use crate::span::{DeserializableTraceData, DeserializableTraceDataLt}; use rmp::decode; use std::borrow::Borrow; @@ -196,7 +196,7 @@ pub fn from_slice(data: &[u8]) -> Result<(TracerPayloadSlice<'_>, usize), Decode } /// Generic over the deserialization mode (owned `BytesData` or borrowed `SliceData`). -pub fn from_buffer( +pub fn from_buffer<'x, T: DeserializableTraceDataLt<'x>>( data: &mut Buffer, ) -> Result<(TracerPayload, usize), DecodeError> where @@ -216,7 +216,7 @@ where /// Any inline string encountered while skipping (at any nesting depth) is interned into `table`, /// same as a recognized field would: skipping a value must not desync later back-references to /// strings that happen to also appear inside it. -pub(super) fn skip_unknown_value( +pub(super) fn skip_unknown_value<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result<(), DecodeError> @@ -225,10 +225,11 @@ where { // Snapshot the buffer's owning handle *before* advancing past the skipped value: any string // found inside it will be a substring of this exact allocation, so this is what - // `T::intern_skipped_str` must derive ownership from. Cloning is cheap (a refcount bump for - // `T::Bytes = Bytes`), unaffected by the lied `'static` lifetime `as_mut_slice` exposes. + // `T::intern_skipped_str_lt` must derive ownership from. Cloning is cheap (a refcount bump + // for `T::Bytes = Bytes`) and, for `SliceData`, `value`'s `'x` below is the buffer's real + // lifetime rather than a lie, so `owner` only matters for the `BytesData` case. let owner = buf.bytes().clone(); - let value = rmpv::decode::read_value_ref(buf.as_mut_slice()) + let value = rmpv::decode::read_value_ref(buf.as_mut_slice_lt()) .map_err(|_| DecodeError::InvalidFormat("Failed to skip unknown V1 value".to_owned()))?; record_strings_in_value_ref::(&value, &owner, table); Ok(()) @@ -239,11 +240,11 @@ where /// [`read_interned_string`]'s own encoder-side counterpart, so they can't be the target of a /// later back-reference either. /// -/// `owner` must be a snapshot of the buffer taken before it was advanced past `value`: the -/// strings inside `value` report a lied `'static` lifetime (see `Buffer::as_mut_slice`) but -/// really borrow from `owner`'s memory. -fn record_strings_in_value_ref( - value: &rmpv::ValueRef<'static>, +/// `owner` must be a snapshot of the buffer taken before it was advanced past `value`. Unlike the +/// `'static`-erased version, `value`'s lifetime `'x` here is genuine: for `SliceData<'a>`, `'x` is +/// `'a` itself, not a lie. +fn record_strings_in_value_ref<'x, T: DeserializableTraceDataLt<'x>>( + value: &rmpv::ValueRef<'x>, owner: &T::Bytes, table: &mut StringTable, ) where @@ -252,7 +253,7 @@ fn record_strings_in_value_ref( match value { rmpv::ValueRef::String(s) => { if let Some(s) = (*s).into_str() { - table.record(&T::intern_skipped_str(owner, s)); + table.record(&T::intern_skipped_str_lt(owner, s)); } } rmpv::ValueRef::Array(items) => { @@ -271,7 +272,7 @@ fn record_strings_in_value_ref( } /// Decodes the top-level V1 payload map: tracer metadata fields + chunks array. -fn decode_payload( +fn decode_payload<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> @@ -319,7 +320,7 @@ where Ok(payload) } -fn decode_chunks( +fn decode_chunks<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result>, DecodeError> @@ -335,7 +336,7 @@ where Ok(chunks) } -fn decode_chunk( +fn decode_chunk<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> diff --git a/libdd-trace-utils/src/msgpack_decoder/v1/span.rs b/libdd-trace-utils/src/msgpack_decoder/v1/span.rs index e1064e9958..e8a08a3977 100644 --- a/libdd-trace-utils/src/msgpack_decoder/v1/span.rs +++ b/libdd-trace-utils/src/msgpack_decoder/v1/span.rs @@ -14,7 +14,7 @@ use crate::msgpack_decoder::decode::buffer::Buffer; use crate::msgpack_decoder::decode::error::DecodeError; use crate::span::v1::{AttributeValue, Span, SpanEvent, SpanKind, SpanLink, ThinVec}; use crate::span::vec_map::VecMap; -use crate::span::DeserializableTraceData; +use crate::span::{DeserializableTraceData, DeserializableTraceDataLt}; use rmp::decode; use std::borrow::Borrow; @@ -22,7 +22,7 @@ use std::borrow::Borrow; /// /// The streaming `StringTable` is shared across the whole payload, so interned references in /// this span can resolve to strings that appeared in an earlier chunk or payload header. -pub(super) fn decode_span( +pub(super) fn decode_span<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> @@ -111,7 +111,7 @@ where } /// Reads a V1 attributes map encoded as a flat array of `[key, type_uint8, value, ...]` triplets. -pub(super) fn read_attributes_map( +pub(super) fn read_attributes_map<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result>, DecodeError> @@ -144,7 +144,7 @@ where /// Reads `[type_uint8, value]` and dispatches by type discriminant. Recurses into `Array` and /// `KeyValueList`. -pub(super) fn read_typed_attribute_value( +pub(super) fn read_typed_attribute_value<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> @@ -214,7 +214,7 @@ fn read_bin(buf: &mut Buffer) -> Result( +pub(super) fn read_span_links<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result>, DecodeError> @@ -230,7 +230,7 @@ where Ok(links) } -fn decode_span_link( +fn decode_span_link<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> @@ -282,7 +282,7 @@ where Ok(link) } -pub(super) fn read_span_events( +pub(super) fn read_span_events<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result>, DecodeError> @@ -298,7 +298,7 @@ where Ok(events) } -fn decode_span_event( +fn decode_span_event<'x, T: DeserializableTraceDataLt<'x>>( buf: &mut Buffer, table: &mut StringTable, ) -> Result, DecodeError> diff --git a/libdd-trace-utils/src/span/mod.rs b/libdd-trace-utils/src/span/mod.rs index be2a3d3caf..97943af7fb 100644 --- a/libdd-trace-utils/src/span/mod.rs +++ b/libdd-trace-utils/src/span/mod.rs @@ -210,6 +210,53 @@ impl<'a> DeserializableTraceData for SliceData<'a> { } } +/// Prototype: an honest-lifetime counterpart to [`DeserializableTraceData`]. +/// +/// `get_mut_slice`/`intern_skipped_str` report `'static` for every implementor, even though for +/// `BytesData` the backing `Bytes` is heap-owned and never truly `'static` — the lifetime is +/// erased via `transmute` and "laundered" by immediately deriving an owned `Self::Text` before +/// it can escape into safe code. For `SliceData<'a>` this erasure is pure overhead: `Self::Bytes` +/// is already `&'a [u8]`, the real target type, so the transmute exists only to satisfy a trait +/// signature that can't express `'a` generically. +/// +/// This trait carries the genuine lifetime `'x` explicitly, so `SliceData<'a>` can implement it +/// with no unsafe code at all. It extends `DeserializableTraceData` rather than replacing it, and +/// `Buffer` gains an additive method (`as_mut_slice_lt`) in a new `impl` block — every existing +/// call site on `DeserializableTraceData`/`Buffer::as_mut_slice` is untouched. +pub trait DeserializableTraceDataLt<'x>: DeserializableTraceData { + fn get_mut_slice_lt(buf: &mut Self::Bytes) -> &mut &'x [u8]; + + /// Same contract as [`DeserializableTraceData::intern_skipped_str`], but `s` carries the + /// real lifetime `'x` instead of an erased `'static`. + fn intern_skipped_str_lt(owner: &Self::Bytes, s: &'x str) -> Self::Text; +} + +impl DeserializableTraceDataLt<'static> for BytesData { + #[inline] + fn get_mut_slice_lt(buf: &mut Bytes) -> &mut &'static [u8] { + Self::get_mut_slice(buf) + } + + #[inline] + fn intern_skipped_str_lt(owner: &Bytes, s: &'static str) -> BytesString { + Self::intern_skipped_str(owner, s) + } +} + +impl<'a> DeserializableTraceDataLt<'a> for SliceData<'a> { + #[inline] + fn get_mut_slice_lt<'b>(buf: &'b mut Self::Bytes) -> &'b mut &'a [u8] { + // No transmute needed: `Self::Bytes` is already `&'a [u8]`, the exact type this method + // claims to return — the `'static` version above only needed unsafe code to lie about it. + buf + } + + #[inline] + fn intern_skipped_str_lt(_owner: &&'a [u8], s: &'a str) -> Cow<'a, str> { + Cow::Borrowed(s) + } +} + #[derive(Debug)] pub struct SpanKeyParseError { pub message: String,