Skip to content

Commit 1e7393c

Browse files
authored
Merge pull request #1123 from alltilla/filterx-fix-indirect-object-sync
FilterX: fix borrowed/indirect objects getting overwritten by scope syncs
2 parents 70ab4da + 717a4dc commit 1e7393c

11 files changed

Lines changed: 186 additions & 25 deletions

File tree

lib/filterx/expr-template.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ _eval_template(FilterXExpr *s)
5353
* expressions, thus the FilterXObject is shorter lived than the scratch
5454
* buffer. */
5555

56-
return filterx_message_value_new_borrowed(value->str, value->len, t);
56+
return filterx_message_value_new_indirect(value->str, value->len, t);
5757
}
5858

5959
static void

lib/filterx/filterx-object.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ struct _FilterXObject
204204
*
205205
*/
206206
guint weak_referenced:1, is_dirty:1, allocator_used:1, floating_ref:1, early_allocation:1, early_allocation_checked:1,
207-
flags:5;
207+
is_nvtable_backed:1, flags:5;
208208
volatile guint32 hash;
209209
FilterXType *type;
210210
};
@@ -756,6 +756,12 @@ filterx_object_dup(FilterXObject *self)
756756
return self->type->clone(self);
757757
}
758758

759+
static inline gboolean
760+
filterx_object_is_nvtable_backed(FilterXObject *self)
761+
{
762+
return self->is_nvtable_backed;
763+
}
764+
759765
static inline FilterXObject *
760766
filterx_object_copy(FilterXObject *self)
761767
{

lib/filterx/filterx-scope.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,36 @@ filterx_scope_foreach_variable_readonly(FilterXScope *self, FilterXScopeForeachF
228228
return result;
229229
}
230230

231+
static void
232+
_filterx_scope_make_object_direct(FilterXObject **object)
233+
{
234+
if (!(*object) || !filterx_object_is_nvtable_backed(*object))
235+
return;
236+
237+
FilterXObject *direct = filterx_object_dup(*object);
238+
filterx_object_unref(*object);
239+
*object = direct;
240+
}
241+
242+
static void
243+
_filterx_scope_make_nvtable_backed_variables_direct(FilterXScope *self)
244+
{
245+
for (FilterXScope *scope = self;
246+
scope && filterx_scope_is_dirty(scope) && !filterx_scope_is_fork_point(scope);
247+
scope = scope->parent_scope)
248+
{
249+
for (guint32 i = 0; i < scope->variables_size; i++)
250+
{
251+
FilterXVariable *v = &scope->variables[i];
252+
if (!v->variable_type)
253+
continue;
254+
255+
if (filterx_variable_is_message_tied(v) || filterx_variable_is_declared(v))
256+
_filterx_scope_make_object_direct(&v->value);
257+
}
258+
}
259+
}
260+
231261
/*
232262
* 1) sync objects to message
233263
* 2) drop undeclared objects
@@ -250,6 +280,13 @@ filterx_scope_sync(FilterXScope *self, LogMessage **pmsg, const LogPathOptions *
250280
return;
251281
}
252282

283+
/*
284+
* This has to run before the sync below writes anything back to the message:
285+
* a write may overwrite the NVTable entry that a later, not-yet-duplicated
286+
* variable still points into.
287+
*/
288+
_filterx_scope_make_nvtable_backed_variables_direct(self);
289+
253290
GString *buffer = NULL;
254291

255292
gint msg_generation = self->msg->generation;

lib/filterx/object-message-value.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ _len(FilterXObject *s, guint64 *len)
399399

400400
/* NOTE: the caller must ensure that repr lives as long as the constructed object, avoids copying */
401401
FilterXObject *
402-
filterx_message_value_new_borrowed(const gchar *repr, gssize repr_len, LogMessageValueType type)
402+
filterx_message_value_new_indirect(const gchar *repr, gssize repr_len, LogMessageValueType type)
403403
{
404404
FilterXMessageValue *self = filterx_new_object(FilterXMessageValue);
405405

@@ -420,7 +420,7 @@ filterx_message_value_new(const gchar *repr, gssize repr_len, LogMessageValueTyp
420420
gchar *buf = g_malloc(len + 1);
421421
memcpy(buf, repr, len);
422422
buf[len] = 0;
423-
FilterXMessageValue *self = (FilterXMessageValue *) filterx_message_value_new_borrowed(buf, len, type);
423+
FilterXMessageValue *self = (FilterXMessageValue *) filterx_message_value_new_indirect(buf, len, type);
424424
self->buf = buf;
425425
return &self->super;
426426
}
@@ -429,7 +429,7 @@ filterx_message_value_new(const gchar *repr, gssize repr_len, LogMessageValueTyp
429429
FilterXObject *
430430
filterx_message_value_new_ref(gchar *repr, gssize repr_len, LogMessageValueType type)
431431
{
432-
FilterXMessageValue *self = (FilterXMessageValue *) filterx_message_value_new_borrowed(repr, repr_len, type);
432+
FilterXMessageValue *self = (FilterXMessageValue *) filterx_message_value_new_indirect(repr, repr_len, type);
433433
self->buf = repr;
434434
return &self->super;
435435
}
@@ -472,13 +472,20 @@ filterx_extract_object_from_logmsg(LogMessage *msg, NVHandle handle)
472472
const gchar *value = log_msg_get_value_if_set_with_type(msg, handle, &value_len, &t);
473473
if (!value)
474474
return NULL;
475+
476+
FilterXObject *self;
475477
switch (t)
476478
{
477479
case LM_VT_STRING:
478-
return filterx_string_new_borrowed(value, value_len);
480+
self = filterx_string_new_indirect(value, value_len);
481+
break;
479482
case LM_VT_NULL:
480483
return filterx_null_new();
481484
default:
482-
return filterx_message_value_new_borrowed(value, value_len, t);
485+
self = filterx_message_value_new_indirect(value, value_len, t);
486+
break;
483487
}
488+
489+
self->is_nvtable_backed = TRUE;
490+
return self;
484491
}

lib/filterx/object-message-value.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ typedef struct _FilterXMessageValue
3737

3838
FILTERX_DECLARE_TYPE(message_value);
3939

40-
FilterXObject *filterx_message_value_new_borrowed(const gchar *repr, gssize repr_len, LogMessageValueType type);
40+
FilterXObject *filterx_message_value_new_indirect(const gchar *repr, gssize repr_len, LogMessageValueType type);
4141
FilterXObject *filterx_message_value_new_ref(gchar *repr, gssize repr_len, LogMessageValueType type);
4242
FilterXObject *filterx_message_value_new(const gchar *repr, gssize repr_len, LogMessageValueType type);
4343

lib/filterx/object-string.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ _free(FilterXObject *s)
9393
FilterXString *self = (FilterXString *) s;
9494
if (self->super.flags & FILTERX_STRING_FLAG_STR_ALLOCATED)
9595
g_free((gchar *) self->str);
96-
else if (self->super.flags & FILTERX_STRING_FLAG_STR_BORROWED_SLICE)
97-
filterx_object_unref(self->storage.slice);
96+
else if (self->super.flags & FILTERX_STRING_FLAG_STR_INDIRECT)
97+
filterx_object_unref(self->storage.indirect);
9898
filterx_object_free_method(s);
9999
}
100100

@@ -393,15 +393,16 @@ filterx_string_new_from_json_literal(const gchar *str, gssize str_len)
393393
}
394394

395395
FilterXObject *
396-
_filterx_string_new_slice_from_borrowed_str_and_len(FilterXObject *object, const gchar *str, gsize str_len)
396+
_filterx_string_new_indirect_from_str_and_len(FilterXObject *object, const gchar *str, gsize str_len)
397397
{
398398
FilterXString *self = filterx_new_object(FilterXString);
399399
filterx_object_init_instance(&self->super, &FILTERX_TYPE_NAME(string));
400400

401-
self->super.flags |= FILTERX_STRING_FLAG_STR_BORROWED_SLICE;
401+
self->super.flags |= FILTERX_STRING_FLAG_STR_INDIRECT;
402+
self->super.is_nvtable_backed = object && filterx_object_is_nvtable_backed(object);
402403
self->str = str;
403404
self->str_len = str_len;
404-
self->storage.slice = filterx_object_ref(object);
405+
self->storage.indirect = filterx_object_ref(object);
405406
return &self->super;
406407
}
407408

@@ -421,7 +422,7 @@ _filterx_string_new_slice_from_non_string(FilterXObject *object, gsize start, gs
421422
if (cached)
422423
return cached;
423424

424-
return _filterx_string_new_slice_from_borrowed_str_and_len(object, str, str_len);
425+
return _filterx_string_new_indirect_from_str_and_len(object, str, str_len);
425426
}
426427

427428
FilterXObject *

lib/filterx/object-string.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
/* the string holds a value that does not need escaping when generating a JSON string literal */
3232
#define FILTERX_STRING_FLAG_NO_JSON_ESCAPE_NEEDED 0x02
3333

34-
/* string is borrowing its payload from another string */
35-
#define FILTERX_STRING_FLAG_STR_BORROWED_SLICE 0x04
34+
/* the string borrows its payload from another object (storage.indirect) instead of owning it,
35+
* similar to NVEntry's indirect in lib/logmsg/nvtable.h */
36+
#define FILTERX_STRING_FLAG_STR_INDIRECT 0x04
3637

3738
/* cache indices */
3839
enum
@@ -61,7 +62,7 @@ struct _FilterXString
6162
guint32 str_len;
6263
union
6364
{
64-
FilterXObject *slice;
65+
FilterXObject *indirect;
6566
gchar bytes[0];
6667
} storage;
6768
};
@@ -225,7 +226,7 @@ filterx_string_new(const gchar *str, gssize str_len)
225226
}
226227

227228
/* slow paths */
228-
FilterXObject *_filterx_string_new_slice_from_borrowed_str_and_len(FilterXObject *object, const gchar *str,
229+
FilterXObject *_filterx_string_new_indirect_from_str_and_len(FilterXObject *object, const gchar *str,
229230
gsize str_len);
230231
FilterXObject *_filterx_string_new_slice_from_non_string(FilterXObject *object, gsize start, gsize end);
231232

@@ -249,16 +250,16 @@ filterx_string_new_slice(FilterXObject *object, gsize start, gsize end)
249250
return cached;
250251
if (slice_len < FILTERX_STRING_SHORT_SLICE_LIMIT)
251252
return _filterx_string_new(slice_start, slice_len);
252-
return _filterx_string_new_slice_from_borrowed_str_and_len(object, slice_start, slice_len);
253+
return _filterx_string_new_indirect_from_str_and_len(object, slice_start, slice_len);
253254
}
254255

255256
return _filterx_string_new_slice_from_non_string(object, start, end);
256257
}
257258

258259
static inline FilterXObject *
259-
filterx_string_new_borrowed(const gchar *str, gssize str_len)
260+
filterx_string_new_indirect(const gchar *str, gssize str_len)
260261
{
261-
return _filterx_string_new_slice_from_borrowed_str_and_len(NULL, str, str_len);
262+
return _filterx_string_new_indirect_from_str_and_len(NULL, str, str_len);
262263
}
263264

264265
void filterx_string_global_init(void);

lib/filterx/tests/test_object_message.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Test(filterx_message, test_filterx_object_message_marshals_to_the_stored_values)
4242

4343
gchar borrowed_value[] = "string";
4444

45-
fobj = filterx_message_value_new_borrowed(borrowed_value, -1, LM_VT_STRING);
45+
fobj = filterx_message_value_new_indirect(borrowed_value, -1, LM_VT_STRING);
4646
assert_marshaled_object(fobj, "string", LM_VT_STRING);
4747
borrowed_value[0]++;
4848
assert_marshaled_object(fobj, "ttring", LM_VT_STRING);
@@ -65,7 +65,7 @@ Test(filterx_message, test_filterx_object_value_maps_to_the_right_json_value)
6565

6666
gchar borrowed_value[] = "string";
6767

68-
fobj = filterx_message_value_new_borrowed(borrowed_value, -1, LM_VT_STRING);
68+
fobj = filterx_message_value_new_indirect(borrowed_value, -1, LM_VT_STRING);
6969
assert_object_json_equals(fobj, "\"string\"");
7070
borrowed_value[0]++;
7171
assert_object_json_equals(fobj, "\"ttring\"");

lib/filterx/tests/test_object_string.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Test(filterx_string, test_string_taking_a_short_slice_of_another_string)
8080
FilterXObject *str2 = filterx_string_new_slice(str, 1, 3);
8181

8282
assert_object_json_equals(str2, "\"bc\"");
83-
cr_assert((str2->flags & FILTERX_STRING_FLAG_STR_BORROWED_SLICE) == 0);
83+
cr_assert((str2->flags & FILTERX_STRING_FLAG_STR_INDIRECT) == 0);
8484
filterx_object_unref(str2);
8585
filterx_object_unref(str);
8686
}
@@ -91,7 +91,7 @@ Test(filterx_string, test_string_taking_a_long_slice_of_another_string)
9191
FilterXObject *str2 = filterx_string_new_slice(str, 1, 15);
9292

9393
assert_object_json_equals(str2, "\"123456789abcde\"");
94-
cr_assert((str2->flags & FILTERX_STRING_FLAG_STR_BORROWED_SLICE) != 0);
94+
cr_assert((str2->flags & FILTERX_STRING_FLAG_STR_INDIRECT) != 0);
9595
filterx_object_unref(str2);
9696
filterx_object_unref(str);
9797
}
@@ -107,6 +107,30 @@ Test(filterx_string, test_string_taking_an_cached_string_slice_results_in_a_cach
107107
filterx_object_unref(str);
108108
}
109109

110+
Test(filterx_string, test_string_slice_propagates_nvtable_backed_marking)
111+
{
112+
/* simulate a value pulled from the message NVTable (the marking is normally
113+
* done by filterx_extract_object_from_logmsg(), not by the constructor) */
114+
FilterXObject *nvtable_backed = filterx_string_new_indirect("0123456789abcdef", 16);
115+
nvtable_backed->is_nvtable_backed = TRUE;
116+
117+
/* a slice of an NVTable-backed string is itself NVTable-backed */
118+
FilterXObject *slice = filterx_string_new_slice(nvtable_backed, 1, 15);
119+
cr_assert(filterx_object_is_nvtable_backed(slice));
120+
121+
/* a slice of a self-owned string is not NVTable-backed, even though it
122+
* borrows the payload */
123+
FilterXObject *owned = filterx_string_new_take(g_strdup("0123456789abcdef"), 16);
124+
FilterXObject *owned_slice = filterx_string_new_slice(owned, 1, 15);
125+
cr_assert(!filterx_object_is_nvtable_backed(owned_slice));
126+
cr_assert((owned_slice->flags & FILTERX_STRING_FLAG_STR_INDIRECT) != 0);
127+
128+
filterx_object_unref(slice);
129+
filterx_object_unref(nvtable_backed);
130+
filterx_object_unref(owned_slice);
131+
filterx_object_unref(owned);
132+
}
133+
110134
static void
111135
_translate_to_incremented(gchar *target, const gchar *source, gsize *len)
112136
{

news/bugfix-1123.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`filterx`: Fixed a bug where a value read from a log message field could be corrupted when that field was overwritten later in the same flow.

0 commit comments

Comments
 (0)