Version in base suite: 2.74.6-2+deb12u8 Base version: glib2.0_2.74.6-2+deb12u8 Target version: glib2.0_2.74.6-2+deb12u9 Base file: /srv/ftp-master.debian.org/ftp/pool/main/g/glib2.0/glib2.0_2.74.6-2+deb12u8.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/g/glib2.0/glib2.0_2.74.6-2+deb12u9.dsc changelog | 16 patches/CVE-2026-0988.patch | 53 + patches/CVE-2026-1484-1.patch | 43 + patches/CVE-2026-1484-2.patch | 42 + patches/CVE-2026-1485.patch | 39 + patches/CVE-2026-1489-1.patch | 38 + patches/CVE-2026-1489-2.patch | 27 patches/CVE-2026-1489-3.patch | 286 ++++++++++ patches/CVE-2026-1489-4.patch | 65 ++ patches/gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch | 189 ++++++ patches/gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch | 42 + patches/series | 10 12 files changed, 850 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpwza6sbis/glib2.0_2.74.6-2+deb12u8.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpwza6sbis/glib2.0_2.74.6-2+deb12u9.dsc: no acceptable signature found diff: /srv/release.debian.org/tmp/45CF2iNrnC/glib2.0-2.74.6/gmodule/COPYING: No such file or directory diff: /srv/release.debian.org/tmp/TEeeEcWMDh/glib2.0-2.74.6/gmodule/COPYING: No such file or directory diff -Nru glib2.0-2.74.6/debian/changelog glib2.0-2.74.6/debian/changelog --- glib2.0-2.74.6/debian/changelog 2025-12-15 14:29:38.000000000 +0000 +++ glib2.0-2.74.6/debian/changelog 2026-02-13 11:35:33.000000000 +0000 @@ -1,3 +1,19 @@ +glib2.0 (2.74.6-2+deb12u9) bookworm; urgency=medium + + * Non-maintainer upload by the LTS Security Team. + * Add patch to fix timezone handling with Debian & Ubuntu's symlinks + (Closes: #1119919) (LP: #2130378) + * CVE-2026-0988: Missing input validation in g_buffered_input_stream_peek + (Closes: #1125752) + * CVE-2026-1484: Integer overflow in base64 encoding can cause memory corruption. + (Closes: #1126551) + * CVE-2026-1485: Buffer underflow vulnerability in content type parsing + caused by (signed) integer wrap for large inputs. (Closes: #1126550) + * CVE-2026-1489: Integer overflow in unicode conversion + can lead to memory corruption. (Closes: #1126549) + + -- Andreas Henriksson Fri, 13 Feb 2026 12:35:33 +0100 + glib2.0 (2.74.6-2+deb12u8) bookworm; urgency=medium * Team upload. diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-0988.patch glib2.0-2.74.6/debian/patches/CVE-2026-0988.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-0988.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-0988.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,53 @@ +From: Philip Withnall +Date: Thu, 18 Dec 2025 23:12:18 +0000 +Subject: gbufferedinputstream: Fix a potential integer overflow in peek() + +If the caller provides `offset` and `count` arguments which overflow, +their sum will overflow and could lead to `memcpy()` reading out more +memory than expected. + +Spotted by Codean Labs. + +Signed-off-by: Philip Withnall + +Fixes: #3851 +(cherry picked from commit c5766cff61ffce0b8e787eae09908ac348338e5f) +--- + gio/gbufferedinputstream.c | 2 +- + gio/tests/buffered-input-stream.c | 10 ++++++++++ + 2 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/gio/gbufferedinputstream.c b/gio/gbufferedinputstream.c +index 55450ce..56f5ece 100644 +--- a/gio/gbufferedinputstream.c ++++ b/gio/gbufferedinputstream.c +@@ -590,7 +590,7 @@ g_buffered_input_stream_peek (GBufferedInputStream *stream, + + available = g_buffered_input_stream_get_available (stream); + +- if (offset > available) ++ if (offset > available || offset > G_MAXSIZE - count) + return 0; + + end = MIN (offset + count, available); +diff --git a/gio/tests/buffered-input-stream.c b/gio/tests/buffered-input-stream.c +index ee084b3..39b4daf 100644 +--- a/gio/tests/buffered-input-stream.c ++++ b/gio/tests/buffered-input-stream.c +@@ -58,6 +58,16 @@ test_peek (void) + g_assert_cmpint (npeek, ==, 0); + g_free (buffer); + ++ buffer = g_new0 (char, 64); ++ npeek = g_buffered_input_stream_peek (G_BUFFERED_INPUT_STREAM (in), buffer, 8, 0); ++ g_assert_cmpint (npeek, ==, 0); ++ g_free (buffer); ++ ++ buffer = g_new0 (char, 64); ++ npeek = g_buffered_input_stream_peek (G_BUFFERED_INPUT_STREAM (in), buffer, 5, G_MAXSIZE); ++ g_assert_cmpint (npeek, ==, 0); ++ g_free (buffer); ++ + g_object_unref (in); + g_object_unref (base); + } diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1484-1.patch glib2.0-2.74.6/debian/patches/CVE-2026-1484-1.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1484-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1484-1.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,43 @@ +From: Marco Trevisan +Date: Fri, 23 Jan 2026 18:48:30 +0100 +Subject: gbase64: Use gsize to prevent potential overflow +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Both g_base64_encode_step() and g_base64_encode_close() return gsize +values, but these are summed to an int value. + +If the sum of these returned values is bigger than MAXINT, we overflow +while doing the null byte write. + +Spotted by treeplus. +Thanks to the Sovereign Tech Resilience programme from the Sovereign +Tech Agency. + +ID: #YWH-PGM9867-168 +Closes: #3870 + +(cherry picked from commit 6845f7776982849a2be1d8c9b0495e389092bff2) + +Co-authored-by: Marco Trevisan (Treviño) +(cherry picked from commit 5ba0ed9ab2c28294713bdc56a8744ff0a446b59c) +--- + glib/gbase64.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/glib/gbase64.c b/glib/gbase64.c +index 3c427f8..60c8560 100644 +--- a/glib/gbase64.c ++++ b/glib/gbase64.c +@@ -264,8 +264,9 @@ g_base64_encode (const guchar *data, + gsize len) + { + gchar *out; +- gint state = 0, outlen; ++ gint state = 0; + gint save = 0; ++ gsize outlen; + + g_return_val_if_fail (data != NULL || len == 0, NULL); + diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1484-2.patch glib2.0-2.74.6/debian/patches/CVE-2026-1484-2.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1484-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1484-2.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,42 @@ +From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= +Date: Wed, 21 Jan 2026 20:09:44 +0100 +Subject: gbase64: Ensure that the out value is within allocated size + +We do not want to deference or write to it + +Related to: #3870 + +(cherry picked from commit 25429bd0b22222d6986d000d62b44eebf490837d) +--- + glib/gbase64.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/glib/gbase64.c b/glib/gbase64.c +index 60c8560..0827e83 100644 +--- a/glib/gbase64.c ++++ b/glib/gbase64.c +@@ -267,6 +267,7 @@ g_base64_encode (const guchar *data, + gint state = 0; + gint save = 0; + gsize outlen; ++ gsize allocsize; + + g_return_val_if_fail (data != NULL || len == 0, NULL); + +@@ -274,10 +275,15 @@ g_base64_encode (const guchar *data, + +1 is needed for trailing \0, also check for unlikely integer overflow */ + g_return_val_if_fail (len < ((G_MAXSIZE - 1) / 4 - 1) * 3, NULL); + +- out = g_malloc ((len / 3 + 1) * 4 + 1); ++ allocsize = (len / 3 + 1) * 4 + 1; ++ out = g_malloc (allocsize); + + outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save); ++ g_assert (outlen <= allocsize); ++ + outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save); ++ g_assert (outlen <= allocsize); ++ + out[outlen] = '\0'; + + return (gchar *) out; diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1485.patch glib2.0-2.74.6/debian/patches/CVE-2026-1485.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1485.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1485.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,39 @@ +From: Marco Trevisan +Date: Fri, 23 Jan 2026 19:05:44 +0100 +Subject: gio/gcontenttype-fdo: Do not overflow if header is longer than + MAXINT +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +In case the header size is longer than MAXINT we may read and write to +invalid locations + +Spotted by treeplus. +Thanks to the Sovereign Tech Resilience programme from the Sovereign +Tech Agency. + +ID: #YWH-PGM9867-169 +Closes: #3871 + +(cherry picked from commit aacda5b07141b944408c79e83bcbed3b2e1e6e45) + +Co-authored-by: Marco Trevisan (Treviño) +(cherry picked from commit ee5acb2cefc643450509374da2600cd3bf49a109) +--- + gio/gcontenttype.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c +index 1e21bbd..6cf9157 100644 +--- a/gio/gcontenttype.c ++++ b/gio/gcontenttype.c +@@ -1021,7 +1021,7 @@ tree_match_free (TreeMatch *match) + static TreeMatch * + parse_header (gchar *line) + { +- gint len; ++ size_t len; + gchar *s; + TreeMatch *match; + diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1489-1.patch glib2.0-2.74.6/debian/patches/CVE-2026-1489-1.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1489-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1489-1.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,38 @@ +From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= +Date: Wed, 21 Jan 2026 22:00:17 +0100 +Subject: guniprop: Use size_t for output_marks length + +The input string length may overflow, and this would lead to wrong +behavior and invalid writes. + +Spotted by treeplus. +Thanks to the Sovereign Tech Resilience programme from the Sovereign +Tech Agency. + +ID: #YWH-PGM9867-171 +Closes: #3872 +(cherry picked from commit 662aa569efa65eaa4672ab0671eb8533a354cd89) +--- + glib/guniprop.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/glib/guniprop.c b/glib/guniprop.c +index d1363e5..d022abc 100644 +--- a/glib/guniprop.c ++++ b/glib/guniprop.c +@@ -772,13 +772,13 @@ get_locale_type (void) + return LOCALE_NORMAL; + } + +-static gint ++static size_t + output_marks (const char **p_inout, + char *out_buffer, + gboolean remove_dot) + { + const char *p = *p_inout; +- gint len = 0; ++ size_t len = 0; + + while (*p) + { diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1489-2.patch glib2.0-2.74.6/debian/patches/CVE-2026-1489-2.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1489-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1489-2.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,27 @@ +From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= +Date: Wed, 21 Jan 2026 22:01:49 +0100 +Subject: guniprop: Do not convert size_t to gint + +We were correctly using size_t in output_special_case() since commit +362f92b69, but then we converted the value back to int + +Related to: #3872 + +(cherry picked from commit 58356619525a1d565df8cc348e9784716f020f2f) +--- + glib/guniprop.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/glib/guniprop.c b/glib/guniprop.c +index d022abc..7ed770d 100644 +--- a/glib/guniprop.c ++++ b/glib/guniprop.c +@@ -798,7 +798,7 @@ output_marks (const char **p_inout, + return len; + } + +-static gint ++static size_t + output_special_case (gchar *out_buffer, + int offset, + int type, diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1489-3.patch glib2.0-2.74.6/debian/patches/CVE-2026-1489-3.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1489-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1489-3.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,286 @@ +From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= +Date: Wed, 21 Jan 2026 22:04:22 +0100 +Subject: guniprop: Ensure we do not overflow size in + g_utf8_{strdown,gstrup}() + +While this is technically not a security issue, when repeatedly adding +to a size_t value, we can overflow and start from 0. + +Now, while being unlikely, technically an utf8 lower or upper string can +have a longer size than the input value, and if the output string is +bigger than G_MAXSIZE we'd end up cutting it silently. + +Let's instead assert each time we increase the output length + +(cherry picked from commit 170dc8c4068db4c4cbf63c7d27192e230436da21) +--- + glib/guniprop.c | 107 ++++++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 69 insertions(+), 38 deletions(-) + +diff --git a/glib/guniprop.c b/glib/guniprop.c +index 7ed770d..f43979a 100644 +--- a/glib/guniprop.c ++++ b/glib/guniprop.c +@@ -772,14 +772,36 @@ get_locale_type (void) + return LOCALE_NORMAL; + } + +-static size_t +-output_marks (const char **p_inout, +- char *out_buffer, +- gboolean remove_dot) ++G_ALWAYS_INLINE static inline void ++increase_size (size_t *sizeptr, size_t add) ++{ ++ g_assert (G_MAXSIZE - *(sizeptr) >= add); ++ *(sizeptr) += add; ++} ++ ++G_ALWAYS_INLINE static inline void ++append_utf8_char_to_buffer (gunichar c, ++ char *out_buffer, ++ size_t *in_out_len) ++{ ++ gint utf8_len; ++ char *buffer; ++ ++ buffer = out_buffer ? out_buffer + *(in_out_len) : NULL; ++ utf8_len = g_unichar_to_utf8 (c, buffer); ++ ++ g_assert (utf8_len >= 0); ++ increase_size (in_out_len, utf8_len); ++} ++ ++static void ++append_mark (const char **p_inout, ++ char *out_buffer, ++ size_t *in_out_len, ++ gboolean remove_dot) + { + const char *p = *p_inout; +- size_t len = 0; +- ++ + while (*p) + { + gunichar c = g_utf8_get_char (p); +@@ -787,7 +809,7 @@ output_marks (const char **p_inout, + if (ISMARK (TYPE (c))) + { + if (!remove_dot || c != 0x307 /* COMBINING DOT ABOVE */) +- len += g_unichar_to_utf8 (c, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (c, out_buffer, in_out_len); + p = g_utf8_next_char (p); + } + else +@@ -795,14 +817,14 @@ output_marks (const char **p_inout, + } + + *p_inout = p; +- return len; + } + +-static size_t +-output_special_case (gchar *out_buffer, +- int offset, +- int type, +- int which) ++static void ++append_special_case (char *out_buffer, ++ size_t *in_out_len, ++ int offset, ++ int type, ++ int which) + { + const gchar *p = special_case_table + offset; + gint len; +@@ -814,10 +836,12 @@ output_special_case (gchar *out_buffer, + p += strlen (p) + 1; + + len = strlen (p); ++ g_assert (len < G_MAXSIZE - *in_out_len); ++ + if (out_buffer) +- memcpy (out_buffer, p, len); ++ memcpy (out_buffer + *in_out_len, p, len); + +- return len; ++ increase_size (in_out_len, len); + } + + static gsize +@@ -858,11 +882,13 @@ real_toupper (const gchar *str, + decomp_len = g_unichar_fully_decompose (c, FALSE, decomp, G_N_ELEMENTS (decomp)); + for (i=0; i < decomp_len; i++) + { ++ + if (decomp[i] != 0x307 /* COMBINING DOT ABOVE */) +- len += g_unichar_to_utf8 (g_unichar_toupper (decomp[i]), out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (g_unichar_toupper (decomp[i]), ++ out_buffer, &len); + } +- +- len += output_marks (&p, out_buffer ? out_buffer + len : NULL, TRUE); ++ ++ append_mark (&p, out_buffer, &len, TRUE); + + continue; + } +@@ -875,17 +901,17 @@ real_toupper (const gchar *str, + if (locale_type == LOCALE_TURKIC && c == 'i') + { + /* i => LATIN CAPITAL LETTER I WITH DOT ABOVE */ +- len += g_unichar_to_utf8 (0x130, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x130, out_buffer, &len); + } + else if (c == 0x0345) /* COMBINING GREEK YPOGEGRAMMENI */ + { + /* Nasty, need to move it after other combining marks .. this would go away if + * we normalized first. + */ +- len += output_marks (&p, out_buffer ? out_buffer + len : NULL, FALSE); ++ append_mark (&p, out_buffer, &len, TRUE); + + /* And output as GREEK CAPITAL LETTER IOTA */ +- len += g_unichar_to_utf8 (0x399, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x399, out_buffer, &len); + } + else if (IS (t, + OR (G_UNICODE_LOWERCASE_LETTER, +@@ -896,8 +922,8 @@ real_toupper (const gchar *str, + + if (val >= 0x1000000) + { +- len += output_special_case (out_buffer ? out_buffer + len : NULL, val - 0x1000000, t, +- t == G_UNICODE_LOWERCASE_LETTER ? 0 : 1); ++ append_special_case (out_buffer, &len, val - 0x1000000, t, ++ t == G_UNICODE_LOWERCASE_LETTER ? 0 : 1); + } + else + { +@@ -917,7 +943,7 @@ real_toupper (const gchar *str, + /* Some lowercase letters, e.g., U+000AA, FEMININE ORDINAL INDICATOR, + * do not have an uppercase equivalent, in which case val will be + * zero. */ +- len += g_unichar_to_utf8 (val ? val : c, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (val ? val : c, out_buffer, &len); + } + } + else +@@ -927,7 +953,7 @@ real_toupper (const gchar *str, + if (out_buffer) + memcpy (out_buffer + len, last, char_len); + +- len += char_len; ++ increase_size (&len, char_len); + } + + } +@@ -965,6 +991,8 @@ g_utf8_strup (const gchar *str, + * We use a two pass approach to keep memory management simple + */ + result_len = real_toupper (str, len, NULL, locale_type); ++ g_assert (result_len < G_MAXSIZE); ++ + result = g_malloc (result_len + 1); + real_toupper (str, len, result, locale_type); + result[result_len] = '\0'; +@@ -1022,14 +1050,15 @@ real_tolower (const gchar *str, + { + /* I + COMBINING DOT ABOVE => i (U+0069) + * LATIN CAPITAL LETTER I WITH DOT ABOVE => i (U+0069) */ +- len += g_unichar_to_utf8 (0x0069, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x0069, out_buffer, &len); ++ + if (combining_dot) + p = g_utf8_next_char (p); + } + else + { + /* I => LATIN SMALL LETTER DOTLESS I */ +- len += g_unichar_to_utf8 (0x131, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x131, out_buffer, &len); + } + } + /* Introduce an explicit dot above when lowercasing capital I's and J's +@@ -1037,19 +1066,19 @@ real_tolower (const gchar *str, + else if (locale_type == LOCALE_LITHUANIAN && + (c == 0x00cc || c == 0x00cd || c == 0x0128)) + { +- len += g_unichar_to_utf8 (0x0069, out_buffer ? out_buffer + len : NULL); +- len += g_unichar_to_utf8 (0x0307, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x0069, out_buffer, &len); ++ append_utf8_char_to_buffer (0x0307, out_buffer, &len); + + switch (c) + { + case 0x00cc: +- len += g_unichar_to_utf8 (0x0300, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x0300, out_buffer, &len); + break; + case 0x00cd: +- len += g_unichar_to_utf8 (0x0301, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x0301, out_buffer, &len); + break; + case 0x0128: +- len += g_unichar_to_utf8 (0x0303, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (0x0303, out_buffer, &len); + break; + } + } +@@ -1058,8 +1087,8 @@ real_tolower (const gchar *str, + c == 'J' || c == G_UNICHAR_FULLWIDTH_J || c == 0x012e) && + has_more_above (p)) + { +- len += g_unichar_to_utf8 (g_unichar_tolower (c), out_buffer ? out_buffer + len : NULL); +- len += g_unichar_to_utf8 (0x0307, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (g_unichar_tolower (c), out_buffer, &len); ++ append_utf8_char_to_buffer (0x0307, out_buffer, &len); + } + else if (c == 0x03A3) /* GREEK CAPITAL LETTER SIGMA */ + { +@@ -1082,7 +1111,7 @@ real_tolower (const gchar *str, + else + val = 0x3c2; /* GREEK SMALL FINAL SIGMA */ + +- len += g_unichar_to_utf8 (val, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (val, out_buffer, &len); + } + else if (IS (t, + OR (G_UNICODE_UPPERCASE_LETTER, +@@ -1093,7 +1122,7 @@ real_tolower (const gchar *str, + + if (val >= 0x1000000) + { +- len += output_special_case (out_buffer ? out_buffer + len : NULL, val - 0x1000000, t, 0); ++ append_special_case (out_buffer, &len, val - 0x1000000, t, 0); + } + else + { +@@ -1112,7 +1141,7 @@ real_tolower (const gchar *str, + + /* Not all uppercase letters are guaranteed to have a lowercase + * equivalent. If this is the case, val will be zero. */ +- len += g_unichar_to_utf8 (val ? val : c, out_buffer ? out_buffer + len : NULL); ++ append_utf8_char_to_buffer (val ? val : c, out_buffer, &len); + } + } + else +@@ -1122,7 +1151,7 @@ real_tolower (const gchar *str, + if (out_buffer) + memcpy (out_buffer + len, last, char_len); + +- len += char_len; ++ increase_size (&len, char_len); + } + + } +@@ -1159,6 +1188,8 @@ g_utf8_strdown (const gchar *str, + * We use a two pass approach to keep memory management simple + */ + result_len = real_tolower (str, len, NULL, locale_type); ++ g_assert (result_len < G_MAXSIZE); ++ + result = g_malloc (result_len + 1); + real_tolower (str, len, result, locale_type); + result[result_len] = '\0'; diff -Nru glib2.0-2.74.6/debian/patches/CVE-2026-1489-4.patch glib2.0-2.74.6/debian/patches/CVE-2026-1489-4.patch --- glib2.0-2.74.6/debian/patches/CVE-2026-1489-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/CVE-2026-1489-4.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,65 @@ +From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= +Date: Fri, 23 Jan 2026 17:39:34 +0100 +Subject: glib/tests/unicode: Add test debug information when parsing input + files + +On case of failures makes it easier to understand on what line of the +source file we're at, as it might not be clear for non-ascii chars + +(cherry picked from commit b96966058f4291db8970ced70ee22103e63679e5) +--- + glib/tests/unicode.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/glib/tests/unicode.c b/glib/tests/unicode.c +index c215891..daca4dc 100644 +--- a/glib/tests/unicode.c ++++ b/glib/tests/unicode.c +@@ -586,6 +586,7 @@ test_casemap_and_casefold (void) + const char *locale; + const char *test; + const char *expected; ++ size_t line = 0; + char *convert; + char *current_locale = setlocale (LC_CTYPE, NULL); + char *old_lc_all, *old_lc_messages, *old_lang; +@@ -606,6 +607,7 @@ test_casemap_and_casefold (void) + + while (fgets (buffer, sizeof (buffer), infile)) + { ++ line++; + if (buffer[0] == '#') + continue; + +@@ -648,6 +650,9 @@ test_casemap_and_casefold (void) + + convert = g_utf8_strup (test, -1); + expected = strings[4][0] ? strings[4] : test; ++ g_test_message ("Converting '%s' => '%s' (line %" G_GSIZE_FORMAT ")", ++ test, expected, line); ++ + g_assert_cmpstr (convert, ==, expected); + g_free (convert); + +@@ -667,9 +672,11 @@ test_casemap_and_casefold (void) + + infile = fopen (filename, "r"); + g_assert (infile != NULL); ++ line = 0; + + while (fgets (buffer, sizeof (buffer), infile)) + { ++ line++; + if (buffer[0] == '#') + continue; + +@@ -679,6 +686,9 @@ test_casemap_and_casefold (void) + test = strings[0]; + + convert = g_utf8_casefold (test, -1); ++ g_test_message ("Converting '%s' => '%s' (line %" G_GSIZE_FORMAT ")", ++ test, strings[1], line); ++ + g_assert_cmpstr (convert, ==, strings[1]); + g_free (convert); + diff -Nru glib2.0-2.74.6/debian/patches/gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch glib2.0-2.74.6/debian/patches/gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch --- glib2.0-2.74.6/debian/patches/gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,189 @@ +From: Alessandro Astone +Date: Tue, 20 Jan 2026 15:49:06 +0100 +Subject: gtimezone: Handle /etc/localtime symlink pointing to another symlink + +To resolve a timezone identifier from /etc/localtime we should traverse its +symlink recursively until we find a target under $TZDIR, then the identifier +is that target minus the $TZDIR path prefix. + +Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/3816 +Bug-Debian: https://bugs.debian.org/1119919 +Origin: upstream, 2.87.3, commit:7073c4872d96b78bfa9396b38e18e8043308550f +--- + glib/gtimezone.c | 130 +++++++++++++++++++++++++++---------------------------- + 1 file changed, 64 insertions(+), 66 deletions(-) + +diff --git a/glib/gtimezone.c b/glib/gtimezone.c +index b8eaf25..5d224aa 100644 +--- a/glib/gtimezone.c ++++ b/glib/gtimezone.c +@@ -504,6 +504,54 @@ zone_identifier_illumos (void) + } + #endif /* defined(__sun) && defined(__SRVR) */ + ++#define MAX_SYMLINKS 20 ++ ++/* ++ * Recursively resolve symlinks from @initial_path, ++ * returning the first target that is in a subtree of @zoneinfo ++ * or the first target that is a regular file, ++ * or NULL. ++ */ ++static gchar * ++resolve_symlink_to_zoneinfo (const char *initial_path, const char *zoneinfo) ++{ ++ char *current_path = g_strdup (initial_path); ++ ++ for (int i = 0; i < MAX_SYMLINKS; i++) ++ { ++ char *link_target; ++ char *parent_dir; ++ char *next_path; ++ GError *error = NULL; ++ ++ link_target = g_file_read_link (current_path, &error); ++ if (!link_target) ++ { ++ gboolean not_a_symlink = g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); ++ ++ g_clear_error (&error); ++ if (not_a_symlink) ++ return current_path; ++ break; ++ } ++ ++ parent_dir = g_path_get_dirname (current_path); ++ next_path = g_canonicalize_filename (link_target, parent_dir); ++ ++ g_free (parent_dir); ++ g_free (link_target); ++ g_free (current_path); ++ current_path = next_path; ++ ++ if (g_str_has_prefix (current_path, zoneinfo)) ++ return current_path; ++ } ++ ++ g_free (current_path); ++ return NULL; ++} ++#undef MAX_SYMLINKS ++ + /* + * returns the path to the top of the Olson zoneinfo timezone hierarchy. + */ +@@ -524,50 +572,16 @@ zone_identifier_unix (void) + { + gchar *resolved_identifier = NULL; + gsize prefix_len = 0; +- gchar *canonical_path = NULL; +- GError *read_link_err = NULL; + const gchar *tzdir; +- gboolean not_a_symlink_to_zoneinfo = FALSE; +- struct stat file_status; ++ GStatBuf buf; + +- /* Resolve the actual timezone pointed to by /etc/localtime. */ +- resolved_identifier = g_file_read_link ("/etc/localtime", &read_link_err); +- +- if (resolved_identifier != NULL) +- { +- if (!g_path_is_absolute (resolved_identifier)) +- { +- gchar *absolute_resolved_identifier = g_build_filename ("/etc", resolved_identifier, NULL); +- g_free (resolved_identifier); +- resolved_identifier = g_steal_pointer (&absolute_resolved_identifier); +- } ++ tzdir = g_getenv ("TZDIR"); ++ if (tzdir == NULL) ++ tzdir = zone_info_base_dir (); + +- if (g_lstat (resolved_identifier, &file_status) == 0) +- { +- if ((file_status.st_mode & S_IFMT) != S_IFREG) +- { +- /* Some systems (e.g. toolbox containers) make /etc/localtime be a symlink +- * to a symlink. +- * +- * Rather than try to cope with that, just ignore /etc/localtime and use +- * the fallback code to read timezone from /etc/timezone +- */ +- g_clear_pointer (&resolved_identifier, g_free); +- not_a_symlink_to_zoneinfo = TRUE; +- } +- } +- else +- { +- g_clear_pointer (&resolved_identifier, g_free); +- } +- } +- else +- { +- not_a_symlink_to_zoneinfo = g_error_matches (read_link_err, +- G_FILE_ERROR, +- G_FILE_ERROR_INVAL); +- g_clear_error (&read_link_err); +- } ++ /* Resolve the actual timezone pointed to by /etc/localtime. */ ++ if (g_lstat ("/etc/localtime", &buf) == 0 && S_ISLNK (buf.st_mode)) ++ resolved_identifier = resolve_symlink_to_zoneinfo ("/etc/localtime", tzdir); + + if (resolved_identifier == NULL) + { +@@ -580,36 +594,24 @@ zone_identifier_unix (void) + * as a last-ditch effort to parse the TZ= setting from within + * /etc/default/init + */ +- if (not_a_symlink_to_zoneinfo && (g_file_get_contents ("/var/db/zoneinfo", +- &resolved_identifier, +- NULL, NULL) || +- g_file_get_contents ("/etc/timezone", +- &resolved_identifier, +- NULL, NULL) ++ if (g_file_get_contents ("/var/db/zoneinfo", ++ &resolved_identifier, ++ NULL, NULL) || ++ g_file_get_contents ("/etc/timezone", ++ &resolved_identifier, ++ NULL, NULL) + #if defined(__sun) && defined(__SVR4) +- || +- (resolved_identifier = zone_identifier_illumos ()) ++ || (resolved_identifier = zone_identifier_illumos ()) + #endif +- )) ++ ) + g_strchomp (resolved_identifier); + else + { + /* Error */ + g_assert (resolved_identifier == NULL); +- goto out; ++ return NULL; + } + } +- else +- { +- /* Resolve relative path */ +- canonical_path = g_canonicalize_filename (resolved_identifier, "/etc"); +- g_free (resolved_identifier); +- resolved_identifier = g_steal_pointer (&canonical_path); +- } +- +- tzdir = g_getenv ("TZDIR"); +- if (tzdir == NULL) +- tzdir = zone_info_base_dir (); + + /* Strip the prefix and slashes if possible. */ + if (g_str_has_prefix (resolved_identifier, tzdir)) +@@ -624,10 +626,6 @@ zone_identifier_unix (void) + strlen (resolved_identifier) - prefix_len + 1 /* nul terminator */); + + g_assert (resolved_identifier != NULL); +- +-out: +- g_free (canonical_path); +- + return resolved_identifier; + } + diff -Nru glib2.0-2.74.6/debian/patches/gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch glib2.0-2.74.6/debian/patches/gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch --- glib2.0-2.74.6/debian/patches/gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch 1970-01-01 00:00:00.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch 2026-02-13 11:35:33.000000000 +0000 @@ -0,0 +1,42 @@ +From: Alessandro Astone +Date: Thu, 22 Jan 2026 16:13:09 +0100 +Subject: gtimezone: Use /var/db/timezone/zoneinfo as the default TZDIR for + macOS + +macOS defines /usr/share/zoneinfo as a symlink to /var/db/timezone/zoneinfo, +and /etc/localtime as a symlink to /var/db/timezone/zoneinfo/. + +By using /usr/share/zoneinfo as TZDIR, we would break the logic that resolves +/etc/localtime as a relative identifier by stripping the TZDIR prefix. +An absolute path still works as identifier, but we prefer a relative one. + +Furthermore, by ensuring that /etc/localtime points to a subdir of TZDIR we +correctly handle the case where /etc/localtime points to a symlink of symlink. + +Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/3816 +Bug-Debian: https://bugs.debian.org/1119919 +Origin: upstream, 2.87.3, commit:bd04ea91dc533303c064ec1cb627844a4aa09aaf +--- + glib/gtimezone.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/glib/gtimezone.c b/glib/gtimezone.c +index 5d224aa..6e8605d 100644 +--- a/glib/gtimezone.c ++++ b/glib/gtimezone.c +@@ -558,10 +558,14 @@ resolve_symlink_to_zoneinfo (const char *initial_path, const char *zoneinfo) + static const gchar * + zone_info_base_dir (void) + { +- if (g_file_test ("/usr/share/zoneinfo", G_FILE_TEST_IS_DIR)) ++ GStatBuf buf; ++ ++ if (g_lstat ("/usr/share/zoneinfo", &buf) == 0 && S_ISDIR (buf.st_mode)) + return "/usr/share/zoneinfo"; /* Most distros */ + else if (g_file_test ("/usr/share/lib/zoneinfo", G_FILE_TEST_IS_DIR)) + return "/usr/share/lib/zoneinfo"; /* Illumos distros */ ++ else if (g_file_test ("/var/db/timezone/zoneinfo", G_FILE_TEST_IS_DIR)) ++ return "/var/db/timezone/zoneinfo"; /* macOS */ + + /* need a better fallback case */ + return "/usr/share/zoneinfo"; diff -Nru glib2.0-2.74.6/debian/patches/series glib2.0-2.74.6/debian/patches/series --- glib2.0-2.74.6/debian/patches/series 2025-12-12 18:02:28.000000000 +0000 +++ glib2.0-2.74.6/debian/patches/series 2026-02-13 11:35:33.000000000 +0000 @@ -59,3 +59,13 @@ gvariant-parser-Use-size_t-to-count-numbers-of-child-elem.patch gvariant-parser-Convert-error-handling-code-to-use-size_t.patch gfileattribute-Fix-integer-overflow-calculating-escaping-.patch +gtimezone-Handle-etc-localtime-symlink-pointing-to-anothe.patch +gtimezone-Use-var-db-timezone-zoneinfo-as-the-default-TZD.patch +CVE-2026-0988.patch +CVE-2026-1484-1.patch +CVE-2026-1484-2.patch +CVE-2026-1485.patch +CVE-2026-1489-1.patch +CVE-2026-1489-2.patch +CVE-2026-1489-3.patch +CVE-2026-1489-4.patch