Skip to content

Commit

Permalink
type-hinting: support explicit value length
Browse files Browse the repository at this point in the history
Sometimes we are casting strings with an explicit length value, but
the type hinting code simply assumed that everything was NUL terminated,
which is not the case for indirect values.

Signed-off-by: Balazs Scheidler <[email protected]>
Signed-off-by: shifter <[email protected]>
  • Loading branch information
bazsi authored and bshifter committed Dec 6, 2023
1 parent 9698ba6 commit 71f9bc8
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 105 deletions.
4 changes: 2 additions & 2 deletions lib/filter/filter-cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ _convert_to_number(const GString *value, LogMessageValueType type, GenericNumber
{
gboolean b;

if (type_cast_to_boolean(value->str, &b, NULL))
if (type_cast_to_boolean(value->str, -1, &b, NULL))
gn_set_int64(number, b);
else
gn_set_int64(number, 0);
Expand All @@ -148,7 +148,7 @@ _convert_to_number(const GString *value, LogMessageValueType type, GenericNumber
{
gint64 msec;

if (type_cast_to_datetime_msec(value->str, &msec, NULL))
if (type_cast_to_datetime_msec(value->str, -1, &msec, NULL))
gn_set_int64(number, msec);
else
gn_set_int64(number, 0);
Expand Down
68 changes: 52 additions & 16 deletions lib/logmsg/tests/test_type_hints.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ ParameterizedTest(StringBoolPair *string_value_pair, type_hints, test_bool_cast)
gboolean value;
GError *error = NULL;

cr_assert_eq(type_cast_to_boolean(string_value_pair->string, &value, &error), TRUE,
cr_assert_eq(type_cast_to_boolean(string_value_pair->string, -1, &value, &error), TRUE,
"Type cast of \"%s\" to gboolean failed", string_value_pair->string);
cr_assert_eq(value, string_value_pair->value);
cr_assert_null(error);
Expand All @@ -140,7 +140,7 @@ Test(type_hints, test_invalid_bool_cast)
gboolean value;

/* test invalid boolean value cast */
cr_assert_eq(type_cast_to_boolean("booyah", &value, &error), FALSE,
cr_assert_eq(type_cast_to_boolean("booyah", -1, &value, &error), FALSE,
"Type cast \"booyah\" to gboolean should be failed");
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
Expand All @@ -154,28 +154,28 @@ Test(type_hints, test_int32_cast)
GError *error = NULL;
gint32 value;

cr_assert(type_cast_to_int32("12345", &value, &error), "Type cast of \"12345\" to gint32 failed");
cr_assert(type_cast_to_int32("12345", -1, &value, &error), "Type cast of \"12345\" to gint32 failed");
cr_assert_eq(value, 12345);
cr_assert_null(error);

cr_assert(type_cast_to_int32("0x1000", &value, &error), "Type cast of \"0x1000\" to gint32 failed");
cr_assert(type_cast_to_int32("0x1000", -1, &value, &error), "Type cast of \"0x1000\" to gint32 failed");
cr_assert_eq(value, 0x1000);
cr_assert_null(error);

cr_assert(type_cast_to_int32("0111", &value, &error), "Type cast of \"0111\" to gint32 failed");
cr_assert(type_cast_to_int32("0111", -1, &value, &error), "Type cast of \"0111\" to gint32 failed");
cr_assert_eq(value, 111);
cr_assert_null(error);

/* test for invalid string */
cr_assert_not(type_cast_to_int32("12345a", &value, &error),
cr_assert_not(type_cast_to_int32("12345a", -1, &value, &error),
"Type cast of invalid string to gint32 should be failed");
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
cr_assert_eq(error->code, TYPE_HINTING_INVALID_CAST);
g_clear_error(&error);

/* empty string */
cr_assert_not(type_cast_to_int32("", &value, &error),
cr_assert_not(type_cast_to_int32("", -1, &value, &error),
"Type cast of empty string to gint32 should be failed");
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
Expand All @@ -184,34 +184,70 @@ Test(type_hints, test_int32_cast)
g_clear_error(&error);
}

Test(type_hints, test_int32_nonzero_terminated)
{
GError *error = NULL;
gint32 int32_value;
gint64 int64_value;
gboolean bool_value;
gdouble dbl_value;
UnixTime ut_value;

cr_assert(type_cast_to_int32("12345", 3, &int32_value, &error),
"Type cast of non-zero terminated \"123\" to gint32 failed");
cr_assert_eq(int32_value, 123);
cr_assert_null(error);

cr_assert(type_cast_to_int64("12345", 3, &int64_value, &error),
"Type cast of non-zero terminated \"123\" to gint64 failed");
cr_assert_eq(int64_value, 123);
cr_assert_null(error);

cr_assert(type_cast_to_boolean("12", 1, &bool_value, &error),
"Type cast of non-zero terminated \"1\" to gboolean failed");
cr_assert_eq(bool_value, 1);
cr_assert_null(error);

cr_assert(type_cast_to_double("123456", 3, &dbl_value, &error),
"Type cast of non-zero terminated \"123\" to gdouble failed");
cr_assert(dbl_value - 123 < 0.1);
cr_assert_null(error);

cr_assert(type_cast_to_datetime_unixtime("1699134067.123", 12, &ut_value, &error),
"Type cast of non-zero terminated \"1699134067.1\" to unixtime failed");
cr_assert(ut_value.ut_sec == 1699134067);
cr_assert(ut_value.ut_usec == 100000);
cr_assert_null(error);
}

Test(type_hints, test_int64_cast)
{
GError *error = NULL;
gint64 value;

cr_assert(type_cast_to_int64("12345", &value, &error), "Type cast of \"12345\" to gint64 failed");
cr_assert(type_cast_to_int64("12345", -1, &value, &error), "Type cast of \"12345\" to gint64 failed");
cr_assert_eq(value, 12345);
cr_assert_null(error);

cr_assert(type_cast_to_int64("0x1000", &value, &error), "Type cast of \"0x1000\" to gint64 failed");
cr_assert(type_cast_to_int64("0x1000", -1, &value, &error), "Type cast of \"0x1000\" to gint64 failed");
cr_assert_eq(value, 0x1000);
cr_assert_null(error);

cr_assert(type_cast_to_int64("0111", &value, &error), "Type cast of \"0111\" to gint64 failed");
cr_assert(type_cast_to_int64("0111", -1, &value, &error), "Type cast of \"0111\" to gint64 failed");
cr_assert_eq(value, 111);
cr_assert_null(error);


/* test for invalid string */
cr_assert_not(type_cast_to_int64("12345a", &value, &error),
cr_assert_not(type_cast_to_int64("12345a", -1, &value, &error),
"Type cast of invalid string to gint64 should be failed");
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
cr_assert_eq(error->code, TYPE_HINTING_INVALID_CAST);
g_clear_error(&error);

/* empty string */
cr_assert_not(type_cast_to_int64("", &value, &error),
cr_assert_not(type_cast_to_int64("", -1, &value, &error),
"Type cast of empty string to gint64 should be failed");
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
Expand Down Expand Up @@ -255,7 +291,7 @@ ParameterizedTest(StringDoublePair *string_value_pair, type_hints, test_double_c
gdouble value;
GError *error = NULL;

cr_assert(type_cast_to_double(string_value_pair->string, &value, &error),
cr_assert(type_cast_to_double(string_value_pair->string, -1, &value, &error),
"Type cast of \"%s\" to double failed", string_value_pair->string);

cr_assert_gdouble_eq(value, string_value_pair->value);
Expand All @@ -281,7 +317,7 @@ ParameterizedTest(StringDoublePair *string_value_pair, type_hints, test_invalid_
gdouble value;
GError *error = NULL;

cr_assert_not(type_cast_to_double(string_value_pair->string, &value, &error),
cr_assert_not(type_cast_to_double(string_value_pair->string, -1, &value, &error),
"Type cast of invalid string (%s) to double should be failed", string_value_pair->string);
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
Expand Down Expand Up @@ -313,7 +349,7 @@ ParameterizedTest(StringUInt64Pair *string_value_pair, type_hints, test_datetime
gint64 value;
GError *error = NULL;

cr_assert_eq(type_cast_to_datetime_msec(string_value_pair->string, &value, &error), TRUE,
cr_assert_eq(type_cast_to_datetime_msec(string_value_pair->string, -1, &value, &error), TRUE,
"Type cast of \"%s\" to msecs failed", string_value_pair->string);
cr_assert_eq(value, string_value_pair->value,
"datetime cast failed %" G_GINT64_FORMAT " != %" G_GINT64_FORMAT,
Expand Down Expand Up @@ -345,7 +381,7 @@ ParameterizedTest(StringUInt64Pair *string_value_pair, type_hints, test_invalid_
GError *error = NULL;
gint64 value;

cr_assert_eq(type_cast_to_datetime_msec(string_value_pair->string, &value, &error), FALSE,
cr_assert_eq(type_cast_to_datetime_msec(string_value_pair->string, -1, &value, &error), FALSE,
"Type cast of invalid string to gint64 should have failed %s", string_value_pair->string);
cr_assert_not_null(error);
cr_assert_eq(error->domain, TYPE_HINTING_ERROR);
Expand Down
52 changes: 36 additions & 16 deletions lib/logmsg/type-hinting.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "type-hinting.h"
#include "template/templates.h"
#include "timeutils/scan-timestamp.h"
#include "str-utils.h"

#include <errno.h>
#include <math.h>
Expand All @@ -52,30 +53,37 @@ type_hint_parse(const gchar *hint, LogMessageValueType *out_type, GError **error
}

gboolean
type_cast_drop_helper(gint drop_flags, const gchar *value,
type_cast_drop_helper(gint drop_flags, const gchar *value, gssize value_len,
const gchar *type_hint)
{
if (!(drop_flags & ON_ERROR_SILENT))
{
msg_error("Casting error",
evt_tag_str("value", value),
evt_tag_mem("value", value, value_len < 0 ? strlen(value) : value_len),
evt_tag_str("type-hint", type_hint));
}
return drop_flags & ON_ERROR_DROP_MESSAGE;
}

gboolean
type_cast_to_boolean(const gchar *value, gboolean *out, GError **error)
type_cast_to_boolean(const gchar *value, gssize value_len, gboolean *out, GError **error)
{
if (value_len == 0)
return FALSE;

if (value[0] == 'T' || value[0] == 't' || value[0] == '1')
*out = TRUE;
else if (value[0] == 'F' || value[0] == 'f' || value[0] == '0')
*out = FALSE;
else
{
if (error)
g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
"boolean(%s)", value);
{
if (value_len < 0)
value_len = strlen(value);
g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
"boolean(%.*s)", (gint) value_len, value);
}
return FALSE;
}

Expand All @@ -91,10 +99,13 @@ _is_value_hex(const gchar *value)
}

gboolean
type_cast_to_int32(const gchar *value, gint32 *out, GError **error)
type_cast_to_int32(const gchar *value, gssize value_len, gint32 *out, GError **error)
{
gchar *endptr;

if (value_len >= 0)
APPEND_ZERO(value, value, value_len);

if (_is_value_hex(value))
*out = (gint32)strtol(value, &endptr, 16);
else
Expand All @@ -111,10 +122,13 @@ type_cast_to_int32(const gchar *value, gint32 *out, GError **error)
}

gboolean
type_cast_to_int64(const gchar *value, gint64 *out, GError **error)
type_cast_to_int64(const gchar *value, gssize value_len, gint64 *out, GError **error)
{
gchar *endptr;

if (value_len >= 0)
APPEND_ZERO(value, value, value_len);

if (_is_value_hex(value))
*out = (gint64)strtoll(value, &endptr, 16);
else
Expand All @@ -131,11 +145,14 @@ type_cast_to_int64(const gchar *value, gint64 *out, GError **error)
}

gboolean
type_cast_to_double(const gchar *value, gdouble *out, GError **error)
type_cast_to_double(const gchar *value, gssize value_len, gdouble *out, GError **error)
{
gchar *endptr = NULL;
gboolean success = TRUE;

if (value_len >= 0)
APPEND_ZERO(value, value, value_len);

errno = 0;
*out = g_ascii_strtod(value, &endptr);
if (errno == ERANGE && (*out >= HUGE_VAL || *out <= -HUGE_VAL))
Expand Down Expand Up @@ -180,12 +197,15 @@ _parse_fixed_point_timestamp_in_nsec(const gchar *value, gchar **endptr, gint64
}

gboolean
type_cast_to_datetime_unixtime(const gchar *value, UnixTime *ut, GError **error)
type_cast_to_datetime_unixtime(const gchar *value, gssize value_len, UnixTime *ut, GError **error)
{
gchar *endptr;
gint64 sec, nsec;
gint tzofs = -1;

if (value_len >= 0)
APPEND_ZERO(value, value, value_len);

if (!_parse_fixed_point_timestamp_in_nsec(value, &endptr, &sec, &nsec))
goto error;

Expand All @@ -211,19 +231,19 @@ type_cast_to_datetime_unixtime(const gchar *value, UnixTime *ut, GError **error)
}

gboolean
type_cast_to_datetime_msec(const gchar *value, gint64 *out, GError **error)
type_cast_to_datetime_msec(const gchar *value, gssize value_len, gint64 *out, GError **error)
{
UnixTime ut;

if (!type_cast_to_datetime_unixtime(value, &ut, error))
if (!type_cast_to_datetime_unixtime(value, value_len, &ut, error))
return FALSE;

*out = ut.ut_sec * 1000 + ut.ut_usec / 1000;
return TRUE;
}

gboolean
type_cast_validate(const gchar *value, LogMessageValueType type, GError **error)
type_cast_validate(const gchar *value, gssize value_len, LogMessageValueType type, GError **error)
{
gboolean b;
gint64 i64;
Expand All @@ -240,13 +260,13 @@ type_cast_validate(const gchar *value, LogMessageValueType type, GError **error)
case LM_VT_LIST:
return TRUE;
case LM_VT_BOOLEAN:
return type_cast_to_boolean(value, &b, error);
return type_cast_to_boolean(value, value_len, &b, error);
case LM_VT_INTEGER:
return type_cast_to_int64(value, &i64, error);
return type_cast_to_int64(value, value_len, &i64, error);
case LM_VT_DOUBLE:
return type_cast_to_double(value, &d, error);
return type_cast_to_double(value, value_len, &d, error);
case LM_VT_DATETIME:
return type_cast_to_datetime_unixtime(value, &ut, error);
return type_cast_to_datetime_unixtime(value, value_len, &ut, error);
default:
g_assert_not_reached();
}
Expand Down
16 changes: 8 additions & 8 deletions lib/logmsg/type-hinting.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ enum TypeHintingError

gboolean type_hint_parse(const gchar *hint, LogMessageValueType *out_hint, GError **error);

gboolean type_cast_drop_helper(gint drop_flags, const gchar *value,
gboolean type_cast_drop_helper(gint drop_flags, const gchar *value, gssize value_len,
const gchar *type_hint);

gboolean type_cast_to_boolean(const gchar *value, gboolean *out, GError **error);
gboolean type_cast_to_int32(const gchar *value, gint32 *out, GError **error);
gboolean type_cast_to_int64(const gchar *value, gint64 *out, GError **error);
gboolean type_cast_to_double(const gchar *value, gdouble *out, GError **error);
gboolean type_cast_to_datetime_msec(const gchar *value, gint64 *out, GError **error);
gboolean type_cast_to_datetime_unixtime(const gchar *value, UnixTime *ut, GError **error);
gboolean type_cast_to_boolean(const gchar *value, gssize value_len, gboolean *out, GError **error);
gboolean type_cast_to_int32(const gchar *value, gssize value_len, gint32 *out, GError **error);
gboolean type_cast_to_int64(const gchar *value, gssize value_len, gint64 *out, GError **error);
gboolean type_cast_to_double(const gchar *value, gssize value_len, gdouble *out, GError **error);
gboolean type_cast_to_datetime_msec(const gchar *value, gssize value_len, gint64 *out, GError **error);
gboolean type_cast_to_datetime_unixtime(const gchar *value, gssize value_len, UnixTime *ut, GError **error);

gboolean type_cast_validate(const gchar *value, LogMessageValueType type, GError **error);
gboolean type_cast_validate(const gchar *value, gssize value_len, LogMessageValueType type, GError **error);

#endif
Loading

0 comments on commit 71f9bc8

Please sign in to comment.