Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEBUG-2334 duplicate mutable values when serializing for dynamic inst… #4009

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/datadog/di/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ module DI
# All serialization methods take the names of the variables being
# serialized in order to be able to redact values.
#
# The result of serialization should not reference parameter values when
# the values are mutable (currently, this only applies to string values).
# Serializer will duplicate such mutable values, so that if method
# arguments are captured at entry and then modified during method execution,
# the serialized values from entry are correctly preserved.
# Alternatively, we could pass a parameter to the serialization methods
# which would control whether values are duplicated. This may be more
# efficient but there would be additional overhead from passing this
# parameter all the time and the API would get more complex.
#
# @api private
class Serializer
def initialize(settings, redactor)
Expand Down Expand Up @@ -92,12 +102,24 @@ def serialize_value(value, name: nil, depth: settings.dynamic_instrumentation.ma
when Integer, Float, TrueClass, FalseClass
serialized.update(value: value.to_s)
when String, Symbol
value = value.to_s
need_dup = false
value = if String === value
p-datadog marked this conversation as resolved.
Show resolved Hide resolved
# This is the only place where we duplicate the value, currently.
# All other values are immutable primitives (e.g. numbers).
# However, do not duplicate if the string is frozen, or if
# it is later truncated.
need_dup = !value.frozen?
value
marcotc marked this conversation as resolved.
Show resolved Hide resolved
else
value.to_s
end
max = settings.dynamic_instrumentation.max_capture_string_length
if value.length > max
serialized.update(truncated: true, size: value.length)
value = value[0...max]
need_dup = false
end
value = value.dup if need_dup
serialized.update(value: value)
when Array
if depth < 0
Expand Down
74 changes: 74 additions & 0 deletions spec/datadog/di/serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -320,5 +320,79 @@ def self.define_cases(cases)
end
end
end

context 'when positional arg is mutated' do
let(:args) do
['hello', 'world']
end

let(:kwargs) { {} }

it 'preserves original value' do
serialized

args.first.gsub!('hello', 'bye')

expect(serialized).to eq(
arg1: {type: 'String', value: 'hello'},
arg2: {type: 'String', value: 'world'},
)
end
end

context 'when keyword arg is mutated' do
let(:args) do
[]
end

let(:kwargs) do
{foo: 'bar'}
end

it 'preserves original value' do
serialized

kwargs[:foo].gsub!('bar', 'bye')

expect(serialized).to eq(
foo: {type: 'String', value: 'bar'},
)
end
end

context 'when positional arg is frozen' do
let(:frozen_string) { 'hello'.freeze }

let(:args) do
[frozen_string, 'world']
end

let(:kwargs) { {} }

it 'serializes without duplication' do
expect(serialized).to eq(
arg1: {type: 'String', value: 'hello'},
arg2: {type: 'String', value: 'world'},
)

expect(serialized[:arg1][:value]).to be frozen_string
end
end

context 'when keyword arg is frozen' do
let(:frozen_string) { 'hello'.freeze }

let(:args) { [] }

let(:kwargs) { {foo: frozen_string} }

it 'serializes without duplication' do
expect(serialized).to eq(
foo: {type: 'String', value: 'hello'},
)

expect(serialized[:foo][:value]).to be frozen_string
end
end
end
end
Loading