Skip to content

Commit 2f8ffa9

Browse files
authored
[mem store] Return clones of spans to prevent in-place changes by adjusters (#2720)
* Copy spans from memory store, fixes #2719 Copying allows spans to be freely modified by adjusters and any other code without accidentally altering what is stored in the in-memory store itself. Signed-off-by: Ivan Babrou <github@ivan.computer> * Add tests to exercise the broken serialization path Signed-off-by: Ivan Babrou <github@ivan.computer>
1 parent e788e55 commit 2f8ffa9

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

plugin/storage/memory/memory.go

+17-6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"sync"
2323
"time"
2424

25+
"github.com/golang/protobuf/proto"
26+
2527
"github.com/jaegertracing/jaeger/model"
2628
"github.com/jaegertracing/jaeger/model/adjuster"
2729
"github.com/jaegertracing/jaeger/pkg/memory/config"
@@ -164,15 +166,19 @@ func (m *Store) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Tra
164166
if !ok {
165167
return nil, spanstore.ErrTraceNotFound
166168
}
167-
return m.copyTrace(trace), nil
169+
return m.copyTrace(trace)
168170
}
169171

170172
// Spans may still be added to traces after they are returned to user code, so make copies.
171-
func (m *Store) copyTrace(trace *model.Trace) *model.Trace {
172-
return &model.Trace{
173-
Spans: append([]*model.Span(nil), trace.Spans...),
174-
Warnings: append([]string(nil), trace.Warnings...),
173+
func (m *Store) copyTrace(trace *model.Trace) (*model.Trace, error) {
174+
bytes, err := proto.Marshal(trace)
175+
if err != nil {
176+
return nil, err
175177
}
178+
179+
copied := &model.Trace{}
180+
err = proto.Unmarshal(bytes, copied)
181+
return copied, err
176182
}
177183

178184
// GetServices returns a list of all known services
@@ -211,7 +217,12 @@ func (m *Store) FindTraces(ctx context.Context, query *spanstore.TraceQueryParam
211217
var retMe []*model.Trace
212218
for _, trace := range m.traces {
213219
if m.validTrace(trace, query) {
214-
retMe = append(retMe, m.copyTrace(trace))
220+
copied, err := m.copyTrace(trace)
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
retMe = append(retMe, copied)
215226
}
216227
}
217228

plugin/storage/memory/memory_test.go

+48-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var testingSpan = &model.Span{
3434
SpanID: model.NewSpanID(1),
3535
Process: &model.Process{
3636
ServiceName: "serviceName",
37-
Tags: model.KeyValues{},
37+
Tags: []model.KeyValue(nil),
3838
},
3939
OperationName: "operationName",
4040
Tags: model.KeyValues{
@@ -43,14 +43,14 @@ var testingSpan = &model.Span{
4343
},
4444
Logs: []model.Log{
4545
{
46-
Timestamp: time.Now(),
46+
Timestamp: time.Now().UTC(),
4747
Fields: []model.KeyValue{
4848
model.String("logKey", "logValue"),
4949
},
5050
},
5151
},
5252
Duration: time.Second * 5,
53-
StartTime: time.Unix(300, 0),
53+
StartTime: time.Unix(300, 0).UTC(),
5454
}
5555

5656
var childSpan1 = &model.Span{
@@ -128,6 +128,14 @@ var childSpan2_1 = &model.Span{
128128
StartTime: time.Unix(300, 0),
129129
}
130130

131+
// This kind of trace cannot be serialized
132+
var nonSerializableSpan = &model.Span{
133+
Process: &model.Process{
134+
ServiceName: "naughtyService",
135+
},
136+
StartTime: time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC),
137+
}
138+
131139
func withPopulatedMemoryStore(f func(store *Store)) {
132140
memStore := NewStore()
133141
memStore.WriteSpan(context.Background(), testingSpan)
@@ -210,6 +218,34 @@ func TestStoreGetTraceSuccess(t *testing.T) {
210218
})
211219
}
212220

221+
func TestStoreGetAndMutateTrace(t *testing.T) {
222+
withPopulatedMemoryStore(func(store *Store) {
223+
trace, err := store.GetTrace(context.Background(), testingSpan.TraceID)
224+
assert.NoError(t, err)
225+
assert.Len(t, trace.Spans, 1)
226+
assert.Equal(t, testingSpan, trace.Spans[0])
227+
assert.Len(t, trace.Spans[0].Warnings, 0)
228+
229+
trace.Spans[0].Warnings = append(trace.Spans[0].Warnings, "the end is near")
230+
231+
trace, err = store.GetTrace(context.Background(), testingSpan.TraceID)
232+
assert.NoError(t, err)
233+
assert.Len(t, trace.Spans, 1)
234+
assert.Equal(t, testingSpan, trace.Spans[0])
235+
assert.Len(t, trace.Spans[0].Warnings, 0)
236+
})
237+
}
238+
239+
func TestStoreGetTraceError(t *testing.T) {
240+
withPopulatedMemoryStore(func(store *Store) {
241+
store.traces[testingSpan.TraceID] = &model.Trace{
242+
Spans: []*model.Span{nonSerializableSpan},
243+
}
244+
_, err := store.GetTrace(context.Background(), testingSpan.TraceID)
245+
assert.Error(t, err)
246+
})
247+
}
248+
213249
func TestStoreGetTraceFailure(t *testing.T) {
214250
withPopulatedMemoryStore(func(store *Store) {
215251
trace, err := store.GetTrace(context.Background(), model.TraceID{})
@@ -282,6 +318,15 @@ func TestStoreGetEmptyTraceSet(t *testing.T) {
282318
})
283319
}
284320

321+
func TestStoreFindTracesError(t *testing.T) {
322+
withPopulatedMemoryStore(func(store *Store) {
323+
err := store.WriteSpan(context.Background(), nonSerializableSpan)
324+
assert.NoError(t, err)
325+
_, err = store.FindTraces(context.Background(), &spanstore.TraceQueryParameters{ServiceName: "naughtyService"})
326+
assert.Error(t, err)
327+
})
328+
}
329+
285330
func TestStoreFindTracesLimitGetsMostRecent(t *testing.T) {
286331
storeSize, querySize := 100, 10
287332

0 commit comments

Comments
 (0)