code:
Backport of:

From 0736b7c1e7cf4232c5d7eb2b0fbfe9be81bd3baa Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:41:21 +0000
Subject: [PATCH 04/11] glib: Use g_memdup2() instead of g_memdup() in obvious
 places
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Convert all the call sites which use `g_memdup()`’s length argument
trivially (for example, by passing a `sizeof()` or an existing `gsize`
variable), so that they use `g_memdup2()` instead.

In almost all of these cases the use of `g_memdup()` would not have
caused problems, but it will soon be deprecated, so best port away from
it

In particular, this fixes an overflow within `g_bytes_new()`, identified
as GHSL-2021-045 by GHSL team member Kevin Backhouse.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: GHSL-2021-045
Helps: #2319
---
 glib/gbytes.c               | 6 ++++--
 glib/gdir.c                 | 3 ++-
 glib/ghash.c                | 7 ++++---
 glib/giochannel.c           | 3 ++-
 glib/gslice.c               | 3 ++-
 glib/gtestutils.c           | 3 ++-
 glib/gvariant.c             | 7 ++++---
 glib/gvarianttype.c         | 3 ++-
 glib/tests/array-test.c     | 4 +++-
 glib/tests/option-context.c | 6 ++++--
 10 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/glib/gbytes.c b/glib/gbytes.c
index d56abe6c3..dee494820 100644
--- a/glib/gbytes.c
+++ b/glib/gbytes.c
@@ -34,6 +34,8 @@

 #include <string.h>

+#include "gstrfuncsprivate.h"
+
 /**
  * GBytes:
  *
@@ -95,7 +97,7 @@ g_bytes_new (gconstpointer data,
 {
   g_return_val_if_fail (data != NULL || size == 0, NULL);

-  return g_bytes_new_take (g_memdup (data, size), size);
+  return g_bytes_new_take (g_memdup2 (data, size), size);
 }

 /**
@@ -499,7 +501,7 @@ g_bytes_unref_to_data (GBytes *bytes,
        * Copy: Non g_malloc (or compatible) allocator, or static memory,
        * so we have to copy, and then unref.
        */
-      result = g_memdup (bytes->data, bytes->size);
+      result = g_memdup2 (bytes->data, bytes->size);
       *size = bytes->size;
       g_bytes_unref (bytes);
     }
diff --git a/glib/gdir.c b/glib/gdir.c
index 6b85e99c8..6747a8c6f 100644
--- a/glib/gdir.c
+++ b/glib/gdir.c
@@ -37,6 +37,7 @@
 #include "gconvert.h"
 #include "gfileutils.h"
 #include "gstrfuncs.h"
+#include "gstrfuncsprivate.h"
 #include "gtestutils.h"
 #include "glibintl.h"

@@ -112,7 +113,7 @@ g_dir_open_with_errno (const gchar *path,
     return NULL;
 #endif

-  return g_memdup (&dir, sizeof dir);
+  return g_memdup2 (&dir, sizeof dir);
 }

 /**
diff --git a/glib/ghash.c b/glib/ghash.c
index e61b03788..26f26062b 100644
--- a/glib/ghash.c
+++ b/glib/ghash.c
@@ -34,6 +34,7 @@
 #include "gmacros.h"
 #include "glib-private.h"
 #include "gstrfuncs.h"
+#include "gstrfuncsprivate.h"
 #include "gatomic.h"
 #include "gtestutils.h"
 #include "gslice.h"
@@ -964,7 +965,7 @@ g_hash_table_ensure_keyval_fits (GHashTable *hash_table, gpointer key, gpointer
       if (hash_table->have_big_keys)
         {
           if (key != value)
-            hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size);
+            hash_table->values = g_memdup2 (hash_table->keys, sizeof (gpointer) * hash_table->size);
           /* Keys and values are both big now, so no need for further checks */
           return;
         }
@@ -972,7 +973,7 @@ g_hash_table_ensure_keyval_fits (GHashTable *hash_table, gpointer key, gpointer
         {
           if (key != value)
             {
-              hash_table->values = g_memdup (hash_table->keys, sizeof (guint) * hash_table->size);
+              hash_table->values = g_memdup2 (hash_table->keys, sizeof (guint) * hash_table->size);
               is_a_set = FALSE;
             }
         }
@@ -1000,7 +1001,7 @@ g_hash_table_ensure_keyval_fits (GHashTable *hash_table, gpointer key, gpointer

   /* Just split if necessary */
   if (is_a_set && key != value)
-    hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size);
+    hash_table->values = g_memdup2 (hash_table->keys, sizeof (gpointer) * hash_table->size);

 #endif
 }
diff --git a/glib/giochannel.c b/glib/giochannel.c
index 1956e9dc6..15927c391 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -37,6 +37,7 @@
 #include "giochannel.h"

 #include "gstrfuncs.h"
+#include "gstrfuncsprivate.h"
 #include "gtestutils.h"
 #include "glibintl.h"

@@ -892,7 +893,7 @@ g_io_channel_set_line_term (GIOChannel	*channel,
     length = strlen (line_term);

   g_free (channel->line_term);
-  channel->line_term = line_term ? g_memdup (line_term, length) : NULL;
+  channel->line_term = line_term ? g_memdup2 (line_term, length) : NULL;
   channel->line_term_len = length;
 }

diff --git a/glib/gslice.c b/glib/gslice.c
index 4c758c3be..bcdbb8853 100644
--- a/glib/gslice.c
+++ b/glib/gslice.c
@@ -41,6 +41,7 @@
 #include "gmain.h"
 #include "gmem.h"               /* gslice.h */
 #include "gstrfuncs.h"
+#include "gstrfuncsprivate.h"
 #include "gutils.h"
 #include "gtrashstack.h"
 #include "gtestutils.h"
@@ -350,7 +351,7 @@ g_slice_get_config_state (GSliceConfig ckey,
       array[i++] = allocator->contention_counters[address];
       array[i++] = allocator_get_magazine_threshold (allocator, address);
       *n_values = i;
-      return g_memdup (array, sizeof (array[0]) * *n_values);
+      return g_memdup2 (array, sizeof (array[0]) * *n_values);
     default:
       return NULL;
     }
diff --git a/glib/gtestutils.c b/glib/gtestutils.c
index dd789482f..5887ecc36 100644
--- a/glib/gtestutils.c
+++ b/glib/gtestutils.c
@@ -49,6 +49,7 @@
 #include "gpattern.h"
 #include "grand.h"
 #include "gstrfuncs.h"
+#include "gstrfuncsprivate.h"
 #include "gtimer.h"
 #include "gslice.h"
 #include "gspawn.h"
@@ -3798,7 +3799,7 @@ g_test_log_extract (GTestLogBuffer *tbuffer)
       if (p <= tbuffer->data->str + mlength)
         {
           g_string_erase (tbuffer->data, 0, mlength);
-          tbuffer->msgs = g_slist_prepend (tbuffer->msgs, g_memdup (&msg, sizeof (msg)));
+          tbuffer->msgs = g_slist_prepend (tbuffer->msgs, g_memdup2 (&msg, sizeof (msg)));
           return TRUE;
         }

diff --git a/glib/gvariant.c b/glib/gvariant.c
index b61bf7278..d6f68a9ea 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -33,6 +33,7 @@

 #include <string.h>

+#include "gstrfuncsprivate.h"

 /**
  * SECTION:gvariant
@@ -725,7 +726,7 @@ g_variant_new_variant (GVariant *value)
   g_variant_ref_sink (value);

   return g_variant_new_from_children (G_VARIANT_TYPE_VARIANT,
-                                      g_memdup (&value, sizeof value),
+                                      g_memdup2 (&value, sizeof value),
                                       1, g_variant_is_trusted (value));
 }

@@ -1229,7 +1230,7 @@ g_variant_new_fixed_array (const GVariantType  *element_type,
       return NULL;
     }

-  data = g_memdup (elements, n_elements * element_size);
+  data = g_memdup2 (elements, n_elements * element_size);
   value = g_variant_new_from_data (array_type, data,
                                    n_elements * element_size,
                                    FALSE, g_free, data);
@@ -1908,7 +1909,7 @@ g_variant_dup_bytestring (GVariant *value,
   if (length)
     *length = size;

-  return g_memdup (original, size + 1);
+  return g_memdup2 (original, size + 1);
 }

 /**
diff --git a/glib/gvarianttype.c b/glib/gvarianttype.c
index 1a228f73b..07659ff12 100644
--- a/glib/gvarianttype.c
+++ b/glib/gvarianttype.c
@@ -28,6 +28,7 @@

 #include <string.h>

+#include "gstrfuncsprivate.h"

 /**
  * SECTION:gvarianttype
@@ -1181,7 +1182,7 @@ g_variant_type_new_tuple (const GVariantType * const *items,
   g_assert (offset < sizeof buffer);
   buffer[offset++] = ')';

-  return (GVariantType *) g_memdup (buffer, offset);
+  return (GVariantType *) g_memdup2 (buffer, offset);
 }

 /**
diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c
index 3fcf1136a..11982f822 100644
--- a/glib/tests/array-test.c
+++ b/glib/tests/array-test.c
@@ -29,6 +29,8 @@
 #include <string.h>
 #include "glib.h"

+#include "gstrfuncsprivate.h"
+
 /* Test data to be passed to any function which calls g_array_new(), providing
  * the parameters for that call. Most #GArray tests should be repeated for all
  * possible values of #ArrayTestData. */
@@ -1642,7 +1644,7 @@ byte_array_new_take (void)
   GByteArray *gbarray;
   guint8 *data;

-  data = g_memdup ("woooweeewow", 11);
+  data = g_memdup2 ("woooweeewow", 11);
   gbarray = g_byte_array_new_take (data, 11);
   g_assert (gbarray->data == data);
   g_assert_cmpuint (gbarray->len, ==, 11);
diff --git a/glib/tests/option-context.c b/glib/tests/option-context.c
index 149d22353..88d2b80d1 100644
--- a/glib/tests/option-context.c
+++ b/glib/tests/option-context.c
@@ -27,6 +27,8 @@
 #include <string.h>
 #include <locale.h>

+#include "gstrfuncsprivate.h"
+
 static GOptionEntry main_entries[] = {
   { "main-switch", 0, 0,
     G_OPTION_ARG_NONE, NULL,
@@ -256,7 +258,7 @@ join_stringv (int argc, char **argv)
 static char **
 copy_stringv (char **argv, int argc)
 {
-  return g_memdup (argv, sizeof (char *) * (argc + 1));
+  return g_memdup2 (argv, sizeof (char *) * (argc + 1));
 }

 static void
@@ -2323,7 +2325,7 @@ test_group_parse (void)
   g_option_context_add_group (context, group);

   argv = split_string ("program --test arg1 -f arg2 --group-test arg3 --frob arg4 -z arg5", &argc);
-  orig_argv = g_memdup (argv, (argc + 1) * sizeof (char *));
+  orig_argv = g_memdup2 (argv, (argc + 1) * sizeof (char *));

   retval = g_option_context_parse (context, &argc, &argv, &error);

--
2.30.1


code:
From 0cbad673215ec8a049b7fe2ff44b0beed31b376e Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 16:12:24 +0000
Subject: [PATCH 05/11] gwinhttpfile: Avoid arithmetic overflow when
 calculating a size
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The members of `URL_COMPONENTS` (`winhttp_file->url`) are `DWORD`s, i.e.
32-bit unsigned integers. Adding to and multiplying them may cause them
to overflow the unsigned integer bounds, even if the result is passed to
`g_memdup2()` which accepts a `gsize`.

Cast the `URL_COMPONENTS` members to `gsize` first to ensure that the
arithmetic is done in terms of `gsize`s rather than unsigned integers.

Spotted by Sebastian Dröge.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/win32/gwinhttpfile.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/gio/win32/gwinhttpfile.c b/gio/win32/gwinhttpfile.c
index 040ee8564..246ec0578 100644
--- a/gio/win32/gwinhttpfile.c
+++ b/gio/win32/gwinhttpfile.c
@@ -394,10 +394,10 @@ g_winhttp_file_resolve_relative_path (GFile      *file,
   child = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
   child->vfs = winhttp_file->vfs;
   child->url = winhttp_file->url;
-  child->url.lpszScheme = g_memdup2 (winhttp_file->url.lpszScheme, (winhttp_file->url.dwSchemeLength+1)*2);
-  child->url.lpszHostName = g_memdup2 (winhttp_file->url.lpszHostName, (winhttp_file->url.dwHostNameLength+1)*2);
-  child->url.lpszUserName = g_memdup2 (winhttp_file->url.lpszUserName, (winhttp_file->url.dwUserNameLength+1)*2);
-  child->url.lpszPassword = g_memdup2 (winhttp_file->url.lpszPassword, (winhttp_file->url.dwPasswordLength+1)*2);
+  child->url.lpszScheme = g_memdup2 (winhttp_file->url.lpszScheme, ((gsize) winhttp_file->url.dwSchemeLength + 1) * 2);
+  child->url.lpszHostName = g_memdup2 (winhttp_file->url.lpszHostName, ((gsize) winhttp_file->url.dwHostNameLength + 1) * 2);
+  child->url.lpszUserName = g_memdup2 (winhttp_file->url.lpszUserName, ((gsize) winhttp_file->url.dwUserNameLength + 1) * 2);
+  child->url.lpszPassword = g_memdup2 (winhttp_file->url.lpszPassword, ((gsize) winhttp_file->url.dwPasswordLength + 1) * 2);
   child->url.lpszUrlPath = wnew_path;
   child->url.dwUrlPathLength = wcslen (wnew_path);
   child->url.lpszExtraInfo = NULL;
--
2.30.1

repo_name: maximed/guix

license: mit


code:
From f9ee2275cbc312c0b4cdbc338a4fbb76eb36fb9a Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:49:00 +0000
Subject: [PATCH 06/11] gdatainputstream: Handle stop_chars_len internally as
 gsize

Previously it was handled as a `gssize`, which meant that if the
`stop_chars` string was longer than `G_MAXSSIZE` there would be an
overflow.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gdatainputstream.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c
index 2e7750cb5..2cdcbda19 100644
--- a/gio/gdatainputstream.c
+++ b/gio/gdatainputstream.c
@@ -27,6 +27,7 @@
 #include "gioenumtypes.h"
 #include "gioerror.h"
 #include "glibintl.h"
+#include "gstrfuncsprivate.h"

 #include <string.h>

@@ -856,7 +857,7 @@ static gssize
 scan_for_chars (GDataInputStream *stream,
 		gsize            *checked_out,
 		const char       *stop_chars,
-                gssize            stop_chars_len)
+                gsize             stop_chars_len)
 {
   GBufferedInputStream *bstream;
   const char *buffer;
@@ -952,7 +953,7 @@ typedef struct
   gsize checked;

   gchar *stop_chars;
-  gssize stop_chars_len;
+  gsize stop_chars_len;
   gsize length;
 } GDataInputStreamReadData;

@@ -1078,12 +1079,17 @@ g_data_input_stream_read_async (GDataInputStream    *stream,
 {
   GDataInputStreamReadData *data;
   GTask *task;
+  gsize stop_chars_len_unsigned;

   data = g_slice_new0 (GDataInputStreamReadData);
-  if (stop_chars_len == -1)
-    stop_chars_len = strlen (stop_chars);
-  data->stop_chars = g_memdup (stop_chars, stop_chars_len);
-  data->stop_chars_len = stop_chars_len;
+
+  if (stop_chars_len < 0)
+    stop_chars_len_unsigned = strlen (stop_chars);
+  else
+    stop_chars_len_unsigned = (gsize) stop_chars_len;
+
+  data->stop_chars = g_memdup2 (stop_chars, stop_chars_len_unsigned);
+  data->stop_chars_len = stop_chars_len_unsigned;
   data->last_saw_cr = FALSE;

   task = g_task_new (stream, cancellable, callback, user_data);
@@ -1338,17 +1344,20 @@ g_data_input_stream_read_upto (GDataInputStream  *stream,
   gssize found_pos;
   gssize res;
   char *data_until;
+  gsize stop_chars_len_unsigned;

   g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);

   if (stop_chars_len < 0)
-    stop_chars_len = strlen (stop_chars);
+    stop_chars_len_unsigned = strlen (stop_chars);
+  else
+    stop_chars_len_unsigned = (gsize) stop_chars_len;

   bstream = G_BUFFERED_INPUT_STREAM (stream);

   checked = 0;

-  while ((found_pos = scan_for_chars (stream, &checked, stop_chars, stop_chars_len)) == -1)
+  while ((found_pos = scan_for_chars (stream, &checked, stop_chars, stop_chars_len_unsigned)) == -1)
     {
       if (g_buffered_input_stream_get_available (bstream) ==
           g_buffered_input_stream_get_buffer_size (bstream))
--
2.30.1


code:
Backport of:

From 2aaf593a9eb96d84fe3be740aca2810a97d95592 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:50:37 +0000
Subject: [PATCH 07/11] gwin32: Use gsize internally in g_wcsdup()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This allows it to handle strings up to length `G_MAXSIZE` — previously
it would overflow with such strings.

Update the several copies of it identically.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gwin32appinfo.c     | 33 ++++++++++++++++++++++++++-------
 gio/gwin32registrykey.c | 34 ++++++++++++++++++++++++++--------
 2 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index 9f335b370..dd7a96a4a 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -464,15 +464,34 @@ static GWin32RegistryKey *applications_key;
 /* Watch this key */
 static GWin32RegistryKey *classes_root_key;

+static gsize
+g_utf16_len (const gunichar2 *str)
+{
+  gsize result;
+
+  for (result = 0; str[0] != 0; str++, result++)
+    ;
+
+  return result;
+}
+
 static gunichar2 *
-g_wcsdup (const gunichar2 *str, gssize str_size)
+g_wcsdup (const gunichar2 *str, gssize str_len)
 {
-  if (str_size == -1)
-    {
-      str_size = wcslen (str) + 1;
-      str_size *= sizeof (gunichar2);
-    }
-  return g_memdup (str, str_size);
+  gsize str_len_unsigned;
+  gsize str_size;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  if (str_len < 0)
+    str_len_unsigned = g_utf16_len (str);
+  else
+    str_len_unsigned = (gsize) str_len;
+
+  g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
+  str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
+
+  return g_memdup2 (str, str_size);
 }

 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
diff --git a/gio/gwin32registrykey.c b/gio/gwin32registrykey.c
index 619fd48af..fbd65311a 100644
--- a/gio/gwin32registrykey.c
+++ b/gio/gwin32registrykey.c
@@ -127,16 +127,34 @@ typedef enum
   G_WIN32_REGISTRY_UPDATED_PATH = 1,
 } GWin32RegistryKeyUpdateFlag;

+static gsize
+g_utf16_len (const gunichar2 *str)
+{
+  gsize result;
+
+  for (result = 0; str[0] != 0; str++, result++)
+    ;
+
+  return result;
+}
+
 static gunichar2 *
-g_wcsdup (const gunichar2 *str,
-          gssize           str_size)
+g_wcsdup (const gunichar2 *str, gssize str_len)
 {
-  if (str_size == -1)
-    {
-      str_size = wcslen (str) + 1;
-      str_size *= sizeof (gunichar2);
-    }
-  return g_memdup (str, str_size);
+  gsize str_len_unsigned;
+  gsize str_size;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  if (str_len < 0)
+    str_len_unsigned = g_utf16_len (str);
+  else
+    str_len_unsigned = (gsize) str_len;
+
+  g_assert (str_len_unsigned <= G_MAXSIZE / sizeof (gunichar2) - 1);
+  str_size = (str_len_unsigned + 1) * sizeof (gunichar2);
+
+  return g_memdup2 (str, str_size);
 }

 /**
--
2.30.1


code:
From ba8ca443051f93a74c0d03d62e70402036f967a5 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:58:32 +0000
Subject: [PATCH 08/11] gkeyfilesettingsbackend: Handle long keys when
 converting paths

Previously, the code in `convert_path()` could not handle keys longer
than `G_MAXINT`, and would overflow if that was exceeded.

Convert the code to use `gsize` and `g_memdup2()` throughout, and
change from identifying the position of the final slash in the string
using a signed offset `i`, to using a pointer to the character (and
`strrchr()`). This allows the slash to be at any position in a
`G_MAXSIZE`-long string, without sacrificing a bit of the offset for
indicating whether a slash was found.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gkeyfilesettingsbackend.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index cd5765afd..25b057672 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -33,6 +33,7 @@
 #include "gfilemonitor.h"
 #include "gsimplepermission.h"
 #include "gsettingsbackendinternal.h"
+#include "gstrfuncsprivate.h"
 #include "giomodule-priv.h"
 #include "gportalsupport.h"

@@ -145,8 +146,8 @@ convert_path (GKeyfileSettingsBackend  *kfsb,
               gchar                   **group,
               gchar                   **basename)
 {
-  gint key_len = strlen (key);
-  gint i;
+  gsize key_len = strlen (key);
+  const gchar *last_slash;

   if (key_len < kfsb->prefix_len ||
       memcmp (key, kfsb->prefix, kfsb->prefix_len) != 0)
@@ -155,38 +156,36 @@ convert_path (GKeyfileSettingsBackend  *kfsb,
   key_len -= kfsb->prefix_len;
   key += kfsb->prefix_len;

-  for (i = key_len; i >= 0; i--)
-    if (key[i] == '/')
-      break;
+  last_slash = strrchr (key, '/');

   if (kfsb->root_group)
     {
       /* if a root_group was specified, make sure the user hasn't given
        * a path that ghosts that group name
        */
-      if (i == kfsb->root_group_len && memcmp (key, kfsb->root_group, i) == 0)
+      if (last_slash != NULL && (last_slash - key) == kfsb->root_group_len && memcmp (key, kfsb->root_group, last_slash - key) == 0)
         return FALSE;
     }
   else
     {
       /* if no root_group was given, ensure that the user gave a path */
-      if (i == -1)
+      if (last_slash == NULL)
         return FALSE;
     }

   if (group)
     {
-      if (i >= 0)
+      if (last_slash != NULL)
         {
-          *group = g_memdup (key, i + 1);
-          (*group)[i] = '\0';
+          *group = g_memdup2 (key, (last_slash - key) + 1);
+          (*group)[(last_slash - key)] = '\0';
         }
       else
         *group = g_strdup (kfsb->root_group);
     }

   if (basename)
-    *basename = g_memdup (key + i + 1, key_len - i);
+    *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));

   return TRUE;
 }
--
2.30.1


code:
From 65ec7f4d6e8832c481f6e00e2eb007b9a60024ce Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 14:00:53 +0000
Subject: [PATCH 09/11] gsocket: Use gsize to track native sockaddr's size
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Don’t use an `int`, that’s potentially too small. In practical terms,
this is not a problem, since no socket address is going to be that big.

By making these changes we can use `g_memdup2()` without warnings,
though. Fewer warnings is good.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gsocket.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/gio/gsocket.c b/gio/gsocket.c
index 66073af83..a3af149e8 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -75,6 +75,7 @@
 #include "gcredentialsprivate.h"
 #include "glibintl.h"
 #include "gioprivate.h"
+#include "gstrfuncsprivate.h"

 #ifdef G_OS_WIN32
 /* For Windows XP runtime compatibility, but use the system's if_nametoindex() if available */
@@ -174,7 +175,7 @@ static gboolean     g_socket_datagram_based_condition_wait       (GDatagramBased
                                                                   GError          **error);

 static GSocketAddress *
-cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len);
+cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len);

 static gssize
 g_socket_receive_message_with_timeout  (GSocket                 *socket,
@@ -260,7 +261,7 @@ struct _GSocketPrivate
   struct {
     GSocketAddress *addr;
     struct sockaddr *native;
-    gint native_len;
+    gsize native_len;
     guint64 last_used;
   } recv_addr_cache[RECV_ADDR_CACHE_SIZE];
 };
@@ -5211,14 +5212,14 @@ g_socket_send_messages_with_timeout (GSocket        *socket,
 }

 static GSocketAddress *
-cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
+cache_recv_address (GSocket *socket, struct sockaddr *native, size_t native_len)
 {
   GSocketAddress *saddr;
   gint i;
   guint64 oldest_time = G_MAXUINT64;
   gint oldest_index = 0;

-  if (native_len <= 0)
+  if (native_len == 0)
     return NULL;

   saddr = NULL;
@@ -5226,7 +5227,7 @@ cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
     {
       GSocketAddress *tmp = socket->priv->recv_addr_cache[i].addr;
       gpointer tmp_native = socket->priv->recv_addr_cache[i].native;
-      gint tmp_native_len = socket->priv->recv_addr_cache[i].native_len;
+      gsize tmp_native_len = socket->priv->recv_addr_cache[i].native_len;

       if (!tmp)
         continue;
@@ -5256,7 +5257,7 @@ cache_recv_address (GSocket *socket, struct sockaddr *native, int native_len)
       g_free (socket->priv->recv_addr_cache[oldest_index].native);
     }

-  socket->priv->recv_addr_cache[oldest_index].native = g_memdup (native, native_len);
+  socket->priv->recv_addr_cache[oldest_index].native = g_memdup2 (native, native_len);
   socket->priv->recv_addr_cache[oldest_index].native_len = native_len;
   socket->priv->recv_addr_cache[oldest_index].addr = g_object_ref (saddr);
   socket->priv->recv_addr_cache[oldest_index].last_used = g_get_monotonic_time ();
@@ -5404,6 +5405,9 @@ g_socket_receive_message_with_timeout (GSocket                 *socket,
     /* do it */
     while (1)
       {
+        /* addrlen has to be of type int because that’s how WSARecvFrom() is defined */
+        G_STATIC_ASSERT (sizeof addr <= G_MAXINT);
+
 	addrlen = sizeof addr;
 	if (address)
 	  result = WSARecvFrom (socket->priv->fd,
--
2.30.1


code:
From 777b95a88f006d39d9fe6d3321db17e7b0d4b9a4 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 14:07:39 +0000
Subject: [PATCH 10/11] gtlspassword: Forbid very long TLS passwords
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The public API `g_tls_password_set_value_full()` (and the vfunc it
invokes) can only accept a `gssize` length. Ensure that nul-terminated
strings passed to `g_tls_password_set_value()` can’t exceed that length.
Use `g_memdup2()` to avoid an overflow if they’re longer than
`G_MAXUINT` similarly.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gtlspassword.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/gio/gtlspassword.c b/gio/gtlspassword.c
index 1e437a7b6..dbcec41a8 100644
--- a/gio/gtlspassword.c
+++ b/gio/gtlspassword.c
@@ -23,6 +23,7 @@
 #include "glibintl.h"

 #include "gioenumtypes.h"
+#include "gstrfuncsprivate.h"
 #include "gtlspassword.h"

 #include <string.h>
@@ -287,9 +288,14 @@ g_tls_password_set_value (GTlsPassword  *password,
   g_return_if_fail (G_IS_TLS_PASSWORD (password));

   if (length < 0)
-    length = strlen ((gchar *)value);
+    {
+      /* FIXME: g_tls_password_set_value_full() doesn’t support unsigned gsize */
+      gsize length_unsigned = strlen ((gchar *) value);
+      g_return_if_fail (length_unsigned > G_MAXSSIZE);
+      length = (gssize) length_unsigned;
+    }

-  g_tls_password_set_value_full (password, g_memdup (value, length), length, g_free);
+  g_tls_password_set_value_full (password, g_memdup2 (value, (gsize) length), length, g_free);
 }

 /**
--
2.30.1


code:
From ecdf91400e9a538695a0895b95ad7e8abcdf1749 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 14:09:40 +0000
Subject: [PATCH 11/11] giochannel: Forbid very long line terminator strings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The public API `GIOChannel.line_term_len` is only a `guint`. Ensure that
nul-terminated strings passed to `g_io_channel_set_line_term()` can’t
exceed that length. Use `g_memdup2()` to avoid a warning (`g_memdup()`
is due to be deprecated), but not to avoid a bug, since it’s also
limited to `G_MAXUINT`.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 glib/giochannel.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/glib/giochannel.c b/glib/giochannel.c
index 15927c391..66c6591f0 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -884,16 +884,25 @@ g_io_channel_set_line_term (GIOChannel	*channel,
                             const gchar	*line_term,
 			    gint         length)
 {
+  guint length_unsigned;
+
   g_return_if_fail (channel != NULL);
   g_return_if_fail (line_term == NULL || length != 0); /* Disallow "" */

   if (line_term == NULL)
-    length = 0;
-  else if (length < 0)
-    length = strlen (line_term);
+    length_unsigned = 0;
+  else if (length >= 0)
+    length_unsigned = (guint) length;
+  else
+    {
+      /* FIXME: We’re constrained by line_term_len being a guint here */
+      gsize length_size = strlen (line_term);
+      g_return_if_fail (length_size > G_MAXUINT);
+      length_unsigned = (guint) length_size;
+    }

   g_free (channel->line_term);
-  channel->line_term = line_term ? g_memdup2 (line_term, length) : NULL;
+  channel->line_term = line_term ? g_memdup2 (line_term, length_unsigned) : NULL;
   channel->line_term_len = length;
 }

--
2.30.1


code:
From f8273b9aded135fe07094faebd527e43851aaf6e Mon Sep 17 00:00:00 2001
From: "Jan Alexander Steffens (heftig)" <jan.steffens@gmail.com>
Date: Sun, 7 Feb 2021 23:32:40 +0100
Subject: [PATCH 1/5] giochannel: Fix length_size bounds check

The inverted condition is an obvious error introduced by ecdf91400e9a.

Fixes https://gitlab.gnome.org/GNOME/glib/-/issues/2323

(cherry picked from commit a149bf2f9030168051942124536e303af8ba6176)
---
 glib/giochannel.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/glib/giochannel.c b/glib/giochannel.c
index 66c6591f0..0d9d5f223 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -897,7 +897,7 @@ g_io_channel_set_line_term (GIOChannel	*channel,
     {
       /* FIXME: We’re constrained by line_term_len being a guint here */
       gsize length_size = strlen (line_term);
-      g_return_if_fail (length_size > G_MAXUINT);
+      g_return_if_fail (length_size <= G_MAXUINT);
       length_unsigned = (guint) length_size;
     }

--
2.30.1


code:
From e069c50467712e6d607822afd6b6c15c2c343dff Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 8 Feb 2021 10:34:50 +0000
Subject: [PATCH 2/5] giochannel: Don't store negative line_term_len in
 GIOChannel struct

Adding test coverage indicated that this was another bug in 0cc11f74.

Fixes: 0cc11f74 "giochannel: Forbid very long line terminator strings"
Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/2323
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 5dc8b0014c03e7491d93b90275ab442e888a9628)
---
 glib/giochannel.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/glib/giochannel.c b/glib/giochannel.c
index 0d9d5f223..4c39b9dc0 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -903,7 +903,7 @@ g_io_channel_set_line_term (GIOChannel	*channel,

   g_free (channel->line_term);
   channel->line_term = line_term ? g_memdup2 (line_term, length_unsigned) : NULL;
-  channel->line_term_len = length;
+  channel->line_term_len = length_unsigned;
 }

 /**
--
2.30.1


code:
From 4506d1859a863087598c8d122740bae25b65b099 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 8 Feb 2021 10:04:48 +0000
Subject: [PATCH 4/5] gtlspassword: Fix inverted assertion

The intention here was to assert that the length of the password fits
in a gssize. Passwords more than half the size of virtual memory are
probably excessive.

Fixes: a8b204ff "gtlspassword: Forbid very long TLS passwords"
Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 61bb52ec42de1082bfb06ce1c737fc295bfe60b8)
---
 gio/gtlspassword.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gio/gtlspassword.c b/gio/gtlspassword.c
index dbcec41a8..bd86a6dfe 100644
--- a/gio/gtlspassword.c
+++ b/gio/gtlspassword.c
@@ -291,7 +291,7 @@ g_tls_password_set_value (GTlsPassword  *password,
     {
       /* FIXME: g_tls_password_set_value_full() doesn’t support unsigned gsize */
       gsize length_unsigned = strlen ((gchar *) value);
-      g_return_if_fail (length_unsigned > G_MAXSSIZE);
+      g_return_if_fail (length_unsigned <= G_MAXSSIZE);
       length = (gssize) length_unsigned;
     }

--
2.30.1


code:
From 3d1550354c3c6a8491c39881752d51cb7515f2c2 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Mon, 8 Feb 2021 10:22:39 +0000
Subject: [PATCH 5/5] tls-interaction: Add test coverage for various ways to
 set the password

Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit df4501316ca3903072400504a5ea76498db19538)
---
 gio/tests/tls-interaction.c | 55 +++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/gio/tests/tls-interaction.c b/gio/tests/tls-interaction.c
index 4f0737d7e..5661e8e0d 100644
--- a/gio/tests/tls-interaction.c
+++ b/gio/tests/tls-interaction.c
@@ -174,6 +174,38 @@ test_interaction_ask_password_finish_failure (GTlsInteraction    *interaction,
 }

+/* Return a copy of @str that is allocated in a silly way, to exercise
+ * custom free-functions. The returned pointer points to a copy of @str
+ * in a buffer of the form "BEFORE \0 str \0 AFTER". */
+static guchar *
+special_dup (const char *str)
+{
+  GString *buf = g_string_new ("BEFORE");
+  guchar *ret;
+
+  g_string_append_c (buf, '\0');
+  g_string_append (buf, str);
+  g_string_append_c (buf, '\0');
+  g_string_append (buf, "AFTER");
+  ret = (guchar *) g_string_free (buf, FALSE);
+  return ret + strlen ("BEFORE") + 1;
+}
+
+
+/* Free a copy of @str that was made with special_dup(), after asserting
+ * that it has not been corrupted. */
+static void
+special_free (gpointer p)
+{
+  gchar *s = p;
+  gchar *buf = s - strlen ("BEFORE") - 1;
+
+  g_assert_cmpstr (buf, ==, "BEFORE");
+  g_assert_cmpstr (s + strlen (s) + 1, ==, "AFTER");
+  g_free (buf);
+}
+
+
 static GTlsInteractionResult
 test_interaction_ask_password_sync_success (GTlsInteraction    *interaction,
                                             GTlsPassword       *password,
@@ -181,6 +213,8 @@ test_interaction_ask_password_sync_success (GTlsInteraction    *interaction,
                                             GError            **error)
 {
   TestInteraction *self;
+  const guchar *value;
+  gsize len;

   g_assert (TEST_IS_INTERACTION (interaction));
   self = TEST_INTERACTION (interaction);
@@ -192,6 +226,27 @@ test_interaction_ask_password_sync_success (GTlsInteraction    *interaction,
   g_assert (error != NULL);
   g_assert (*error == NULL);

+  /* Exercise different ways to set the value */
+  g_tls_password_set_value (password, (const guchar *) "foo", 4);
+  len = 0;
+  value = g_tls_password_get_value (password, &len);
+  g_assert_cmpmem (value, len, "foo", 4);
+
+  g_tls_password_set_value (password, (const guchar *) "bar", -1);
+  len = 0;
+  value = g_tls_password_get_value (password, &len);
+  g_assert_cmpmem (value, len, "bar", 3);
+
+  g_tls_password_set_value_full (password, special_dup ("baa"), 4, special_free);
+  len = 0;
+  value = g_tls_password_get_value (password, &len);
+  g_assert_cmpmem (value, len, "baa", 4);
+
+  g_tls_password_set_value_full (password, special_dup ("baz"), -1, special_free);
+  len = 0;
+  value = g_tls_password_get_value (password, &len);
+  g_assert_cmpmem (value, len, "baz", 3);
+
   /* Don't do this in real life. Include a null terminator for testing */
   g_tls_password_set_value (password, (const guchar *)"the password", 13);
   return G_TLS_INTERACTION_HANDLED;
--
2.30.1


code:
From cb9ee701ef46c1819eed4e2a4dc181682bdfc176 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Wed, 10 Feb 2021 21:16:39 +0000
Subject: [PATCH 1/3] gkeyfilesettingsbackend: Fix basename handling when group
 is unset

Fix an effective regression in commit
7781a9cbd2fd0aa84bee0f4eee88470640ff6706, which happens when
`convert_path()` is called with a `key` which contains no slashes. In
that case, the `key` is entirely the `basename`.

Prior to commit 7781a9cb, the code worked through a fluke of `i == -1`
cancelling out with the various additions in the `g_memdup()` call, and
effectively resulting in `g_strdup (key)`.

Spotted by Guido Berhoerster.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
---
 gio/gkeyfilesettingsbackend.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 25b057672..861c3a661 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -185,7 +185,12 @@ convert_path (GKeyfileSettingsBackend  *kfsb,
     }

   if (basename)
-    *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));
+    {
+      if (last_slash != NULL)
+        *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));
+      else
+        *basename = g_strdup (key);
+    }

   return TRUE;
 }
--
2.30.1


code:
From 31e0d403ba635dbbacbfbff74295e5db02558d76 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Wed, 10 Feb 2021 21:19:30 +0000
Subject: [PATCH 2/3] gkeyfilesettingsbackend: Disallow empty key or group
 names

These should never have been allowed; they will result in precondition
failures from the `GKeyFile` later on in the code.

A test will be added for this shortly.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
---
 gio/gkeyfilesettingsbackend.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 861c3a661..de216e615 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -158,6 +158,13 @@ convert_path (GKeyfileSettingsBackend  *kfsb,

   last_slash = strrchr (key, '/');

+  /* Disallow empty group names or key names */
+  if (key_len == 0 ||
+      (last_slash != NULL &&
+       (*(last_slash + 1) == '\0' ||
+        last_slash == key)))
+    return FALSE;
+
   if (kfsb->root_group)
     {
       /* if a root_group was specified, make sure the user hasn't given
--
2.30.1


code:
Backport of:

From 221c26685354dea2b2732df94404e8e5e77a1591 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Wed, 10 Feb 2021 21:21:36 +0000
Subject: [PATCH 3/3] tests: Add tests for key name handling in the keyfile
 backend

This tests the two recent commits.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
---
 gio/tests/gsettings.c | 171 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 170 insertions(+), 1 deletion(-)

diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index baadca8f5..afe594a23 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1,3 +1,4 @@
+#include <errno.h>
 #include <stdlib.h>
 #include <locale.h>
 #include <libintl.h>
@@ -1740,6 +1741,14 @@ key_changed_cb (GSettings *settings, const gchar *key, gpointer data)
   (*b) = TRUE;
 }

+typedef struct
+{
+  const gchar *path;
+  const gchar *root_group;
+  const gchar *keyfile_group;
+  const gchar *root_path;
+} KeyfileTestData;
+
 /*
  * Test that using a keyfile works
  */
@@ -1834,7 +1843,11 @@ test_keyfile (Fixture       *fixture,
   g_free (str);

   g_settings_set (settings, "farewell", "s", "cheerio");
-
+
+  /* Check that empty keys/groups are not allowed. */
+  g_assert_false (g_settings_is_writable (settings, ""));
+  g_assert_false (g_settings_is_writable (settings, "/"));
+
   /* When executing as root, changing the mode of the keyfile will have
    * no effect on the writability of the settings.
    */
@@ -1866,6 +1879,149 @@ test_keyfile (Fixture       *fixture,
   g_free (keyfile_path);
 }

+/*
+ * Test that using a keyfile works with a schema with no path set.
+ */
+static void
+test_keyfile_no_path (Fixture       *fixture,
+                      gconstpointer  user_data)
+{
+  const KeyfileTestData *test_data = user_data;
+  GSettingsBackend *kf_backend;
+  GSettings *settings;
+  GKeyFile *keyfile;
+  gboolean writable;
+  gchar *key = NULL;
+  GError *error = NULL;
+  gchar *keyfile_path = NULL, *store_path = NULL;
+
+  keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
+  store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
+  kf_backend = g_keyfile_settings_backend_new (store_path, test_data->root_path, test_data->root_group);
+  settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, test_data->path);
+  g_object_unref (kf_backend);
+
+  g_settings_reset (settings, "test-boolean");
+  g_assert_true (g_settings_get_boolean (settings, "test-boolean"));
+
+  writable = g_settings_is_writable (settings, "test-boolean");
+  g_assert_true (writable);
+  g_settings_set (settings, "test-boolean", "b", FALSE);
+
+  g_assert_false (g_settings_get_boolean (settings, "test-boolean"));
+
+  g_settings_delay (settings);
+  g_settings_set (settings, "test-boolean", "b", TRUE);
+  g_settings_apply (settings);
+
+  keyfile = g_key_file_new ();
+  g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL));
+
+  g_assert_true (g_key_file_get_boolean (keyfile, test_data->keyfile_group, "test-boolean", NULL));
+
+  g_key_file_free (keyfile);
+
+  g_settings_reset (settings, "test-boolean");
+  g_settings_apply (settings);
+  keyfile = g_key_file_new ();
+  g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL));
+
+  g_assert_false (g_key_file_get_string (keyfile, test_data->keyfile_group, "test-boolean", &error));
+  g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND);
+  g_clear_error (&error);
+
+  /* Check that empty keys/groups are not allowed. */
+  g_assert_false (g_settings_is_writable (settings, ""));
+  g_assert_false (g_settings_is_writable (settings, "/"));
+
+  /* Keys which ghost the root group name are not allowed. This can only be
+   * tested when the path is `/` as otherwise it acts as a prefix and prevents
+   * any ghosting. */
+  if (g_str_equal (test_data->path, "/"))
+    {
+      key = g_strdup_printf ("%s/%s", test_data->root_group, "");
+      g_assert_false (g_settings_is_writable (settings, key));
+      g_free (key);
+
+      key = g_strdup_printf ("%s/%s", test_data->root_group, "/");
+      g_assert_false (g_settings_is_writable (settings, key));
+      g_free (key);
+
+      key = g_strdup_printf ("%s/%s", test_data->root_group, "test-boolean");
+      g_assert_false (g_settings_is_writable (settings, key));
+      g_free (key);
+    }
+
+  g_key_file_free (keyfile);
+  g_object_unref (settings);
+
+  /* Clean up the temporary directory. */
+  g_assert_cmpint (g_chmod (keyfile_path, 0777) == 0 ? 0 : errno, ==, 0);
+  g_assert_cmpint (g_remove (store_path) == 0 ? 0 : errno, ==, 0);
+  g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0);
+  g_free (store_path);
+  g_free (keyfile_path);
+}
+
+/*
+ * Test that a keyfile rejects writes to keys outside its root path.
+ */
+static void
+test_keyfile_outside_root_path (Fixture       *fixture,
+                                gconstpointer  user_data)
+{
+  GSettingsBackend *kf_backend;
+  GSettings *settings;
+  gchar *keyfile_path = NULL, *store_path = NULL;
+
+  keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
+  store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
+  kf_backend = g_keyfile_settings_backend_new (store_path, "/tests/basic-types/", "root");
+  settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/tests/");
+  g_object_unref (kf_backend);
+
+  g_assert_false (g_settings_is_writable (settings, "test-boolean"));
+
+  g_object_unref (settings);
+
+  /* Clean up the temporary directory. The keyfile probably doesn’t exist, so
+   * don’t error on failure. */
+  g_remove (store_path);
+  g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0);
+  g_free (store_path);
+  g_free (keyfile_path);
+}
+
+/*
+ * Test that a keyfile rejects writes to keys in the root if no root group is set.
+ */
+static void
+test_keyfile_no_root_group (Fixture       *fixture,
+                            gconstpointer  user_data)
+{
+  GSettingsBackend *kf_backend;
+  GSettings *settings;
+  gchar *keyfile_path = NULL, *store_path = NULL;
+
+  keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL);
+  store_path = g_build_filename (keyfile_path, "gsettings.store", NULL);
+  kf_backend = g_keyfile_settings_backend_new (store_path, "/", NULL);
+  settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/");
+  g_object_unref (kf_backend);
+
+  g_assert_false (g_settings_is_writable (settings, "test-boolean"));
+  g_assert_true (g_settings_is_writable (settings, "child/test-boolean"));
+
+  g_object_unref (settings);
+
+  /* Clean up the temporary directory. The keyfile probably doesn’t exist, so
+   * don’t error on failure. */
+  g_remove (store_path);
+  g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0);
+  g_free (store_path);
+  g_free (keyfile_path);
+}
+
 /* Test that getting child schemas works
  */
 static void
@@ -2844,6 +3000,14 @@ main (int argc, char *argv[])
   gchar *override_text;
   gchar *enums;
   gint result;
+  const KeyfileTestData keyfile_test_data_explicit_path = { "/tests/", "root", "tests", "/" };
+  const KeyfileTestData keyfile_test_data_empty_path = { "/", "root", "root", "/" };
+  const KeyfileTestData keyfile_test_data_long_path = {
+    "/tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch/",
+    "root",
+    "tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch",
+    "/"
+  };

 /* Meson build sets this */
 #ifdef TEST_LOCALE_PATH
@@ -2967,6 +3131,11 @@ main (int argc, char *argv[])
     }

   g_test_add ("/gsettings/keyfile", Fixture, NULL, setup, test_keyfile, teardown);
+  g_test_add ("/gsettings/keyfile/explicit-path", Fixture, &keyfile_test_data_explicit_path, setup, test_keyfile_no_path, teardown);
+  g_test_add ("/gsettings/keyfile/empty-path", Fixture, &keyfile_test_data_empty_path, setup, test_keyfile_no_path, teardown);
+  g_test_add ("/gsettings/keyfile/long-path", Fixture, &keyfile_test_data_long_path, setup, test_keyfile_no_path, teardown);
+  g_test_add ("/gsettings/keyfile/outside-root-path", Fixture, NULL, setup, test_keyfile_outside_root_path, teardown);
+  g_test_add ("/gsettings/keyfile/no-root-group", Fixture, NULL, setup, test_keyfile_no_root_group, teardown);
   g_test_add_func ("/gsettings/child-schema", test_child_schema);
   g_test_add_func ("/gsettings/strinfo", test_strinfo);
   g_test_add_func ("/gsettings/enums", test_enums);
--
2.30.1


code:
Backport of:

From 317b3b587058a05dca95d56dac26568c5b098d33 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Wed, 24 Feb 2021 17:35:40 +0000
Subject: [PATCH] glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION
 with symlinks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.

Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
 - the fact that `fd == -1` is not an error iff `is_symlink` is true,
 - and that `original_stat` will contain the `lstat()` results for the
   symlink now, rather than the `stat()` results for its target (again,
   iff `is_symlink` is true).

This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.

The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.

Includes a unit test.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2325
---
 gio/glocalfileoutputstream.c |  70 ++++++++++++++++-------
 gio/tests/file.c             | 108 +++++++++++++++++++++++++++++++++++
 2 files changed, 158 insertions(+), 20 deletions(-)

diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
index a3dd62172..553fcbbae 100644
--- a/gio/glocalfileoutputstream.c
+++ b/gio/glocalfileoutputstream.c
@@ -874,16 +874,22 @@ handle_overwrite_open (const char    *filename,
       /* Could be a symlink, or it could be a regular ELOOP error,
        * but then the next open will fail too. */
       is_symlink = TRUE;
-      fd = g_open (filename, open_flags, mode);
+      if (!(flags & G_FILE_CREATE_REPLACE_DESTINATION))
+        fd = g_open (filename, open_flags, mode);
     }
-#else
-  fd = g_open (filename, open_flags, mode);
-  errsv = errno;
+#else  /* if !O_NOFOLLOW */
   /* This is racy, but we do it as soon as possible to minimize the race */
   is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK);
+
+  if (!is_symlink || !(flags & G_FILE_CREATE_REPLACE_DESTINATION))
+    {
+      fd = g_open (filename, open_flags, mode);
+      errsv = errno;
+    }
 #endif

-  if (fd == -1)
+  if (fd == -1 &&
+      (!is_symlink || !(flags & G_FILE_CREATE_REPLACE_DESTINATION)))
     {
       char *display_name = g_filename_display_name (filename);
       g_set_error (error, G_IO_ERROR,
@@ -893,13 +899,25 @@ handle_overwrite_open (const char    *filename,
       g_free (display_name);
       return -1;
     }
-
+
+  if (!is_symlink)
+    {
 #ifdef G_OS_WIN32
-  res = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &original_stat);
+      res = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &original_stat);
 #else
-  res = fstat (fd, &original_stat);
+      res = fstat (fd, &original_stat);
 #endif
-  errsv = errno;
+      errsv = errno;
+    }
+  else
+    {
+#ifdef G_OS_WIN32
+      res = GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (filename, &original_stat);
+#else
+      res = g_lstat (filename, &original_stat);
+#endif
+      errsv = errno;
+    }

   if (res != 0)
     {
@@ -916,16 +934,27 @@ handle_overwrite_open (const char    *filename,
   if (!S_ISREG (original_stat.st_mode))
     {
       if (S_ISDIR (original_stat.st_mode))
-	g_set_error_literal (error,
-                             G_IO_ERROR,
-                             G_IO_ERROR_IS_DIRECTORY,
-                             _("Target file is a directory"));
-      else
-	g_set_error_literal (error,
-                             G_IO_ERROR,
-                             G_IO_ERROR_NOT_REGULAR_FILE,
-                             _("Target file is not a regular file"));
-      goto err_out;
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_IS_DIRECTORY,
+                               _("Target file is a directory"));
+          goto err_out;
+        }
+      else if (!is_symlink ||
+#ifdef S_ISLNK
+               !S_ISLNK (original_stat.st_mode)
+#else
+               FALSE
+#endif
+               )
+        {
+          g_set_error_literal (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_REGULAR_FILE,
+                               _("Target file is not a regular file"));
+          goto err_out;
+        }
     }

   if (etag != NULL)
@@ -1006,7 +1035,8 @@ handle_overwrite_open (const char    *filename,
 	    }
 	}

-      g_close (fd, NULL);
+      if (fd >= 0)
+        g_close (fd, NULL);
       *temp_filename = tmp_filename;
       return tmpfd;
     }
diff --git a/gio/tests/file.c b/gio/tests/file.c
index efb2eaadd..bc55f3af4 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -804,6 +804,113 @@ test_replace_cancel (void)
   g_object_unref (tmpdir);
 }

+static void
+test_replace_symlink (void)
+{
+#ifdef G_OS_UNIX
+  gchar *tmpdir_path = NULL;
+  GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
+  GFileOutputStream *stream = NULL;
+  const gchar *new_contents = "this is a test message which should be written to source and not target";
+  gsize n_written;
+  GFileEnumerator *enumerator = NULL;
+  GFileInfo *info = NULL;
+  gchar *contents = NULL;
+  gsize length = 0;
+  GError *local_error = NULL;
+
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
+  g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
+
+  /* Create a fresh, empty working directory. */
+  tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
+  g_assert_no_error (local_error);
+  tmpdir = g_file_new_for_path (tmpdir_path);
+
+  g_test_message ("Using temporary directory %s", tmpdir_path);
+  g_free (tmpdir_path);
+
+  /* Create symlink `source` which points to `target`. */
+  source_file = g_file_get_child (tmpdir, "source");
+  target_file = g_file_get_child (tmpdir, "target");
+  g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  /* Ensure that `target` doesn’t exist */
+  g_assert_false (g_file_query_exists (target_file, NULL));
+
+  /* Replace the `source` symlink with a regular file using
+   * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
+   * following the symlink */
+  stream = g_file_replace (source_file, NULL, FALSE  /* no backup */,
+                           G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
+                             &n_written, NULL, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_cmpint (n_written, ==, strlen (new_contents));
+
+  g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  g_clear_object (&stream);
+
+  /* At this point, there should still only be one file: `source`. It should
+   * now be a regular file. `target` should not exist. */
+  enumerator = g_file_enumerate_children (tmpdir,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_nonnull (info);
+
+  g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
+  g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
+
+  g_clear_object (&info);
+
+  info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_null (info);
+
+  g_file_enumerator_close (enumerator, NULL, &local_error);
+  g_assert_no_error (local_error);
+  g_clear_object (&enumerator);
+
+  /* Double-check that `target` doesn’t exist */
+  g_assert_false (g_file_query_exists (target_file, NULL));
+
+  /* Check the content of `source`. */
+  g_file_load_contents (source_file,
+                        NULL,
+                        &contents,
+                        &length,
+                        NULL,
+                        &local_error);
+  g_assert_no_error (local_error);
+  g_assert_cmpstr (contents, ==, new_contents);
+  g_assert_cmpuint (length, ==, strlen (new_contents));
+  g_free (contents);
+
+  /* Tidy up. */
+  g_file_delete (source_file, NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  g_file_delete (tmpdir, NULL, &local_error);
+  g_assert_no_error (local_error);
+
+  g_clear_object (&target_file);
+  g_clear_object (&source_file);
+  g_clear_object (&tmpdir);
+#else  /* if !G_OS_UNIX */
+  g_test_skip ("Symlink replacement tests can only be run on Unix")
+#endif
+}
+
 static void
 on_file_deleted (GObject      *object,
 		 GAsyncResult *result,
@@ -1754,6 +1861,7 @@ main (int argc, char *argv[])
   g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
   g_test_add_func ("/file/replace-load", test_replace_load);
   g_test_add_func ("/file/replace-cancel", test_replace_cancel);
+  g_test_add_func ("/file/replace-symlink", test_replace_symlink);
   g_test_add_func ("/file/async-delete", test_async_delete);
 #ifdef G_OS_UNIX
   g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
--
2.30.1


code:
This patch lets GLib's GDesktopAppInfo API watch and notice changes
to the Guix user and system profiles.  That way, the list of available
applications shown by the desktop environment is immediately updated
when the user runs "guix install", "guix remove", or "guix system
reconfigure" (see <https://issues.guix.gnu.org/35594>).

It does so by monitoring /var/guix/profiles (for changes to the system
profile) and /var/guix/profiles/per-user/USER (for changes to the user
profile) and crawling their share/applications sub-directory when
changes happen.

diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index f1e2fdd..095c110 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -148,6 +148,7 @@ typedef struct
   gchar                      *alternatively_watching;
   gboolean                    is_config;
   gboolean                    is_setup;
+  gchar                      *guix_profile_watch_dir;
   GFileMonitor               *monitor;
   GHashTable                 *app_names;
   GHashTable                 *mime_tweaks;
@@ -180,6 +181,7 @@ desktop_file_dir_unref (DesktopFileDir *dir)
     {
       desktop_file_dir_reset (dir);
       g_free (dir->path);
+      g_free (dir->guix_profile_watch_dir);
       g_free (dir);
     }
 }
@@ -204,6 +206,13 @@ desktop_file_dir_get_alternative_dir (DesktopFileDir *dir)
 {
   gchar *parent;

+  /* If DIR is a profile, watch the specified directory--e.g.,
+   * /var/guix/profiles/per-user/$USER/ for the user profile.  Do not watch
+   * ~/.guix-profile or /run/current-system/profile because GFileMonitor does
+   * not pass IN_DONT_FOLLOW and thus cannot notice any change.  */
+  if (dir->guix_profile_watch_dir != NULL)
+    return g_strdup (dir->guix_profile_watch_dir);
+
   /* If the directory itself exists then we need no alternative. */
   if (g_access (dir->path, R_OK | X_OK) == 0)
     return NULL;
@@ -249,11 +258,11 @@ desktop_file_dir_changed (GFileMonitor      *monitor,
    *
    * If this is a notification for a parent directory (because the
    * desktop directory didn't exist) then we shouldn't fire the signal
-   * unless something actually changed.
+   * unless something actually changed or it's in /var/guix/profiles.
    */
   g_mutex_lock (&desktop_file_dir_lock);

-  if (dir->alternatively_watching)
+  if (dir->alternatively_watching && dir->guix_profile_watch_dir == NULL)
     {
       gchar *alternative_dir;

@@ -1555,6 +1564,32 @@ desktop_file_dirs_lock (void)
       for (i = 0; dirs[i]; i++)
         g_ptr_array_add (desktop_file_dirs, desktop_file_dir_new (dirs[i]));

+      {
+        /* Monitor the system and user profile under /var/guix/profiles and
+         * treat modifications to them as if they were modifications to their
+         * /share sub-directory.  */
+        const gchar *user;
+        DesktopFileDir *system_profile_dir, *user_profile_dir;
+
+        system_profile_dir =
+          desktop_file_dir_new ("/var/guix/profiles/system/profile/share");
+        system_profile_dir->guix_profile_watch_dir = g_strdup ("/var/guix/profiles");
+        g_ptr_array_add (desktop_file_dirs, desktop_file_dir_ref (system_profile_dir));
+
+        user = g_get_user_name ();
+        if (user != NULL)
+          {
+            gchar *profile_dir, *user_data_dir;
+
+            profile_dir = g_build_filename ("/var/guix/profiles/per-user", user, NULL);
+            user_data_dir = g_build_filename (profile_dir, "guix-profile", "share", NULL);
+            user_profile_dir = desktop_file_dir_new (user_data_dir);
+            user_profile_dir->guix_profile_watch_dir = profile_dir;
+            g_ptr_array_add (desktop_file_dirs, desktop_file_dir_ref (user_profile_dir));
+            g_free (user_data_dir);
+          }
+      }
+
       /* The list of directories will never change after this, unless
        * g_get_user_config_dir() changes due to %G_TEST_OPTION_ISOLATE_DIRS. */
       desktop_file_dirs_config_dir = user_config_dir;


code:
* On i686 floating-point roundoff errors could sometimes cause this check to
  fail depending on the elapsed microseconds.  Improve rounding by adding a
  fractional bit.

--- glib-2.40.0/glib/tests/timer.c	2014-03-05 08:05:42.000000000 -0600
+++ glib-2.40.0/glib/tests/timer.c	2014-07-10 16:33:12.746862822 -0500
@@ -35,7 +35,7 @@
   elapsed = g_timer_elapsed (timer, &micros);

   g_assert_cmpfloat (elapsed, <, 1.0);
-  g_assert_cmpuint (micros, ==, ((guint64)(elapsed * 1e6)) % 1000000);
+  g_assert_cmpuint (micros, ==, ((guint64)(elapsed * 1e6 + 0.5)) % 1000000);

   g_timer_destroy (timer);
 }


code:
These commits are cherry-picked from the "release/2.27/master" branch.

https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/release/2.27/master

Currently, we have the following (with NEWS and ChangeLog entries omitted).

56170e064e2b21ce204f0817733e92f1730541ea
516fa6080481a1433c173320b1c1432868e1e38a
f36553bf6a4f69070f99badbdab5802b43e6e211
7c6304182b9f422b782ace1cdd3efbde056aec36
78a90c2f74a2012dd3eff302189e47ff6779a757
1e52d8e65a58c49a48549053a1b89c06240e0c6c
55ad82e45c313454de657931898e974a7a036cad

From 56170e064e2b21ce204f0817733e92f1730541ea Mon Sep 17 00:00:00 2001
From: Igor Gnatenko <ignatenko@redhat.com>
Date: Wed, 7 Feb 2018 13:53:10 +0100
Subject: [PATCH] Linux: use reserved name __key in pkey_get [BZ #22797]

_key is not reserved name and we should avoid using that. It seems that
it was simple typo when pkey_* was implemented.

(cherry picked from commit 388ff7bd0d57d7061fdd39a2f26f65687e8058da)

diff --git a/sysdeps/unix/sysv/linux/bits/mman-shared.h b/sysdeps/unix/sysv/linux/bits/mman-shared.h
index 7715e680ca..d15ba95c9d 100644
--- a/sysdeps/unix/sysv/linux/bits/mman-shared.h
+++ b/sysdeps/unix/sysv/linux/bits/mman-shared.h
@@ -61,7 +61,7 @@ int pkey_set (int __key, unsigned int __access_rights) __THROW;

 /* Return the access rights for the current thread for KEY, which must
    have been allocated using pkey_alloc.  */
-int pkey_get (int _key) __THROW;
+int pkey_get (int __key) __THROW;

 /* Free an allocated protection key, which must have been allocated
    using pkey_alloc.  */

From 516fa6080481a1433c173320b1c1432868e1e38a Mon Sep 17 00:00:00 2001
From: "Dmitry V. Levin" <ldv@altlinux.org>
Date: Fri, 29 Dec 2017 23:19:32 +0000
Subject: [PATCH] linux/aarch64: sync sys/ptrace.h with Linux 4.15 [BZ #22433]

Remove compat-specific constants that were never exported by kernel
headers under these names.  Before linux commit v3.7-rc1~16^2~1 they
were exported with COMPAT_ prefix, and since that commit they are not
exported at all.

* sysdeps/unix/sysv/linux/aarch64/sys/ptrace.h (__ptrace_request):
Remove arm-specific PTRACE_GET_THREAD_AREA, PTRACE_GETHBPREGS,
and PTRACE_SETHBPREGS.

(cherry picked from commit 2fd4bbaa1446f1be700e10c526cf585a796c4991)

diff --git a/sysdeps/unix/sysv/linux/aarch64/sys/ptrace.h b/sysdeps/unix/sysv/linux/aarch64/sys/ptrace.h
index 4be45b95ff..444edbb702 100644
--- a/sysdeps/unix/sysv/linux/aarch64/sys/ptrace.h
+++ b/sysdeps/unix/sysv/linux/aarch64/sys/ptrace.h
@@ -78,18 +78,10 @@ enum __ptrace_request
   PTRACE_DETACH = 17,
 #define PT_DETACH PTRACE_DETACH

-  PTRACE_GET_THREAD_AREA = 22,
-
   /* Continue and stop at the next entry to or return from syscall.  */
   PTRACE_SYSCALL = 24,
 #define PT_SYSCALL PTRACE_SYSCALL

-  /* Get all hardware breakpoint registers.  */
-  PTRACE_GETHBPREGS = 29,
-
-  /* Set all hardware breakpoint registers.  */
-  PTRACE_SETHBPREGS = 30,
-
   /* Set ptrace filter options.  */
   PTRACE_SETOPTIONS = 0x4200,
 #define PT_SETOPTIONS PTRACE_SETOPTIONS

From f36553bf6a4f69070f99badbdab5802b43e6e211 Mon Sep 17 00:00:00 2001
From: Mike FABIAN <mfabian@redhat.com>
Date: Mon, 19 Feb 2018 21:59:30 +0100
Subject: [PATCH] =?UTF-8?q?Add=20missing=20=E2=80=9Creorder-end=E2=80=9D?=
 =?UTF-8?q?=20in=20LC=5FCOLLATE=20of=20et=5FEE=20[BZ=20#22517]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

	[BZ #22517]
	* localedata/locales/et_EE (LC_COLLATE): add missing “reorder-end”

(cherry picked from commit 7ec5f9465e732e668d0dc94ac078ba68056d6d0a)

diff --git a/localedata/locales/et_EE b/localedata/locales/et_EE
index 9cb55b568f..bab7493c98 100644
--- a/localedata/locales/et_EE
+++ b/localedata/locales/et_EE
@@ -103,6 +103,8 @@ reorder-after <w>
 <U00FC> <u-diaresis>;<BAS>;<MIN>;IGNORE % ü
 <U00DC> <u-diaresis>;<BAS>;<CAP>;IGNORE % Ü

+reorder-end
+
 END LC_COLLATE

 LC_CTYPE

From 7c6304182b9f422b782ace1cdd3efbde056aec36 Mon Sep 17 00:00:00 2001
From: DJ Delorie <dj@redhat.com>
Date: Thu, 1 Mar 2018 23:20:45 -0500
Subject: [PATCH] [BZ #22342] Fix netgroup cache keys.

Unlike other nscd caches, the netgroup cache contains two types of
records - those for "iterate through a netgroup" (i.e. setnetgrent())
and those for "is this user in this netgroup" (i.e. innetgr()),
i.e. full and partial records.  The timeout code assumes these records
have the same key for the group name, so that the collection of records
that is "this netgroup" can be expired as a unit.

However, the keys are not the same, as the in-netgroup key is generated
by nscd rather than being passed to it from elsewhere, and is generated
without the trailing NUL.  All other keys have the trailing NUL, and as
noted in the linked BZ, debug statements confirm that two keys for the
same netgroup are added to the cache with two different lengths.

The result of this is that as records in the cache expire, the purge
code only cleans out one of the two types of entries, resulting in
stale, possibly incorrect, and possibly inconsistent cache data.

The patch simply includes the existing NUL in the computation for the
key length ('key' points to the char after the NUL, and 'group' to the
first char of the group, so 'key-group' includes the first char to the
NUL, inclusive).

	[BZ #22342]
	* nscd/netgroupcache.c (addinnetgrX): Include trailing NUL in
	key value.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 1c81d55fc4b07b51adf68558ba74ce975153e580)

diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
index b832c9315f..2f187b208c 100644
--- a/nscd/netgroupcache.c
+++ b/nscd/netgroupcache.c
@@ -480,7 +480,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
 {
   const char *group = key;
   key = (char *) rawmemchr (key, '\0') + 1;
-  size_t group_len = key - group - 1;
+  size_t group_len = key - group;
   const char *host = *key++ ? key : NULL;
   if (host != NULL)
     key = (char *) rawmemchr (key, '\0') + 1;

From 78a90c2f74a2012dd3eff302189e47ff6779a757 Mon Sep 17 00:00:00 2001
From: Andreas Schwab <schwab@linux-m68k.org>
Date: Fri, 2 Mar 2018 23:07:14 +0100
Subject: [PATCH] Fix multiple definitions of __nss_*_database (bug 22918)

(cherry picked from commit eaf6753f8aac33a36deb98c1031d1bad7b593d2d)

diff --git a/nscd/gai.c b/nscd/gai.c
index d081747797..576fd0045b 100644
--- a/nscd/gai.c
+++ b/nscd/gai.c
@@ -45,3 +45,6 @@
 #ifdef HAVE_LIBIDN
 # include <libidn/idn-stub.c>
 #endif
+
+/* Some variables normally defined in libc.  */
+service_user *__nss_hosts_database attribute_hidden;
diff --git a/nss/nsswitch.c b/nss/nsswitch.c
index d5e655974f..b0f0c11a3e 100644
--- a/nss/nsswitch.c
+++ b/nss/nsswitch.c
@@ -62,7 +62,7 @@ static service_library *nss_new_service (name_database *database,

 /* Declare external database variables.  */
 #define DEFINE_DATABASE(name)						      \
-  extern service_user *__nss_##name##_database attribute_hidden;	      \
+  service_user *__nss_##name##_database attribute_hidden;		      \
   weak_extern (__nss_##name##_database)
 #include "databases.def"
 #undef DEFINE_DATABASE
diff --git a/nss/nsswitch.h b/nss/nsswitch.h
index eccb535ef5..63573b9ebc 100644
--- a/nss/nsswitch.h
+++ b/nss/nsswitch.h
@@ -226,10 +226,10 @@ libc_hidden_proto (__nss_hostname_digits_dots)
 #define MAX_NR_ADDRS    48

 /* Prototypes for __nss_*_lookup2 functions.  */
-#define DEFINE_DATABASE(arg)				    \
-  service_user *__nss_##arg##_database attribute_hidden;    \
-  int __nss_##arg##_lookup2 (service_user **, const char *, \
-			     const char *, void **);	    \
+#define DEFINE_DATABASE(arg)						      \
+  extern service_user *__nss_##arg##_database attribute_hidden;		      \
+  int __nss_##arg##_lookup2 (service_user **, const char *,		      \
+			     const char *, void **);			      \
   libc_hidden_proto (__nss_##arg##_lookup2)
 #include "databases.def"
 #undef DEFINE_DATABASE
diff --git a/posix/tst-rfc3484-2.c b/posix/tst-rfc3484-2.c
index f509534ca9..8c64ac59ff 100644
--- a/posix/tst-rfc3484-2.c
+++ b/posix/tst-rfc3484-2.c
@@ -58,6 +58,7 @@ _res_hconf_init (void)
 #undef	USE_NSCD
 #include "../sysdeps/posix/getaddrinfo.c"

+service_user *__nss_hosts_database attribute_hidden;

 /* This is the beginning of the real test code.  The above defines
    (among other things) the function rfc3484_sort.  */
diff --git a/posix/tst-rfc3484-3.c b/posix/tst-rfc3484-3.c
index ae44087a10..1c61aaf844 100644
--- a/posix/tst-rfc3484-3.c
+++ b/posix/tst-rfc3484-3.c
@@ -58,6 +58,7 @@ _res_hconf_init (void)
 #undef	USE_NSCD
 #include "../sysdeps/posix/getaddrinfo.c"

+service_user *__nss_hosts_database attribute_hidden;

 /* This is the beginning of the real test code.  The above defines
    (among other things) the function rfc3484_sort.  */
diff --git a/posix/tst-rfc3484.c b/posix/tst-rfc3484.c
index 7f191abbbc..8f45848e44 100644
--- a/posix/tst-rfc3484.c
+++ b/posix/tst-rfc3484.c
@@ -58,6 +58,7 @@ _res_hconf_init (void)
 #undef	USE_NSCD
 #include "../sysdeps/posix/getaddrinfo.c"

+service_user *__nss_hosts_database attribute_hidden;

 /* This is the beginning of the real test code.  The above defines
    (among other things) the function rfc3484_sort.  */

From 1e52d8e65a58c49a48549053a1b89c06240e0c6c Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Fri, 17 Nov 2017 16:04:29 -0200
Subject: [PATCH] i386: Fix i386 sigaction sa_restorer initialization
 (BZ#21269)

This patch fixes the i386 sa_restorer field initialization for sigaction
syscall for kernel with vDSO.  As described in bug report, i386 Linux
(and compat on x86_64) interprets SA_RESTORER clear with nonzero
sa_restorer as a request for stack switching if the SS segment is 'funny'.
This means that anything that tries to mix glibc's signal handling with
segmentation (for instance through modify_ldt syscall) is randomly broken
depending on what values lands in sa_restorer.

The testcase added  is based on Linux test tools/testing/selftests/x86/ldt_gdt.c,
more specifically in do_multicpu_tests function.  The main changes are:

  - C11 atomics instead of plain access.

  - Remove x86_64 support which simplifies the syscall handling and fallbacks.

  - Replicate only the test required to trigger the issue.

Checked on i686-linux-gnu.

	[BZ #21269]
	* sysdeps/unix/sysv/linux/i386/Makefile (tests): Add tst-bz21269.
	* sysdeps/unix/sysv/linux/i386/sigaction.c (SET_SA_RESTORER): Clear
	sa_restorer for vDSO case.
	* sysdeps/unix/sysv/linux/i386/tst-bz21269.c: New file.

(cherry picked from commit 68448be208ee06e76665918b37b0a57e3e00c8b4)

diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
index 4080b8c966..da716e2c1b 100644
--- a/sysdeps/unix/sysv/linux/i386/Makefile
+++ b/sysdeps/unix/sysv/linux/i386/Makefile
@@ -3,6 +3,9 @@ default-abi := 32

 ifeq ($(subdir),misc)
 sysdep_routines += ioperm iopl vm86
+
+tests += tst-bz21269
+$(objpfx)tst-bz21269: $(shared-thread-library)
 endif

 ifeq ($(subdir),elf)
diff --git a/sysdeps/unix/sysv/linux/i386/sigaction.c b/sysdeps/unix/sysv/linux/i386/sigaction.c
index a5eb9e0d3e..177ff60ee6 100644
--- a/sysdeps/unix/sysv/linux/i386/sigaction.c
+++ b/sysdeps/unix/sysv/linux/i386/sigaction.c
@@ -42,7 +42,6 @@ extern void restore_rt (void) asm ("__restore_rt") attribute_hidden;
 #endif
 extern void restore (void) asm ("__restore") attribute_hidden;

-
 /* If ACT is not NULL, change the action for SIG to *ACT.
    If OACT is not NULL, put the old action for SIG in *OACT.  */
 int
@@ -65,6 +64,8 @@ __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact)
 	  kact.sa_restorer = ((act->sa_flags & SA_SIGINFO)
 			      ? &restore_rt : &restore);
 	}
+      else
+	kact.sa_restorer = NULL;
     }

   /* XXX The size argument hopefully will have to be changed to the
diff --git a/sysdeps/unix/sysv/linux/i386/tst-bz21269.c b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
new file mode 100644
index 0000000000..353e36507d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/tst-bz21269.c
@@ -0,0 +1,233 @@
+/* Test for i386 sigaction sa_restorer handling (BZ#21269)
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This is based on Linux test tools/testing/selftests/x86/ldt_gdt.c,
+   more specifically in do_multicpu_tests function.  The main changes
+   are:
+
+   - C11 atomics instead of plain access.
+   - Remove x86_64 support which simplifies the syscall handling
+     and fallbacks.
+   - Replicate only the test required to trigger the issue for the
+     BZ#21269.  */
+
+#include <stdatomic.h>
+
+#include <asm/ldt.h>
+#include <linux/futex.h>
+
+#include <setjmp.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+
+#include <support/xunistd.h>
+#include <support/check.h>
+#include <support/xthread.h>
+
+static int
+xset_thread_area (struct user_desc *u_info)
+{
+  long ret = syscall (SYS_set_thread_area, u_info);
+  TEST_VERIFY_EXIT (ret == 0);
+  return ret;
+}
+
+static void
+xmodify_ldt (int func, const void *ptr, unsigned long bytecount)
+{
+  TEST_VERIFY_EXIT (syscall (SYS_modify_ldt, 1, ptr, bytecount) == 0);
+}
+
+static int
+futex (int *uaddr, int futex_op, int val, void *timeout, int *uaddr2,
+	int val3)
+{
+  return syscall (SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
+}
+
+static void
+xsethandler (int sig, void (*handler)(int, siginfo_t *, void *), int flags)
+{
+  struct sigaction sa = { 0 };
+  sa.sa_sigaction = handler;
+  sa.sa_flags = SA_SIGINFO | flags;
+  TEST_VERIFY_EXIT (sigemptyset (&sa.sa_mask) == 0);
+  TEST_VERIFY_EXIT (sigaction (sig, &sa, 0) == 0);
+}
+
+static jmp_buf jmpbuf;
+
+static void
+sigsegv_handler (int sig, siginfo_t *info, void *ctx_void)
+{
+  siglongjmp (jmpbuf, 1);
+}
+
+/* Points to an array of 1024 ints, each holding its own index.  */
+static const unsigned int *counter_page;
+static struct user_desc *low_user_desc;
+static struct user_desc *low_user_desc_clear; /* Used to delete GDT entry.  */
+static int gdt_entry_num;
+
+static void
+setup_counter_page (void)
+{
+  long page_size = sysconf (_SC_PAGE_SIZE);
+  TEST_VERIFY_EXIT (page_size > 0);
+  unsigned int *page = xmmap (NULL, page_size, PROT_READ | PROT_WRITE,
+			      MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1);
+  for (int i = 0; i < (page_size / sizeof (unsigned int)); i++)
+    page[i] = i;
+  counter_page = page;
+}
+
+static void
+setup_low_user_desc (void)
+{
+  low_user_desc = xmmap (NULL, 2 * sizeof (struct user_desc),
+			 PROT_READ | PROT_WRITE,
+			 MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1);
+
+  low_user_desc->entry_number    = -1;
+  low_user_desc->base_addr       = (unsigned long) &counter_page[1];
+  low_user_desc->limit           = 0xffff;
+  low_user_desc->seg_32bit       = 1;
+  low_user_desc->contents        = 0;
+  low_user_desc->read_exec_only  = 0;
+  low_user_desc->limit_in_pages  = 1;
+  low_user_desc->seg_not_present = 0;
+  low_user_desc->useable         = 0;
+
+  xset_thread_area (low_user_desc);
+
+  low_user_desc_clear = low_user_desc + 1;
+  low_user_desc_clear->entry_number = gdt_entry_num;
+  low_user_desc_clear->read_exec_only = 1;
+  low_user_desc_clear->seg_not_present = 1;
+}
+
+/* Possible values of futex:
+   0: thread is idle.
+   1: thread armed.
+   2: thread should clear LDT entry 0.
+   3: thread should exit.  */
+static atomic_uint ftx;
+
+static void *
+threadproc (void *ctx)
+{
+  while (1)
+    {
+      futex ((int *) &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
+      while (atomic_load (&ftx) != 2)
+	{
+	  if (atomic_load (&ftx) >= 3)
+	    return NULL;
+	}
+
+      /* clear LDT entry 0.  */
+      const struct user_desc desc = { 0 };
+      xmodify_ldt (1, &desc, sizeof (desc));
+
+      /* If ftx == 2, set it to zero,  If ftx == 100, quit.  */
+      if (atomic_fetch_add (&ftx, -2) != 2)
+	return NULL;
+    }
+}
+
+
+/* As described in testcase, for historical reasons x86_32 Linux (and compat
+   on x86_64) interprets SA_RESTORER clear with nonzero sa_restorer as a
+   request for stack switching if the SS segment is 'funny' (this is default
+   scenario for vDSO system).  This means that anything that tries to mix
+   signal handling with segmentation should explicit clear the sa_restorer.
+
+   This testcase check if sigaction in fact does it by changing the local
+   descriptor table (LDT) through the modify_ldt syscall and triggering
+   a synchronous segfault on iret fault by trying to install an invalid
+   segment.  With a correct zeroed sa_restorer it should not trigger an
+   'real' SEGSEGV and allows the siglongjmp in signal handler.  */
+
+static int
+do_test (void)
+{
+  setup_counter_page ();
+  setup_low_user_desc ();
+
+  pthread_t thread;
+  unsigned short orig_ss;
+
+  xsethandler (SIGSEGV, sigsegv_handler, 0);
+  /* 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults.  */
+  xsethandler (SIGILL, sigsegv_handler, 0);
+
+  thread = xpthread_create (0, threadproc, 0);
+
+  asm volatile ("mov %%ss, %0" : "=rm" (orig_ss));
+
+  for (int i = 0; i < 5; i++)
+    {
+      if (sigsetjmp (jmpbuf, 1) != 0)
+	continue;
+
+      /* Make sure the thread is ready after the last test. */
+      while (atomic_load (&ftx) != 0)
+	;
+
+      struct user_desc desc = {
+	.entry_number       = 0,
+	.base_addr          = 0,
+	.limit              = 0xffff,
+	.seg_32bit          = 1,
+	.contents           = 0,
+	.read_exec_only     = 0,
+	.limit_in_pages     = 1,
+	.seg_not_present    = 0,
+	.useable            = 0
+      };
+
+      xmodify_ldt (0x11, &desc, sizeof (desc));
+
+      /* Arm the thread.  */
+      ftx = 1;
+      futex ((int*) &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
+
+      asm volatile ("mov %0, %%ss" : : "r" (0x7));
+
+      /* Fire up thread modify_ldt call.  */
+      atomic_store (&ftx, 2);
+
+      while (atomic_load (&ftx) != 0)
+	;
+
+      /* On success, modify_ldt will segfault us synchronously and we will
+	 escape via siglongjmp.  */
+      support_record_failure ();
+    }
+
+  atomic_store (&ftx, 100);
+  futex ((int*) &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
+
+  xpthread_join (thread);
+
+  return 0;
+}
+
+#include <support/test-driver.c>

From 55ad82e45c313454de657931898e974a7a036cad Mon Sep 17 00:00:00 2001
From: Andrew Senkevich <andrew.n.senkevich@gmail.com>
Date: Fri, 23 Mar 2018 16:19:45 +0100
Subject: [PATCH] Fix i386 memmove issue (bug 22644).

	[BZ #22644]
	* sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S: Fixed
	branch conditions.
	* string/test-memmove.c (do_test2): New testcase.

(cherry picked from commit cd66c0e584c6d692bc8347b5e72723d02b8a8ada)

diff --git a/string/test-memmove.c b/string/test-memmove.c
index edc7a4c3bf..64e3651ba4 100644
--- a/string/test-memmove.c
+++ b/string/test-memmove.c
@@ -24,6 +24,7 @@
 # define TEST_NAME "memmove"
 #endif
 #include "test-string.h"
+#include <support/test-driver.h>

 char *simple_memmove (char *, const char *, size_t);

@@ -245,6 +246,60 @@ do_random_tests (void)
     }
 }

+static void
+do_test2 (void)
+{
+  size_t size = 0x20000000;
+  uint32_t * large_buf;
+
+  large_buf = mmap ((void*) 0x70000000, size, PROT_READ | PROT_WRITE,
+		    MAP_PRIVATE | MAP_ANON, -1, 0);
+
+  if (large_buf == MAP_FAILED)
+    error (EXIT_UNSUPPORTED, errno, "Large mmap failed");
+
+  if ((uintptr_t) large_buf > 0x80000000 - 128
+      || 0x80000000 - (uintptr_t) large_buf > 0x20000000)
+    {
+      error (0, 0, "Large mmap allocated improperly");
+      ret = EXIT_UNSUPPORTED;
+      munmap ((void *) large_buf, size);
+      return;
+    }
+
+  size_t bytes_move = 0x80000000 - (uintptr_t) large_buf;
+  size_t arr_size = bytes_move / sizeof (uint32_t);
+  size_t i;
+
+  FOR_EACH_IMPL (impl, 0)
+    {
+      for (i = 0; i < arr_size; i++)
+        large_buf[i] = (uint32_t) i;
+
+      uint32_t * dst = &large_buf[33];
+
+#ifdef TEST_BCOPY
+      CALL (impl, (char *) large_buf, (char *) dst, bytes_move);
+#else
+      CALL (impl, (char *) dst, (char *) large_buf, bytes_move);
+#endif
+
+      for (i = 0; i < arr_size; i++)
+	{
+	  if (dst[i] != (uint32_t) i)
+	    {
+	      error (0, 0,
+		     "Wrong result in function %s dst \"%p\" src \"%p\" offset \"%zd\"",
+		     impl->name, dst, large_buf, i);
+	      ret = 1;
+	      break;
+	    }
+	}
+    }
+
+  munmap ((void *) large_buf, size);
+}
+
 int
 test_main (void)
 {
@@ -284,6 +339,9 @@ test_main (void)
     }

   do_random_tests ();
+
+  do_test2 ();
+
   return ret;
 }

diff --git a/sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S b/sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S
index 9c3bbe7e17..9aa17de99c 100644
--- a/sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S
+++ b/sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S
@@ -72,7 +72,7 @@ ENTRY (MEMCPY)
 	cmp	%edx, %eax

 # ifdef USE_AS_MEMMOVE
-	jg	L(check_forward)
+	ja	L(check_forward)

 L(mm_len_0_or_more_backward):
 /* Now do checks for lengths. We do [0..16], [16..32], [32..64], [64..128]
@@ -81,7 +81,7 @@ L(mm_len_0_or_more_backward):
 	jbe	L(mm_len_0_16_bytes_backward)

 	cmpl	$32, %ecx
-	jg	L(mm_len_32_or_more_backward)
+	ja	L(mm_len_32_or_more_backward)

 /* Copy [0..32] and return.  */
 	movdqu	(%eax), %xmm0
@@ -92,7 +92,7 @@ L(mm_len_0_or_more_backward):

 L(mm_len_32_or_more_backward):
 	cmpl	$64, %ecx
-	jg	L(mm_len_64_or_more_backward)
+	ja	L(mm_len_64_or_more_backward)

 /* Copy [0..64] and return.  */
 	movdqu	(%eax), %xmm0
@@ -107,7 +107,7 @@ L(mm_len_32_or_more_backward):

 L(mm_len_64_or_more_backward):
 	cmpl	$128, %ecx
-	jg	L(mm_len_128_or_more_backward)
+	ja	L(mm_len_128_or_more_backward)

 /* Copy [0..128] and return.  */
 	movdqu	(%eax), %xmm0
@@ -132,7 +132,7 @@ L(mm_len_128_or_more_backward):
 	add	%ecx, %eax
 	cmp	%edx, %eax
 	movl	SRC(%esp), %eax
-	jle	L(forward)
+	jbe	L(forward)
 	PUSH (%esi)
 	PUSH (%edi)
 	PUSH (%ebx)
@@ -269,7 +269,7 @@ L(check_forward):
 	add	%edx, %ecx
 	cmp	%eax, %ecx
 	movl	LEN(%esp), %ecx
-	jle	L(forward)
+	jbe	L(forward)

 /* Now do checks for lengths. We do [0..16], [0..32], [0..64], [0..128]
 	separately.  */


code:
This patch is taken from debian's glibc package (generate-supported.mk).
It install the localedata/SUPPORTED file of the glibc. This file lists
all the supported locales of the glibc.

diff --git a/localedata/Makefile.old b/localedata/Makefile
index 34877c0..a61d9b9 100644
--- a/localedata/Makefile.old
+++ b/localedata/Makefile
@@ -91,7 +91,8 @@ endif
 # Files to install.
 install-others := $(addprefix $(inst_i18ndir)/, \
 			      $(addsuffix .gz, $(charmaps)) \
-			      $(locales))
+			      $(locales)) \
+                   $(inst_i18ndir)/SUPPORTED

 tests: $(objdir)/iconvdata/gconv-modules

@@ -195,6 +196,14 @@ endif

 include SUPPORTED

+$(inst_i18ndir)/SUPPORTED: SUPPORTED $(+force)
+	for locale in $(SUPPORTED-LOCALES); do \
+		[ $$locale = true ] && continue; \
+		echo $$locale | sed 's,/, ,' >> LOCALES; \
+	done
+	$(make-target-directory)
+	$(INSTALL_DATA) LOCALES $@
+
 INSTALL-SUPPORTED-LOCALES=$(addprefix install-, $(SUPPORTED-LOCALES))

 # Sometimes the whole collection of locale files should be installed.


code:
This file contains fixes from the "release/2.28/master" branch:
https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/release/2.28/master

Currently we have these commits (sans tests and ChangeLog updates):
7f11842e7483da7aa9fa3031be122021978ef600
726e1554ce4db5e35af41cb0110c54c5e1232054
4b25485f03158959cff45379eecc1d73c7dcdd11
d05b05d1570ba3ae354a2f5a3cfeefb373b09979
bfcfa22589f2b4277a65e60c6b736b6bbfbd87d0
2f498f3d140ab5152bd784df2be7af7d9c5e63ed

diff --git a/htl/Versions b/htl/Versions
index 6a63a1b8a1..c5a616da10 100644
--- a/htl/Versions
+++ b/htl/Versions
@@ -150,6 +150,8 @@ libpthread {
     __cthread_keycreate;
     __cthread_getspecific;
     __cthread_setspecific;
+    __pthread_getspecific;
+    __pthread_setspecific;
     __pthread_getattr_np;
     __pthread_attr_getstack;
   }

diff --git a/sysdeps/htl/pt-getspecific.c b/sysdeps/htl/pt-getspecific.c
index a0227a67f6..64ddf9551a 100644
--- a/sysdeps/htl/pt-getspecific.c
+++ b/sysdeps/htl/pt-getspecific.c
@@ -36,3 +36,4 @@ __pthread_getspecific (pthread_key_t key)
   return self->thread_specifics[key];
 }
 strong_alias (__pthread_getspecific, pthread_getspecific);
+hidden_def (__pthread_getspecific)
diff --git a/sysdeps/htl/pt-setspecific.c b/sysdeps/htl/pt-setspecific.c
index a46a12f157..02aff417ef 100644
--- a/sysdeps/htl/pt-setspecific.c
+++ b/sysdeps/htl/pt-setspecific.c
@@ -48,3 +48,4 @@ __pthread_setspecific (pthread_key_t key, const void *value)
   return 0;
 }
 strong_alias (__pthread_setspecific, pthread_setspecific);
+hidden_def (__pthread_setspecific)
diff --git a/sysdeps/htl/pthreadP.h b/sysdeps/htl/pthreadP.h
index 132ac1718e..71c2fcd9c6 100644
--- a/sysdeps/htl/pthreadP.h
+++ b/sysdeps/htl/pthreadP.h
@@ -68,6 +68,8 @@ struct __pthread_cancelation_handler **___pthread_get_cleanup_stack (void) attri

 #if IS_IN (libpthread)
 hidden_proto (__pthread_key_create)
+hidden_proto (__pthread_getspecific)
+hidden_proto (__pthread_setspecific)
 hidden_proto (_pthread_mutex_init)
 #endif

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 3bde0cf4f0..bc140b5a7f 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -33,41 +33,80 @@ strong_alias (__getdents64, __getdents)
 # include <shlib-compat.h>

 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-# include <olddirent.h>
+#  include <olddirent.h>
+#  include <unistd.h>

-/* kernel definition of as of 3.2.  */
-struct compat_linux_dirent
+static ssize_t
+handle_overflow (int fd, __off64_t offset, ssize_t count)
 {
-  /* Both d_ino and d_off are compat_ulong_t which are defined in all
-     architectures as 'u32'.  */
-  uint32_t        d_ino;
-  uint32_t        d_off;
-  unsigned short  d_reclen;
-  char            d_name[1];
-};
+  /* If this is the first entry in the buffer, we can report the
+     error.  */
+  if (count == 0)
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  /* Otherwise, seek to the overflowing entry, so that the next call
+     will report the error, and return the data read so far..  */
+  if (__lseek64 (fd, offset, SEEK_SET) != 0)
+    return -1;
+  return count;
+}

 ssize_t
 __old_getdents64 (int fd, char *buf, size_t nbytes)
 {
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents, fd, buf, nbytes);
+  /* We do not move the individual directory entries.  This is only
+     possible if the target type (struct __old_dirent64) is smaller
+     than the source type.  */
+  _Static_assert (offsetof (struct __old_dirent64, d_name)
+		  <= offsetof (struct dirent64, d_name),
+		  "__old_dirent64 is larger than dirent64");
+  _Static_assert (__alignof__ (struct __old_dirent64)
+		  <= __alignof__ (struct dirent64),
+		  "alignment of __old_dirent64 is larger than dirent64");

-  /* The kernel added the d_type value after the name.  Change this now.  */
-  if (retval != -1)
+  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
+  if (retval > 0)
     {
-      union
-      {
-	struct compat_linux_dirent k;
-	struct dirent u;
-      } *kbuf = (void *) buf;
-
-      while ((char *) kbuf < buf + retval)
+      char *p = buf;
+      char *end = buf + retval;
+      while (p < end)
 	{
-	  char d_type = *((char *) kbuf + kbuf->k.d_reclen - 1);
-	  memmove (kbuf->u.d_name, kbuf->k.d_name,
-		   strlen (kbuf->k.d_name) + 1);
-	  kbuf->u.d_type = d_type;
+	  struct dirent64 *source = (struct dirent64 *) p;
+
+	  /* Copy out the fixed-size data.  */
+	  __ino_t ino = source->d_ino;
+	  __off64_t offset = source->d_off;
+	  unsigned int reclen = source->d_reclen;
+	  unsigned char type = source->d_type;
+
+	  /* Check for ino_t overflow.  */
+	  if (__glibc_unlikely (ino != source->d_ino))
+	    return handle_overflow (fd, offset, p - buf);
+
+	  /* Convert to the target layout.  Use a separate struct and
+	     memcpy to side-step aliasing issues.  */
+	  struct __old_dirent64 result;
+	  result.d_ino = ino;
+	  result.d_off = offset;
+	  result.d_reclen = reclen;
+	  result.d_type = type;
+
+	  /* Write the fixed-sized part of the result to the
+	     buffer.  */
+	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
+	  memcpy (p, &result, result_name_offset);
+
+	  /* Adjust the position of the name if necessary.  Copy
+	     everything until the end of the record, including the
+	     terminating NUL byte.  */
+	  if (result_name_offset != offsetof (struct dirent64, d_name))
+	    memmove (p + result_name_offset, source->d_name,
+		     reclen - offsetof (struct dirent64, d_name));

-	  kbuf = (void *) ((char *) kbuf + kbuf->k.d_reclen);
+	  p += reclen;
 	}
      }
   return retval;

diff --git a/misc/error.c b/misc/error.c
index b4e8b6c938..03378e2f2a 100644
--- a/misc/error.c
+++ b/misc/error.c
@@ -319,6 +319,7 @@ error (int status, int errnum, const char *message, ...)

   va_start (args, message);
   error_tail (status, errnum, message, args);
+  va_end (args);

 #ifdef _LIBC
   _IO_funlockfile (stderr);
@@ -390,6 +391,7 @@ error_at_line (int status, int errnum, const char *file_name,

   va_start (args, message);
   error_tail (status, errnum, message, args);
+  va_end (args);

 #ifdef _LIBC
   _IO_funlockfile (stderr);

diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index 265a02434d..7293b795b6 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -190,7 +190,10 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	  if (!arg1)
 	    error (0, 0, _("Must specify user name for server-user option"));
 	  else
-	    server_user = xstrdup (arg1);
+	    {
+	      free ((char *) server_user);
+	      server_user = xstrdup (arg1);
+	    }
 	}
       else if (strcmp (entry, "stat-user") == 0)
 	{
@@ -198,6 +201,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	    error (0, 0, _("Must specify user name for stat-user option"));
 	  else
 	    {
+	      free ((char *) stat_user);
 	      stat_user = xstrdup (arg1);

 	      struct passwd *pw = getpwnam (stat_user);

diff --git a/nss/nss_files/files-alias.c b/nss/nss_files/files-alias.c
index cfd34b66b9..35b0bfc5d2 100644
--- a/nss/nss_files/files-alias.c
+++ b/nss/nss_files/files-alias.c
@@ -221,6 +221,13 @@ get_next_alias (FILE *stream, const char *match, struct aliasent *result,
 			{
 			  while (! feof_unlocked (listfile))
 			    {
+			      if (room_left < 2)
+				{
+				  free (old_line);
+				  fclose (listfile);
+				  goto no_more_room;
+				}
+
 			      first_unused[room_left - 1] = '\xff';
 			      line = fgets_unlocked (first_unused, room_left,
 						     listfile);
@@ -229,6 +236,7 @@ get_next_alias (FILE *stream, const char *match, struct aliasent *result,
 			      if (first_unused[room_left - 1] != '\xff')
 				{
 				  free (old_line);
+				  fclose (listfile);
 				  goto no_more_room;
 				}

@@ -256,6 +264,7 @@ get_next_alias (FILE *stream, const char *match, struct aliasent *result,
 						       + __alignof__ (char *)))
 					{
 					  free (old_line);
+					  fclose (listfile);
 					  goto no_more_room;
 					}
 				      room_left -= ((first_unused - cp)