File 0001-Fix-buffer-handling-of-syslog-and-timestamp-parsers.patch of Package syslog-ng.17828

From 881c31a0beb7bfa5e993440e78b813a651128f9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?=
 <laszlo.varady@protonmail.com>
Date: Sat, 20 Aug 2022 12:42:38 +0200
Subject: [PATCH] Fix buffer handling of syslog and timestamp parsers

---
 lib/timeutils/scan-timestamp.c            |  68 ++++++-----
 lib/timeutils/tests/test_scan-timestamp.c | 133 +++++++++++++++++++---
 news/bugfix-4110.md                       |   4 +
 3 files changed, 162 insertions(+), 43 deletions(-)
 create mode 100644 news/bugfix-4110.md

diff --git a/lib/timeutils/scan-timestamp.c b/lib/timeutils/scan-timestamp.c
index 2f6a6b76d..0f7f52e36 100644
--- a/lib/timeutils/scan-timestamp.c
+++ b/lib/timeutils/scan-timestamp.c
@@ -34,41 +34,43 @@ scan_day_abbrev(const gchar **buf, gint *left, gint *wday)
 {
   *wday = -1;
 
-  if (*left < 3)
+  const gsize abbrev_length = 3;
+
+  if (*left < abbrev_length)
     return FALSE;
 
   switch (**buf)
     {
     case 'S':
-      if (strncasecmp(*buf, "Sun", 3) == 0)
+      if (strncasecmp(*buf, "Sun", abbrev_length) == 0)
         *wday = 0;
-      else if (strncasecmp(*buf, "Sat", 3) == 0)
+      else if (strncasecmp(*buf, "Sat", abbrev_length) == 0)
         *wday = 6;
       else
         return FALSE;
       break;
     case 'M':
-      if (strncasecmp(*buf, "Mon", 3) == 0)
+      if (strncasecmp(*buf, "Mon", abbrev_length) == 0)
         *wday = 1;
       else
         return FALSE;
       break;
     case 'T':
-      if (strncasecmp(*buf, "Tue", 3) == 0)
+      if (strncasecmp(*buf, "Tue", abbrev_length) == 0)
         *wday = 2;
-      else if (strncasecmp(*buf, "Thu", 3) == 0)
+      else if (strncasecmp(*buf, "Thu", abbrev_length) == 0)
         *wday = 4;
       else
         return FALSE;
       break;
     case 'W':
-      if (strncasecmp(*buf, "Wed", 3) == 0)
+      if (strncasecmp(*buf, "Wed", abbrev_length) == 0)
         *wday = 3;
       else
         return FALSE;
       break;
     case 'F':
-      if (strncasecmp(*buf, "Fri", 3) == 0)
+      if (strncasecmp(*buf, "Fri", abbrev_length) == 0)
         *wday = 5;
       else
         return FALSE;
@@ -77,8 +79,8 @@ scan_day_abbrev(const gchar **buf, gint *left, gint *wday)
       return FALSE;
     }
 
-  (*buf) += 3;
-  (*left) -= 3;
+  (*buf) += abbrev_length;
+  (*left) -= abbrev_length;
   return TRUE;
 }
 
@@ -87,63 +89,65 @@ scan_month_abbrev(const gchar **buf, gint *left, gint *mon)
 {
   *mon = -1;
 
-  if (*left < 3)
+  const gsize abbrev_length = 3;
+
+  if (*left < abbrev_length)
     return FALSE;
 
   switch (**buf)
     {
     case 'J':
-      if (strncasecmp(*buf, "Jan", 3) == 0)
+      if (strncasecmp(*buf, "Jan", abbrev_length) == 0)
         *mon = 0;
-      else if (strncasecmp(*buf, "Jun", 3) == 0)
+      else if (strncasecmp(*buf, "Jun", abbrev_length) == 0)
         *mon = 5;
-      else if (strncasecmp(*buf, "Jul", 3) == 0)
+      else if (strncasecmp(*buf, "Jul", abbrev_length) == 0)
         *mon = 6;
       else
         return FALSE;
       break;
     case 'F':
-      if (strncasecmp(*buf, "Feb", 3) == 0)
+      if (strncasecmp(*buf, "Feb", abbrev_length) == 0)
         *mon = 1;
       else
         return FALSE;
       break;
     case 'M':
-      if (strncasecmp(*buf, "Mar", 3) == 0)
+      if (strncasecmp(*buf, "Mar", abbrev_length) == 0)
         *mon = 2;
-      else if (strncasecmp(*buf, "May", 3) == 0)
+      else if (strncasecmp(*buf, "May", abbrev_length) == 0)
         *mon = 4;
       else
         return FALSE;
       break;
     case 'A':
-      if (strncasecmp(*buf, "Apr", 3) == 0)
+      if (strncasecmp(*buf, "Apr", abbrev_length) == 0)
         *mon = 3;
-      else if (strncasecmp(*buf, "Aug", 3) == 0)
+      else if (strncasecmp(*buf, "Aug", abbrev_length) == 0)
         *mon = 7;
       else
         return FALSE;
       break;
     case 'S':
-      if (strncasecmp(*buf, "Sep", 3) == 0)
+      if (strncasecmp(*buf, "Sep", abbrev_length) == 0)
         *mon = 8;
       else
         return FALSE;
       break;
     case 'O':
-      if (strncasecmp(*buf, "Oct", 3) == 0)
+      if (strncasecmp(*buf, "Oct", abbrev_length) == 0)
         *mon = 9;
       else
         return FALSE;
       break;
     case 'N':
-      if (strncasecmp(*buf, "Nov", 3) == 0)
+      if (strncasecmp(*buf, "Nov", abbrev_length) == 0)
         *mon = 10;
       else
         return FALSE;
       break;
     case 'D':
-      if (strncasecmp(*buf, "Dec", 3) == 0)
+      if (strncasecmp(*buf, "Dec", abbrev_length) == 0)
         *mon = 11;
       else
         return FALSE;
@@ -152,8 +156,8 @@ scan_month_abbrev(const gchar **buf, gint *left, gint *mon)
       return FALSE;
     }
 
-  (*buf) += 3;
-  (*left) -= 3;
+  (*buf) += abbrev_length;
+  (*left) -= abbrev_length;
   return TRUE;
 }
 
@@ -328,7 +332,7 @@ __parse_usec(const guchar **data, gint *length)
           src++;
           (*length)--;
         }
-      while (isdigit(*src))
+      while (*length > 0 && isdigit(*src))
         {
           src++;
           (*length)--;
@@ -342,19 +346,21 @@ __parse_usec(const guchar **data, gint *length)
 static gboolean
 __has_iso_timezone(const guchar *src, gint length)
 {
-  return (length >= 5) &&
+  return (length >= 6) &&
          (*src == '+' || *src == '-') &&
          isdigit(*(src+1)) &&
          isdigit(*(src+2)) &&
          *(src+3) == ':' &&
          isdigit(*(src+4)) &&
          isdigit(*(src+5)) &&
-         !isdigit(*(src+6));
+         (length < 7 || !isdigit(*(src+6)));
 }
 
 static guint32
 __parse_iso_timezone(const guchar **data, gint *length)
 {
+  g_assert(*length >= 6);
+
   gint hours, mins;
   const guchar *src = *data;
   guint32 tz = 0;
@@ -364,8 +370,10 @@ __parse_iso_timezone(const guchar **data, gint *length)
   hours = (*(src + 1) - '0') * 10 + *(src + 2) - '0';
   mins = (*(src + 4) - '0') * 10 + *(src + 5) - '0';
   tz = sign * (hours * 3600 + mins * 60);
+
   src += 6;
   (*length) -= 6;
+
   *data = src;
   return tz;
 }
@@ -419,7 +427,7 @@ __parse_bsd_timestamp(const guchar **data, gint *length, WallClockTime *wct)
       if (!scan_pix_timestamp((const gchar **) &src, &left, wct))
         return FALSE;
 
-      if (*src == ':')
+      if (left && *src == ':')
         {
           src++;
           left--;
@@ -470,7 +478,7 @@ scan_rfc3164_timestamp(const guchar **data, gint *length, WallClockTime *wct)
    * looking at you, skip that as well, so we can reliably detect IPv6
    * addresses as hostnames, which would be using ":" as well. */
 
-  if (*src == ':')
+  if (left && *src == ':')
     {
       ++src;
       --left;
diff --git a/lib/timeutils/tests/test_scan-timestamp.c b/lib/timeutils/tests/test_scan-timestamp.c
index e65542531..50edb6a25 100644
--- a/lib/timeutils/tests/test_scan-timestamp.c
+++ b/lib/timeutils/tests/test_scan-timestamp.c
@@ -49,17 +49,21 @@ fake_time_add(time_t diff)
 }
 
 static gboolean
-_parse_rfc3164(const gchar *ts, gchar isotimestamp[32])
+_parse_rfc3164(const gchar *ts, gint len, gchar isotimestamp[32])
 {
   UnixTime stamp;
-  const guchar *data = (const guchar *) ts;
-  gint length = strlen(ts);
+  const guchar *tsu = (const guchar *) ts;
+  gint tsu_len = len < 0 ? strlen(ts) : len;
   GString *result = g_string_new("");
   WallClockTime wct = WALL_CLOCK_TIME_INIT;
 
-
+  const guchar *data = tsu;
+  gint length = tsu_len;
   gboolean success = scan_rfc3164_timestamp(&data, &length, &wct);
 
+  cr_assert(length >= 0);
+  cr_assert(data == &tsu[tsu_len - length]);
+
   unix_time_unset(&stamp);
   convert_wall_clock_time_to_unix_time(&wct, &stamp);
 
@@ -70,16 +74,21 @@ _parse_rfc3164(const gchar *ts, gchar isotimestamp[32])
 }
 
 static gboolean
-_parse_rfc5424(const gchar *ts, gchar isotimestamp[32])
+_parse_rfc5424(const gchar *ts, gint len, gchar isotimestamp[32])
 {
   UnixTime stamp;
-  const guchar *data = (const guchar *) ts;
-  gint length = strlen(ts);
+  const guchar *tsu = (const guchar *) ts;
+  gint tsu_len = len < 0 ? strlen(ts) : len;
   GString *result = g_string_new("");
   WallClockTime wct = WALL_CLOCK_TIME_INIT;
 
+  const guchar *data = tsu;
+  gint length = tsu_len;
   gboolean success = scan_rfc5424_timestamp(&data, &length, &wct);
 
+  cr_assert(length >= 0);
+  cr_assert(data == &tsu[tsu_len - length]);
+
   unix_time_unset(&stamp);
   convert_wall_clock_time_to_unix_time(&wct, &stamp);
 
@@ -90,31 +99,60 @@ _parse_rfc5424(const gchar *ts, gchar isotimestamp[32])
 }
 
 static gboolean
-_rfc3164_timestamp_eq(const gchar *ts, const gchar *expected, gchar converted[32])
+_rfc3164_timestamp_eq(const gchar *ts, gint len, const gchar *expected, gchar converted[32])
 {
-  cr_assert(_parse_rfc3164(ts, converted));
+  cr_assert(_parse_rfc3164(ts, len, converted));
   return strcmp(converted, expected) == 0;
 }
 
 static gboolean
-_rfc5424_timestamp_eq(const gchar *ts, const gchar *expected, gchar converted[32])
+_rfc5424_timestamp_eq(const gchar *ts, gint len, const gchar *expected, gchar converted[32])
 {
-  cr_assert(_parse_rfc5424(ts, converted));
+  cr_assert(_parse_rfc5424(ts, len, converted));
   return strcmp(converted, expected) == 0;
 }
 
 #define _expect_rfc3164_timestamp_eq(ts, expected) \
   ({ \
     gchar converted[32]; \
-    cr_expect(_rfc3164_timestamp_eq(ts, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
+    cr_expect(_rfc3164_timestamp_eq(ts, -1, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
+  })
+
+#define _expect_rfc3164_timestamp_len_eq(ts, len, expected) \
+  ({ \
+    gchar converted[32]; \
+    cr_expect(_rfc3164_timestamp_eq(ts, len, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
+  })
+
+#define _expect_rfc3164_fails(ts, len) \
+  ({  \
+    WallClockTime wct = WALL_CLOCK_TIME_INIT; \
+    const guchar *data = (guchar *) ts; \
+    gint length = len < 0 ? strlen(ts) : len; \
+    cr_assert_not(scan_rfc3164_timestamp(&data, &length, &wct)); \
   })
 
 #define _expect_rfc5424_timestamp_eq(ts, expected) \
   ({ \
     gchar converted[32]; \
-    cr_expect(_rfc5424_timestamp_eq(ts, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
+    cr_expect(_rfc5424_timestamp_eq(ts, -1, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
+  })
+
+#define _expect_rfc5424_timestamp_len_eq(ts, len, expected) \
+  ({ \
+    gchar converted[32]; \
+    cr_expect(_rfc5424_timestamp_eq(ts, len, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \
   })
 
+#define _expect_rfc5424_fails(ts, len) \
+  ({  \
+    WallClockTime wct = WALL_CLOCK_TIME_INIT; \
+    const guchar *data = (guchar *) ts; \
+    gint length = len < 0 ? strlen(ts) : len; \
+    cr_assert_not(scan_rfc5424_timestamp(&data, &length, &wct)); \
+  })
+
+
 Test(parse_timestamp, standard_bsd_format)
 {
   _expect_rfc3164_timestamp_eq("Oct  1 17:46:12", "2017-10-01T17:46:12.000+02:00");
@@ -148,6 +186,75 @@ Test(parse_timestamp, standard_bsd_format_year_in_the_past)
   _expect_rfc3164_timestamp_eq("Dec 31 17:46:12", "2017-12-31T17:46:12.000+01:00");
 }
 
+Test(parse_timestamp, non_zero_terminated_rfc3164_iso_input_is_handled_properly)
+{
+  gchar *ts = "2022-08-17T05:02:28.417Z whatever";
+  gint ts_len = 24;
+
+  _expect_rfc3164_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.417+00:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.417+00:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.417+00:00");
+
+  /* no "Z" parsed, timezone defaults to local, forced CET */
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len - 1, "2022-08-17T05:02:28.417+02:00");
+
+  /* msec is partially parsed as we trim the string from the right */
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len - 2, "2022-08-17T05:02:28.410+02:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len - 3, "2022-08-17T05:02:28.400+02:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len - 4, "2022-08-17T05:02:28.000+02:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len - 5, "2022-08-17T05:02:28.000+02:00");
+
+  for (gint i = 6; i < ts_len; i++)
+    _expect_rfc3164_fails(ts, ts_len - i);
+
+}
+
+Test(parse_timestamp, non_zero_terminated_rfc3164_bsd_pix_or_asa_input_is_handled_properly)
+{
+  gchar *ts = "Aug 17 2022 05:02:28: whatever";
+  gint ts_len = 21;
+
+  _expect_rfc3164_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.000+02:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.000+02:00");
+  _expect_rfc3164_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.000+02:00");
+
+  /* no ":" at the end, that's a problem, unrecognized */
+  _expect_rfc3164_fails(ts, ts_len - 1);
+
+  for (gint i = 1; i < ts_len; i++)
+    _expect_rfc3164_fails(ts, ts_len - i);
+}
+
+Test(parse_timestamp, non_zero_terminated_rfc5424_input_is_handled_properly)
+{
+  gchar *ts = "2022-08-17T05:02:28.417Z whatever";
+  gint ts_len = 24;
+
+  _expect_rfc5424_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.417+00:00");
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.417+00:00");
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.417+00:00");
+
+  /* no "Z" parsed, timezone defaults to local, forced CET */
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len - 1, "2022-08-17T05:02:28.417+02:00");
+
+  /* msec is partially parsed as we trim the string from the right */
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len - 2, "2022-08-17T05:02:28.410+02:00");
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len - 3, "2022-08-17T05:02:28.400+02:00");
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len - 4, "2022-08-17T05:02:28.000+02:00");
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len - 5, "2022-08-17T05:02:28.000+02:00");
+
+  for (gint i = 6; i < ts_len; i++)
+    _expect_rfc5424_fails(ts, ts_len - i);
+
+}
+
+Test(parse_timestamp, non_zero_terminated_rfc5424_timestamp_only)
+{
+  const gchar *ts = "2022-08-17T05:02:28.417+03:00";
+  gint ts_len = strlen(ts);
+  _expect_rfc5424_timestamp_len_eq(ts, ts_len, ts);
+}
+
 
 Test(parse_timestamp, daylight_saving_behavior_at_spring_with_explicit_timezones)
 {
diff --git a/news/bugfix-4110.md b/news/bugfix-4110.md
new file mode 100644
index 000000000..fbf1bee3c
--- /dev/null
+++ b/news/bugfix-4110.md
@@ -0,0 +1,4 @@
+Fixed buffer handling of syslog and timestamp parsers
+
+Multiple buffer out-of-bounds issues have been fixed, which could cause
+hangs, high CPU usage, or other undefined behavior.
-- 
2.39.0

openSUSE Build Service is sponsored by