1 /*
2  * Copyright (c) 2002-2016 Balabit
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * As an additional exemption you are allowed to compile & link against the
19  * OpenSSL libraries as published by the OpenSSL project. See the file
20  * COPYING for details.
21  *
22  */
23 
24 #include <criterion/criterion.h>
25 
26 #include "msg_parse_lib.h"
27 #include "apphook.h"
28 #include "logpipe.h"
29 #include "rcptid.h"
30 #include "libtest/persist_lib.h"
31 
32 #include <stdlib.h>
33 #include <glib/gprintf.h>
34 
35 MsgFormatOptions parse_options;
36 
37 typedef struct _LogMessageTestParams
38 {
39   LogMessage *message;
40   LogMessage *cloned_message;
41   NVHandle nv_handle;
42   NVHandle sd_handle;
43   const gchar *tag_name;
44 } LogMessageTestParams;
45 
46 static LogMessage *
_construct_log_message(void)47 _construct_log_message(void)
48 {
49   const gchar *raw_msg = "foo";
50   LogMessage *msg;
51 
52   msg = log_msg_new(raw_msg, strlen(raw_msg), &parse_options);
53   log_msg_set_value(msg, LM_V_HOST, raw_msg, -1);
54   return msg;
55 }
56 
57 static LogMessage *
_construct_merge_base_message(void)58 _construct_merge_base_message(void)
59 {
60   LogMessage *msg;
61 
62   msg = log_msg_new_empty();
63   log_msg_set_value_by_name(msg, "base", "basevalue", -1);
64   log_msg_set_tag_by_name(msg, "basetag");
65   return msg;
66 }
67 
68 static LogMessage *
_construct_merged_message(const gchar * name,const gchar * value)69 _construct_merged_message(const gchar *name, const gchar *value)
70 {
71   LogMessage *msg;
72 
73   msg = log_msg_new_empty();
74   log_msg_set_value_by_name(msg, name, value, -1);
75   log_msg_set_tag_by_name(msg, "mergedtag");
76   return msg;
77 }
78 
79 static void
assert_log_msg_clear_clears_all_properties(LogMessage * message,NVHandle nv_handle,NVHandle sd_handle,const gchar * tag_name)80 assert_log_msg_clear_clears_all_properties(LogMessage *message, NVHandle nv_handle,
81                                            NVHandle sd_handle, const gchar *tag_name)
82 {
83   message->flags |= LF_LOCAL + LF_UTF8 + LF_INTERNAL + LF_MARK;
84   log_msg_clear(message);
85 
86   cr_assert_str_empty(log_msg_get_value(message, nv_handle, NULL),
87                       "Message still contains value after log_msg_clear");
88 
89   cr_assert_str_empty(log_msg_get_value(message, sd_handle, NULL),
90                       "Message still contains sdata value after log_msg_clear");
91 
92   cr_assert_null(message->saddr, "Message still contains an saddr after log_msg_clear");
93   cr_assert_not(log_msg_is_tag_by_name(message, tag_name),
94                 "Message still contains a valid tag after log_msg_clear");
95   cr_assert((message->flags & LF_LOCAL) == 0, "Message still contains the 'local' flag after log_msg_clear");
96   cr_assert((message->flags & LF_UTF8) == 0, "Message still contains the 'utf8' flag after log_msg_clear");
97   cr_assert((message->flags & LF_MARK) == 0, "Message still contains the 'mark' flag after log_msg_clear");
98   cr_assert((message->flags & LF_INTERNAL) == 0, "Message still contains the 'internal' flag after log_msg_clear");
99 }
100 
101 static void
assert_sdata_value_with_seqnum_equals(LogMessage * msg,guint32 seq_num,const gchar * expected)102 assert_sdata_value_with_seqnum_equals(LogMessage *msg, guint32 seq_num, const gchar *expected)
103 {
104   GString *result = g_string_sized_new(0);
105 
106   log_msg_append_format_sdata(msg, result, seq_num);
107   cr_assert_str_eq(result->str, expected, "SDATA value does not match, '%s' vs '%s'", expected, result->str);
108   g_string_free(result, TRUE);
109 }
110 
111 static void
assert_sdata_value_equals(LogMessage * msg,const gchar * expected)112 assert_sdata_value_equals(LogMessage *msg, const gchar *expected)
113 {
114   assert_sdata_value_with_seqnum_equals(msg, 0, expected);
115 }
116 
117 static LogMessageTestParams *
log_message_test_params_new(void)118 log_message_test_params_new(void)
119 {
120   LogMessageTestParams *params = g_new0(LogMessageTestParams, 1);
121 
122   params->tag_name = "tag";
123   params->message = _construct_log_message();
124 
125   params->nv_handle = log_msg_get_value_handle("foo");
126   params->sd_handle = log_msg_get_value_handle(".SDATA.foo.bar");
127 
128   log_msg_set_value(params->message, params->nv_handle, "value", -1);
129   log_msg_set_value(params->message, params->sd_handle, "value", -1);
130   params->message->saddr = g_sockaddr_inet_new("1.2.3.4", 5050);
131   log_msg_set_tag_by_name(params->message, params->tag_name);
132 
133   return params;
134 }
135 
136 void
log_message_test_params_free(LogMessageTestParams * params)137 log_message_test_params_free(LogMessageTestParams *params)
138 {
139   log_msg_unref(params->message);
140 
141   if (params->cloned_message)
142     log_msg_unref(params->cloned_message);
143 
144   g_free(params);
145 }
146 
147 LogMessage *
log_message_test_params_clone_message(LogMessageTestParams * params)148 log_message_test_params_clone_message(LogMessageTestParams *params)
149 {
150   LogPathOptions path_options = LOG_PATH_OPTIONS_INIT;
151   params->cloned_message = log_msg_clone_cow(params->message, &path_options);
152 
153   return params->cloned_message;
154 }
155 
156 
157 void
setup(void)158 setup(void)
159 {
160   app_startup();
161   init_parse_options_and_load_syslogformat(&parse_options);
162 }
163 
164 void
teardown(void)165 teardown(void)
166 {
167   deinit_syslogformat_module();
168   app_shutdown();
169 }
170 
171 TestSuite(log_message, .init = setup, .fini = teardown);
172 
Test(log_message,test_log_message_can_be_created_and_freed)173 Test(log_message, test_log_message_can_be_created_and_freed)
174 {
175   LogMessage *msg = _construct_log_message();
176   log_msg_unref(msg);
177 }
178 
Test(log_message,test_log_message_can_be_cleared)179 Test(log_message, test_log_message_can_be_cleared)
180 {
181   LogMessageTestParams *params = log_message_test_params_new();
182 
183   log_message_test_params_clone_message(params);
184 
185   assert_log_msg_clear_clears_all_properties(params->message, params->nv_handle,
186                                              params->sd_handle, params->tag_name);
187   assert_log_msg_clear_clears_all_properties(params->cloned_message, params->nv_handle,
188                                              params->sd_handle, params->tag_name);
189 
190   log_message_test_params_free(params);
191 }
192 
Test(log_message,test_log_msg_clear_handles_cloned_noninline_tags_properly)193 Test(log_message, test_log_msg_clear_handles_cloned_noninline_tags_properly)
194 {
195   LogMessage *msg = _construct_log_message();
196 
197   for (gint i = 0; i < 100; i++)
198     {
199       gchar tag_name[32];
200 
201       g_snprintf(tag_name, sizeof(tag_name), "tag%d", i);
202       log_msg_set_tag_by_name(msg, tag_name);
203     }
204 
205   LogPathOptions path_options = LOG_PATH_OPTIONS_INIT;
206   LogMessage *cloned = log_msg_clone_cow(msg, &path_options);
207 
208   log_msg_clear(cloned);
209 
210   for (gint i = 0; i < 100; i++)
211     {
212       gchar tag_name[32];
213 
214       g_snprintf(tag_name, sizeof(tag_name), "tag%d", i);
215       cr_assert(log_msg_is_tag_by_name(cloned, tag_name) == FALSE);
216     }
217   log_msg_unref(cloned);
218   log_msg_unref(msg);
219 
220 }
221 
Test(log_message,test_rcptid_is_automatically_assigned_to_a_newly_created_log_message)222 Test(log_message, test_rcptid_is_automatically_assigned_to_a_newly_created_log_message)
223 {
224   LogMessage *msg;
225   PersistState *state = clean_and_create_persist_state_for_test("test_values.persist");
226   rcptid_init(state, TRUE);
227 
228   msg = log_msg_new_empty();
229   cr_assert_eq(msg->rcptid, 1, "rcptid is not automatically set");
230   log_msg_unref(msg);
231 
232   commit_and_destroy_persist_state(state);
233   rcptid_deinit();
234 }
235 
Test(log_message,test_log_message_merge_with_empty_context)236 Test(log_message, test_log_message_merge_with_empty_context)
237 {
238   LogMessageTestParams *params = log_message_test_params_new();
239   LogMessage *context[] = {};
240 
241   log_message_test_params_clone_message(params);
242 
243   log_msg_merge_context(params->message, context, 0);
244   assert_log_messages_equal(params->message, params->cloned_message);
245 
246   log_message_test_params_free(params);
247 }
248 
Test(log_message,test_log_message_merge_unset_value)249 Test(log_message, test_log_message_merge_unset_value)
250 {
251   LogMessage *msg;
252   GPtrArray *context = g_ptr_array_sized_new(0);
253 
254   msg = _construct_merge_base_message();
255   g_ptr_array_add(context, _construct_merged_message("merged", "mergedvalue"));
256   log_msg_merge_context(msg, (LogMessage **) context->pdata, context->len);
257 
258   assert_log_message_value_by_name(msg, "base", "basevalue");
259   assert_log_message_value_by_name(msg, "merged", "mergedvalue");
260   g_ptr_array_foreach(context, (GFunc) log_msg_unref, NULL);
261   g_ptr_array_free(context, TRUE);
262   log_msg_unref(msg);
263 }
264 
Test(log_message,test_log_message_merge_doesnt_overwrite_already_set_values)265 Test(log_message, test_log_message_merge_doesnt_overwrite_already_set_values)
266 {
267   LogMessage *msg;
268   GPtrArray *context = g_ptr_array_sized_new(0);
269 
270   msg = _construct_merge_base_message();
271   g_ptr_array_add(context, _construct_merged_message("base", "mergedvalue"));
272   log_msg_merge_context(msg, (LogMessage **) context->pdata, context->len);
273 
274   assert_log_message_value_by_name(msg, "base", "basevalue");
275   g_ptr_array_foreach(context, (GFunc) log_msg_unref, NULL);
276   g_ptr_array_free(context, TRUE);
277   log_msg_unref(msg);
278 }
279 
Test(log_message,test_log_message_merge_merges_the_closest_value_in_the_context)280 Test(log_message, test_log_message_merge_merges_the_closest_value_in_the_context)
281 {
282   LogMessage *msg;
283   GPtrArray *context = g_ptr_array_sized_new(0);
284 
285   msg = _construct_merge_base_message();
286   g_ptr_array_add(context, _construct_merged_message("merged", "mergedvalue1"));
287   g_ptr_array_add(context, _construct_merged_message("merged", "mergedvalue2"));
288   log_msg_merge_context(msg, (LogMessage **) context->pdata, context->len);
289 
290   assert_log_message_value_by_name(msg, "merged", "mergedvalue2");
291   g_ptr_array_foreach(context, (GFunc) log_msg_unref, NULL);
292   g_ptr_array_free(context, TRUE);
293   log_msg_unref(msg);
294 }
295 
Test(log_message,test_log_message_merge_merges_from_all_messages_in_the_context)296 Test(log_message, test_log_message_merge_merges_from_all_messages_in_the_context)
297 {
298   LogMessage *msg;
299   GPtrArray *context = g_ptr_array_sized_new(0);
300 
301   msg = _construct_merge_base_message();
302   g_ptr_array_add(context, _construct_merged_message("merged1", "mergedvalue1"));
303   g_ptr_array_add(context, _construct_merged_message("merged2", "mergedvalue2"));
304   g_ptr_array_add(context, _construct_merged_message("merged3", "mergedvalue3"));
305   log_msg_merge_context(msg, (LogMessage **) context->pdata, context->len);
306 
307   assert_log_message_value_by_name(msg, "merged1", "mergedvalue1");
308   assert_log_message_value_by_name(msg, "merged2", "mergedvalue2");
309   assert_log_message_value_by_name(msg, "merged3", "mergedvalue3");
310   g_ptr_array_foreach(context, (GFunc) log_msg_unref, NULL);
311   g_ptr_array_free(context, TRUE);
312   log_msg_unref(msg);
313 }
314 
Test(log_message,test_log_message_merge_leaves_base_tags_intact)315 Test(log_message, test_log_message_merge_leaves_base_tags_intact)
316 {
317   LogMessage *msg;
318   GPtrArray *context = g_ptr_array_sized_new(0);
319 
320   msg = _construct_merge_base_message();
321   g_ptr_array_add(context, _construct_merged_message("merged1", "mergedvalue1"));
322   log_msg_merge_context(msg, (LogMessage **) context->pdata, context->len);
323 
324   assert_log_message_has_tag(msg, "basetag");
325   assert_log_message_doesnt_have_tag(msg, "mergedtag");
326   g_ptr_array_foreach(context, (GFunc) log_msg_unref, NULL);
327   g_ptr_array_free(context, TRUE);
328   log_msg_unref(msg);
329 }
330 
Test(log_message,test_log_msg_set_value_indirect_with_self_referencing_handle_results_in_a_nonindirect_value)331 Test(log_message, test_log_msg_set_value_indirect_with_self_referencing_handle_results_in_a_nonindirect_value)
332 {
333   LogMessageTestParams *params = log_message_test_params_new();
334   gssize value_len;
335 
336   log_msg_set_value_indirect(params->message, params->nv_handle, params->nv_handle, 0, 0, 5);
337   cr_assert_str_eq(log_msg_get_value(params->message, params->nv_handle, &value_len), "value",
338                    "indirect self-reference value doesn't match");
339 
340   log_message_test_params_free(params);
341 }
342 
Test(log_message,test_log_msg_get_value_with_time_related_macro)343 Test(log_message, test_log_msg_get_value_with_time_related_macro)
344 {
345   LogMessage *msg;
346   gssize value_len;
347   NVHandle handle;
348   const char *date_value;
349 
350   msg = log_msg_new_empty();
351   msg->timestamps[LM_TS_STAMP].ut_sec = 1389783444;
352   msg->timestamps[LM_TS_STAMP].ut_gmtoff = 3600;
353 
354   handle = log_msg_get_value_handle("ISODATE");
355   date_value = log_msg_get_value(msg, handle, &value_len);
356   cr_assert_str_eq(date_value, "2014-01-15T11:57:24+01:00", "ISODATE macro value does not match! value=%s", date_value);
357 
358   log_msg_unref(msg);
359 }
360 
Test(log_message,test_local_logmsg_created_with_the_right_flags_and_timestamps)361 Test(log_message, test_local_logmsg_created_with_the_right_flags_and_timestamps)
362 {
363   LogMessage *msg = log_msg_new_local();
364 
365   gboolean are_equals = unix_time_eq(&msg->timestamps[LM_TS_STAMP], &msg->timestamps[LM_TS_RECVD]);
366 
367   cr_assert_neq((msg->flags & LF_LOCAL), 0, "LogMessage created by log_msg_new_local() should have LF_LOCAL flag set");
368   cr_assert(are_equals, "The timestamps in a LogMessage created by log_msg_new_local() should be equals");
369 
370   log_msg_unref(msg);
371 }
372 
Test(log_message,test_sdata_sanitization)373 Test(log_message, test_sdata_sanitization)
374 {
375   LogMessage *msg;
376   /* These keys looks strange, but JSON object can be parsed to SDATA,
377    * so the key could contain any character, while the specification
378    * does not declare any way to encode the keys, just the values.
379    * The goal is to have a syntactically valid syslog message.
380    *
381    * Block names are sanitized with the same function as the keys,
382    * thus no need for exhaust testing, added just one case to the end
383    * to see if business logic applied. */
384 
385   msg = log_msg_new_empty();
386   log_msg_set_value_by_name(msg, ".SDATA.foo.bar[0]", "value[0]", -1);
387   assert_sdata_value_equals(msg, "[foo bar%5B0%5D=\"value[0\\]\"]");
388   log_msg_unref(msg);
389 
390   msg = log_msg_new_empty();
391   log_msg_set_value_by_name(msg, ".SDATA.foo.bácsi", "bácsi", -1);
392   assert_sdata_value_equals(msg, "[foo b%C3%A1csi=\"bácsi\"]");
393   log_msg_unref(msg);
394 
395   msg = log_msg_new_empty();
396   log_msg_set_value_by_name(msg, ".SDATA.foo.sp ace", "sp ace", -1);
397   assert_sdata_value_equals(msg, "[foo sp%20ace=\"sp ace\"]");
398   log_msg_unref(msg);
399 
400   msg = log_msg_new_empty();
401   log_msg_set_value_by_name(msg, ".SDATA.foo.eq=al", "eq=al", -1);
402   assert_sdata_value_equals(msg, "[foo eq%3Dal=\"eq=al\"]");
403   log_msg_unref(msg);
404 
405   msg = log_msg_new_empty();
406   log_msg_set_value_by_name(msg, ".SDATA.foo.quo\"te", "quo\"te", -1);
407   assert_sdata_value_equals(msg, "[foo quo%22te=\"quo\\\"te\"]");
408   log_msg_unref(msg);
409 
410   msg = log_msg_new_empty();
411   log_msg_set_value_by_name(msg, ".SDATA.fo@o[0].bar", "value", -1);
412   assert_sdata_value_equals(msg, "[fo@o%5B0%5D bar=\"value\"]");
413   log_msg_unref(msg);
414 }
415 
Test(log_message,test_sdata_value_is_updated_by_sdata_name_value_pairs)416 Test(log_message, test_sdata_value_is_updated_by_sdata_name_value_pairs)
417 {
418   LogMessage *msg;
419 
420   msg = log_msg_new_empty();
421   log_msg_set_value_by_name(msg, ".SDATA.foo.bar1", "value", -1);
422   assert_sdata_value_equals(msg, "[foo bar1=\"value\"]");
423   log_msg_set_value_by_name(msg, ".SDATA.foo.bar2", "value", -1);
424   assert_sdata_value_equals(msg, "[foo bar1=\"value\" bar2=\"value\"]");
425   log_msg_set_value_by_name(msg, ".SDATA.foo.bar3", "value", -1);
426   assert_sdata_value_equals(msg, "[foo bar1=\"value\" bar2=\"value\" bar3=\"value\"]");
427   log_msg_set_value_by_name(msg, ".SDATA.post.value1", "value", -1);
428   assert_sdata_value_equals(msg, "[post value1=\"value\"][foo bar1=\"value\" bar2=\"value\" bar3=\"value\"]");
429   log_msg_set_value_by_name(msg, ".SDATA.post.value2", "value", -1);
430   assert_sdata_value_equals(msg,
431                             "[post value1=\"value\" value2=\"value\"][foo bar1=\"value\" bar2=\"value\" bar3=\"value\"]");
432   log_msg_unref(msg);
433 }
434 
Test(log_message,test_sdata_seqnum_adds_meta_sequence_id)435 Test(log_message, test_sdata_seqnum_adds_meta_sequence_id)
436 {
437   LogMessage *msg;
438 
439   msg = log_msg_new_empty();
440   log_msg_set_value_by_name(msg, ".SDATA.foo.bar1", "value", -1);
441   log_msg_set_value_by_name(msg, ".SDATA.foo.bar2", "value", -1);
442   log_msg_set_value_by_name(msg, ".SDATA.foo.bar3", "value", -1);
443   assert_sdata_value_with_seqnum_equals(msg, 5,
444                                         "[foo bar1=\"value\" bar2=\"value\" bar3=\"value\"][meta sequenceId=\"5\"]");
445   log_msg_set_value_by_name(msg, ".SDATA.meta.foobar", "value", -1);
446   assert_sdata_value_with_seqnum_equals(msg, 6,
447                                         "[meta sequenceId=\"6\" foobar=\"value\"][foo bar1=\"value\" bar2=\"value\" bar3=\"value\"]");
448   log_msg_unref(msg);
449 }
450 
Test(log_message,test_sdata_value_omits_unset_values)451 Test(log_message, test_sdata_value_omits_unset_values)
452 {
453   LogMessage *msg;
454 
455   msg = log_msg_new_empty();
456   log_msg_set_value_by_name(msg, ".SDATA.foo.bar1", "value", -1);
457   log_msg_set_value_by_name(msg, ".SDATA.foo.bar2", "value", -1);
458   log_msg_set_value_by_name(msg, ".SDATA.foo.bar3", "value", -1);
459   assert_sdata_value_equals(msg, "[foo bar1=\"value\" bar2=\"value\" bar3=\"value\"]");
460   log_msg_unset_value_by_name(msg, ".SDATA.foo.bar2");
461   assert_sdata_value_equals(msg, "[foo bar1=\"value\" bar3=\"value\"]");
462   log_msg_unset_value_by_name(msg, ".SDATA.foo.bar1");
463   log_msg_unset_value_by_name(msg, ".SDATA.foo.bar3");
464   assert_sdata_value_equals(msg, "");
465   log_msg_unref(msg);
466 }
467 
468 #define DEFUN_KEY_VALUE(name, key, value, size) \
469   gchar name ## _key[size]; \
470   gchar name ## _value[size]; \
471   name ## _key[size-1] = name ## _value[size-1] = 0; \
472   memset(name ## _key, key, sizeof(name ##_key)-1); \
473   memset(name ## _value, value, sizeof(name ##_value)-1); \
474 
475 
476 typedef struct
477 {
478   gssize nvtable_size_old;
479   gssize nvtable_size_new;
480   gssize msg_size_old;
481   gssize msg_size_new;
482 } sizes_t;
483 
484 static sizes_t
add_key_value(LogMessage * msg,gchar * key,gchar * value)485 add_key_value(LogMessage *msg, gchar *key, gchar *value)
486 {
487   sizes_t sizes;
488   sizes.msg_size_old = log_msg_get_size(msg);
489   sizes.nvtable_size_old = nv_table_get_memory_consumption(msg->payload);
490   log_msg_set_value_by_name(msg, key, value, -1);
491   sizes.nvtable_size_new = nv_table_get_memory_consumption(msg->payload);
492   sizes.msg_size_new = log_msg_get_size(msg);
493   return sizes;
494 }
495 
496 
497 static void
test_with_sdata(LogMessage * msg,guint32 old_msg_size)498 test_with_sdata(LogMessage *msg, guint32 old_msg_size)
499 {
500   sizes_t sizes;
501   gchar key[]          = ".SDATA.**";
502   gchar value[] = "AAAAAAA";
503 
504   guint32 single_sdata_kv_size;
505   guint32 sdata_payload_array_size;
506 
507   const char iter_length = 17;
508 
509   for (char i = 0; i < iter_length; i++)
510     {
511       g_sprintf(key, ".SDATA.%02d", i);
512       sizes = add_key_value(msg, key, value);
513 
514       single_sdata_kv_size = NV_ENTRY_DIRECT_HDR + NV_TABLE_BOUND(strlen(key)+1 + strlen(value)+1);
515 
516       /* i+1 is stored, but the sdata array size is calculated when adding the i-th */
517       sdata_payload_array_size = STRICT_ROUND_TO_NEXT_EIGHT(i) * sizeof(msg->sdata[0]);
518       cr_assert_eq(old_msg_size + (i+1) * single_sdata_kv_size + sdata_payload_array_size, sizes.msg_size_new);
519     }
520 }
521 
522 #define SMALL_LENGTH 10
523 #define LARGE_LENGTH 256
524 
Test(log_message,test_message_size)525 Test(log_message, test_message_size)
526 {
527   LogMessage *msg = log_msg_new_empty();
528 
529   sizes_t sizes;
530 
531   DEFUN_KEY_VALUE(small, 'C', 'D', SMALL_LENGTH);
532   sizes = add_key_value(msg, small_key, small_value);
533   // (SMALL_LENGTH-1)*'C'+'\0' + (SMALL_LENGTH-1)*'D'+'\0'
534   guint32 entry_size = NV_ENTRY_DIRECT_HDR + NV_TABLE_BOUND(SMALL_LENGTH + SMALL_LENGTH);
535   cr_assert_eq(sizes.nvtable_size_old + entry_size, sizes.nvtable_size_new);
536   cr_assert_eq(sizes.msg_size_old + entry_size, sizes.msg_size_new);  // Size increased because of nvtable
537 
538   guint32 msg_size = sizes.msg_size_new;
539   log_msg_set_tag_by_name(msg, "test_tag_storage");
540   cr_assert(log_msg_is_tag_by_name(msg, "test_tag_storage"));
541   cr_assert_eq(msg_size, log_msg_get_size(msg)); // Tag is not increased until tag id 65
542 
543   char *tag_name = strdup("00tagname");
544   // (*8 to convert to bits) + no need plus 1 bcause we already added one tag: test_tag_storage
545   for (int i = 0; i < GLIB_SIZEOF_LONG*8; i++)
546     {
547       sprintf(tag_name, "%dtagname", i);
548       log_msg_set_tag_by_name(msg, tag_name);
549     }
550   free(tag_name);
551   cr_assert_eq(msg_size + 2*GLIB_SIZEOF_LONG, log_msg_get_size(msg));
552 
553 
554   DEFUN_KEY_VALUE(big, 'A', 'B', LARGE_LENGTH);
555   sizes = add_key_value(msg, big_key, big_value); // nvtable is expanded
556   entry_size = NV_ENTRY_DIRECT_HDR + NV_TABLE_BOUND(LARGE_LENGTH + LARGE_LENGTH);
557   cr_assert_eq(sizes.nvtable_size_old + entry_size, sizes.nvtable_size_new); // but only increased by the entry
558   cr_assert_eq(sizes.msg_size_old + entry_size, sizes.msg_size_new);  // nvtable is doubled
559 
560   test_with_sdata(msg, sizes.msg_size_new);
561 
562   log_msg_unref(msg);
563 }
564 
565 Test(log_message, when_get_indirect_value_with_null_value_len_abort_instead_of_sigsegv, .signal=SIGABRT)
566 {
567   LogMessageTestParams *params = log_message_test_params_new();
568 
569   NVHandle indirect = log_msg_get_value_handle("INDIRECT");
570   log_msg_set_value_indirect(params->message, indirect, params->nv_handle, 0, 0, 5);
571   log_msg_get_value(params->message, indirect, NULL);
572 
573   log_message_test_params_free(params);
574 }
575 
Test(log_message,test_cow_writing_cloned_message)576 Test(log_message, test_cow_writing_cloned_message)
577 {
578   LogMessage *msg = _construct_log_message();
579   log_msg_set_value_by_name(msg, "orig_name", "orig_value", -1);
580 
581   LogPathOptions path_options = LOG_PATH_OPTIONS_INIT;
582   LogMessage *cloned = log_msg_clone_cow(msg, &path_options);
583 
584   log_msg_set_value_by_name(cloned, "cloned_name", "cloned_value", -1);
585   log_msg_set_value_by_name(cloned, "orig_name", "modified_value", -1);
586 
587   cr_assert_str_eq(log_msg_get_value_by_name(msg, "orig_name", NULL), "orig_value",
588                    "Modifications on a COW-cloned message should not leak into the original message; actual: %s, expected: %s",
589                    log_msg_get_value_by_name(msg, "orig_name", NULL), "orig_value");
590 
591   NVHandle cloned_name = log_msg_get_value_handle("cloned_name");
592   gssize value_length;
593   cr_assert_null(log_msg_get_value_if_set(msg, cloned_name, &value_length),
594                  "Modifications on a COW-cloned message should not leak into the original message");
595 
596   log_msg_unref(cloned);
597   log_msg_unref(msg);
598 }
599 
600 
Test(log_message,test_cow_make_writable)601 Test(log_message, test_cow_make_writable)
602 {
603   LogMessage *msg = _construct_log_message();
604   log_msg_set_value_by_name(msg, "orig_name", "orig_value", -1);
605 
606   log_msg_write_protect(msg);
607   cr_assert(log_msg_is_write_protected(msg));
608 
609   LogMessage *orig_msg = log_msg_ref(msg);
610 
611   LogPathOptions path_options = LOG_PATH_OPTIONS_INIT;
612   log_msg_make_writable(&msg, &path_options);
613 
614   log_msg_set_value_by_name(msg, "orig_name2", "orig_value2", -1);
615 
616   NVHandle orig_name2 = log_msg_get_value_handle("orig_name2");
617   gssize value_length;
618   cr_assert_null(log_msg_get_value_if_set(orig_msg, orig_name2, &value_length),
619                  "Modifications on a COW-cloned message should not leak into the original message");
620 
621   log_msg_unref(orig_msg);
622   log_msg_unref(msg);
623 }
624 
Test(log_message,test_cow_unset_value)625 Test(log_message, test_cow_unset_value)
626 {
627   LogMessage *msg = _construct_log_message();
628   log_msg_set_value_by_name(msg, "orig_name", "orig_value", -1);
629   log_msg_write_protect(msg);
630 
631   LogMessage *orig_msg = log_msg_ref(msg);
632 
633   LogPathOptions path_options = LOG_PATH_OPTIONS_INIT;
634   log_msg_make_writable(&msg, &path_options);
635 
636   log_msg_unset_value_by_name(msg, "orig_name");
637 
638   cr_assert_str_eq(log_msg_get_value_by_name(orig_msg, "orig_name", NULL), "orig_value",
639                    "Unsetting a value in a COW-cloned message should not unset the value in the original message; actual: %s, expected: %s",
640                    log_msg_get_value_by_name(orig_msg, "orig_name", NULL), "orig_value");
641 
642   log_msg_unref(orig_msg);
643   log_msg_unref(msg);
644 }
645