~lle-bout/guix

6477daa338fbf1c9edacfc3690aca77cacfe0008 — Raghav Gururajan 7 months ago c2366b9
gnu: glib: Ungraft and make some cosmetic changes.

* gnu/packages/patches/glib-CVE-2021-27218.patch
gnu/packages/patches/glib-CVE-2021-27219-01.patch
gnu/packages/patches/glib-CVE-2021-27219-02.patch
gnu/packages/patches/glib-CVE-2021-27219-03.patch
gnu/packages/patches/glib-CVE-2021-27219-04.patch
gnu/packages/patches/glib-CVE-2021-27219-05.patch
gnu/packages/patches/glib-CVE-2021-27219-06.patch
gnu/packages/patches/glib-CVE-2021-27219-07.patch
gnu/packages/patches/glib-CVE-2021-27219-08.patch
gnu/packages/patches/glib-CVE-2021-27219-09.patch
gnu/packages/patches/glib-CVE-2021-27219-10.patch
gnu/packages/patches/glib-CVE-2021-27219-11.patch
gnu/packages/patches/glib-CVE-2021-27219-12.patch
gnu/packages/patches/glib-CVE-2021-27219-13.patch
gnu/packages/patches/glib-CVE-2021-27219-14.patch
gnu/packages/patches/glib-CVE-2021-27219-15.patch
gnu/packages/patches/glib-CVE-2021-27219-16.patch
gnu/packages/patches/glib-CVE-2021-27219-17.patch
gnu/packages/patches/glib-CVE-2021-27219-18.patch
gnu/packages/patches/glib-CVE-2021-28153.patch: Remove patches.
* gnu/local.mk (dist_patch_DATA): Unregister them.
* gnu/packages/glib.scm (glib): Make some cosmetic changes.
[replacement]: Remove.
(glib/fixed): Remove.

Signed-off-by: Léo Le Bouter <lle-bout@zaclys.net>
22 files changed, 202 insertions(+), 2632 deletions(-)

M gnu/local.mk
M gnu/packages/glib.scm
D gnu/packages/patches/glib-CVE-2021-27218.patch
D gnu/packages/patches/glib-CVE-2021-27219-01.patch
D gnu/packages/patches/glib-CVE-2021-27219-02.patch
D gnu/packages/patches/glib-CVE-2021-27219-03.patch
D gnu/packages/patches/glib-CVE-2021-27219-04.patch
D gnu/packages/patches/glib-CVE-2021-27219-05.patch
D gnu/packages/patches/glib-CVE-2021-27219-06.patch
D gnu/packages/patches/glib-CVE-2021-27219-07.patch
D gnu/packages/patches/glib-CVE-2021-27219-08.patch
D gnu/packages/patches/glib-CVE-2021-27219-09.patch
D gnu/packages/patches/glib-CVE-2021-27219-10.patch
D gnu/packages/patches/glib-CVE-2021-27219-11.patch
D gnu/packages/patches/glib-CVE-2021-27219-12.patch
D gnu/packages/patches/glib-CVE-2021-27219-13.patch
D gnu/packages/patches/glib-CVE-2021-27219-14.patch
D gnu/packages/patches/glib-CVE-2021-27219-15.patch
D gnu/packages/patches/glib-CVE-2021-27219-16.patch
D gnu/packages/patches/glib-CVE-2021-27219-17.patch
D gnu/packages/patches/glib-CVE-2021-27219-18.patch
D gnu/packages/patches/glib-CVE-2021-28153.patch
M gnu/local.mk => gnu/local.mk +0 -20
@@ 1082,26 1082,6 @@ dist_patch_DATA =						\
  %D%/packages/patches/ghostscript-no-header-creationdate.patch \
  %D%/packages/patches/glib-appinfo-watch.patch			\
  %D%/packages/patches/glib-tests-timer.patch			\
  %D%/packages/patches/glib-CVE-2021-27218.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-01.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-02.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-03.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-04.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-05.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-06.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-07.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-08.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-09.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-10.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-11.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-12.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-13.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-14.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-15.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-16.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-17.patch		\
  %D%/packages/patches/glib-CVE-2021-27219-18.patch		\
  %D%/packages/patches/glib-CVE-2021-28153.patch		\
  %D%/packages/patches/glibc-CVE-2018-11236.patch		\
  %D%/packages/patches/glibc-CVE-2018-11237.patch		\
  %D%/packages/patches/glibc-CVE-2019-7309.patch		\

M gnu/packages/glib.scm => gnu/packages/glib.scm +202 -252
@@ 170,258 170,208 @@ shared NFS home directories.")

(define glib
  (package
   (name "glib")
   (version "2.62.6")
   (replacement glib/fixed)
   (source (origin
            (method url-fetch)
            (uri (string-append "mirror://gnome/sources/"
                                name "/" (string-take version 4) "/"
                                name "-" version ".tar.xz"))
            (sha256
             (base32
              "174bsmbmcvaw69ff9g60q5sx0fn23rkhqcwqz17h5s7sprps4kqh"))
            (patches (search-patches "glib-appinfo-watch.patch"
                                     "glib-tests-timer.patch"))
            (modules '((guix build utils)))
            (snippet
             '(begin
                (substitute* "tests/spawn-test.c"
                  (("/bin/sh") "sh"))
                #t))))
   (build-system meson-build-system)
   (outputs '("out"           ; everything
              "bin"           ; glib-mkenums, gtester, etc.; depends on Python
              "debug"))
   (propagated-inputs
    `(("pcre" ,pcre)  ; in the Requires.private field of glib-2.0.pc
      ("libffi" ,libffi) ; in the Requires.private field of gobject-2.0.pc
      ;; These are in the Requires.private field of gio-2.0.pc
      ("util-linux" ,util-linux "lib")  ;for libmount
      ("zlib" ,zlib)))
   (native-inputs
    `(("gettext" ,gettext-minimal)
      ("m4" ,m4) ; for installing m4 macros
      ("dbus" ,dbus)                              ; for GDBus tests
      ("pkg-config" ,pkg-config)
      ("python" ,python-minimal-wrapper)
      ("perl" ,perl)                              ; needed by GIO tests
      ("tzdata" ,tzdata-for-tests)))                  ; for tests/gdatetime.c
   (arguments
    `(#:disallowed-references (,tzdata-for-tests)
      #:configure-flags '("-Dselinux=disabled")
      #:phases
      (modify-phases %standard-phases
        (add-after 'unpack 'patch-dbus-launch-path
          (lambda* (#:key inputs #:allow-other-keys)
            (let ((dbus (assoc-ref inputs "dbus")))
              (substitute* "gio/gdbusaddress.c"
                (("command_line = g_strdup_printf \\(\"dbus-launch")
                 (string-append "command_line = g_strdup_printf (\""
                                dbus "/bin/dbus-launch")))
              #t)))
        (add-after 'unpack 'patch-gio-launch-desktop
          (lambda* (#:key outputs #:allow-other-keys)
            (let ((out (assoc-ref outputs "out")))
              ;; See also <https://gitlab.gnome.org/GNOME/glib/issues/1633>
              ;; for another future fix.
              (substitute* "gio/gdesktopappinfo.c"
               (("gio-launch-desktop")
                (string-append out "/libexec/gio-launch-desktop")))
              #t)))
        ;; TODO: Remove the conditional in the next core-updates cycle.
        ;; Needed to build glib on slower ARM nodes.
        ,@(if (string-prefix? "arm" (%current-system))
              `((add-after 'unpack 'increase-test-timeout
                  (lambda _
                    (substitute* "meson.build"
                      (("test_timeout = 60")
                       "test_timeout = 120")
                      (("test_timeout_slow = 120")
                       "test_timeout_slow = 180")))))
              '())
        (add-before 'build 'pre-build
          (lambda* (#:key inputs outputs #:allow-other-keys)
            ;; For tests/gdatetime.c.
            (setenv "TZDIR"
                    (string-append (assoc-ref inputs "tzdata")
                                   "/share/zoneinfo"))

            ;; Some tests want write access there.
            (setenv "HOME" (getcwd))
            (setenv "XDG_CACHE_HOME" (getcwd))
            #t))
        (add-after 'unpack 'disable-failing-tests
          (lambda _
            (let ((disable
                   (lambda (test-file test-paths)
                     (define pattern+procs
                       (map (lambda (test-path)
                              (cons
                               ;; XXX: only works for single line statements.
                               (format #f "g_test_add_func.*\"~a\".*" test-path)
                               (const "")))
                            test-paths))
                     (substitute test-file pattern+procs)))
                  (failing-tests
                   '(("glib/tests/thread.c"
                      (;; prlimit(2) returns ENOSYS on Linux 2.6.32-5-xen-amd64
                       ;; as found on hydra.gnu.org, and strace(1) doesn't
                       ;; recognize it.
                       "/thread/thread4"))

                     ;; This tries to find programs in FHS directories.
                     ("glib/tests/utils.c"
                      ("/utils/find-program"))

                     ;; This fails because "glib/tests/echo-script" cannot be
                     ;; found.
                     ("glib/tests/spawn-singlethread.c"
                      ("/gthread/spawn-script"))

                     ("glib/tests/timer.c"
                      (;; fails if compiler optimizations are enabled, which they
                       ;; are by default.
                       "/timer/stop"))

                     ("gio/tests/gapplication.c"
                      (;; XXX: proven to be unreliable.  See:
                       ;;  <https://bugs.debian.org/756273>
                       ;;  <http://bugs.gnu.org/18445>
                       "/gapplication/quit"

                       ;; XXX: fails randomly for unknown reason. See:
                       ;;  <https://lists.gnu.org/archive/html/guix-devel/2016-04/msg00215.html>
                       "/gapplication/local-actions"))

                     ("gio/tests/contenttype.c"
                      (;; XXX: requires shared-mime-info.
                       "/contenttype/guess"
                       "/contenttype/guess_svg_from_data"
                       "/contenttype/subtype"
                       "/contenttype/list"
                       "/contenttype/icon"
                       "/contenttype/symbolic-icon"
                       "/contenttype/tree"))

                     ("gio/tests/appinfo.c"
                      (;; XXX: requires update-desktop-database.
                       "/appinfo/associations"))

                     ("gio/tests/desktop-app-info.c"
                      (;; XXX: requires update-desktop-database.
                       "/desktop-app-info/delete"
                       "/desktop-app-info/default"
                       "/desktop-app-info/fallback"
                       "/desktop-app-info/lastused"
                       "/desktop-app-info/search"))

                     ("gio/tests/gdbus-peer.c"
                      (;; Requires /etc/machine-id.
                       "/gdbus/codegen-peer-to-peer"))

                     ("gio/tests/gdbus-address-get-session.c"
                      (;; Requires /etc/machine-id.
                       "/gdbus/x11-autolaunch"))

                     ("gio/tests/gsocketclient-slow.c"
                      (;; These tests tries to resolve "localhost", and fails.
                       "/socket-client/happy-eyeballs/slow"
                       "/socket-client/happy-eyeballs/cancellation/delayed"))

                     )))
              (for-each (lambda (x) (apply disable x)) failing-tests)
              #t)))
        (replace 'check
          (lambda* (#:key tests? #:allow-other-keys)
            (if tests?
                (begin
                  (setenv "MESON_TESTTHREADS"
                          (number->string (parallel-job-count)))
                  ;; Do not run tests marked as "flaky".
                  (invoke "meson" "test" "--no-suite" "flaky"))
                #t)))
        ;; TODO: meson does not permit the bindir to be outside of prefix.
        ;; See https://github.com/mesonbuild/meson/issues/2561
        ;; We can remove this once meson is patched.
        (add-after 'install 'move-executables
          (lambda* (#:key outputs #:allow-other-keys)
            (let ((out (assoc-ref outputs "out"))
                  (bin (assoc-ref outputs "bin")))
              (mkdir-p bin)
              (rename-file (string-append out "/bin")
                           (string-append bin "/bin"))
              ;; This one is an implementation detail of glib.
              ;; It is wrong that that's in "/bin" in the first place,
              ;; but that's what upstream is doing right now.
              ;; See <https://gitlab.gnome.org/GNOME/glib/issues/1633>.
              (mkdir (string-append out "/libexec"))
              (rename-file (string-append bin "/bin/gio-launch-desktop")
                           (string-append out "/libexec/gio-launch-desktop"))
              ;; Do not refer to "bindir", which points to "${prefix}/bin".
              ;; We don't patch "bindir" to point to "$bin/bin", because that
              ;; would create a reference cycle between the "out" and "bin"
              ;; outputs.
              (substitute* (list (string-append out "/lib/pkgconfig/gio-2.0.pc")
                                 (string-append out "/lib/pkgconfig/glib-2.0.pc"))
                (("bindir=\\$\\{prefix\\}/bin") "")
                (("=\\$\\{bindir\\}/") "="))
              #t))))))
      ;; TODO: see above for explanation.
      ;; #:configure-flags (list (string-append "--bindir="
      ;;                                        (assoc-ref %outputs "bin")
      ;;                                        "/bin"))

   (native-search-paths
    ;; This variable is not really "owned" by GLib, but several related
    ;; packages refer to it: gobject-introspection's tools use it as a search
    ;; path for .gir files, and it's also a search path for schemas produced
    ;; by 'glib-compile-schemas'.
    (list (search-path-specification
           (variable "XDG_DATA_DIRS")
           (files '("share")))
          ;; To load extra gio modules from glib-networking, etc.
          (search-path-specification
           (variable "GIO_EXTRA_MODULES")
           (files '("lib/gio/modules")))))
   (search-paths native-search-paths)
   (properties '((hidden? . #t)))

   (synopsis "Thread-safe general utility library; basis of GTK+ and GNOME")
   (description
    "GLib provides data structure handling for C, portability wrappers,
and interfaces for such runtime functionality as an event loop, threads,
dynamic loading, and an object system.")
   (home-page "https://developer.gnome.org/glib/")
   (license license:lgpl2.1+)))

(define glib/fixed
  (package
    (inherit glib)
    (source (origin
              (inherit (package-source glib))
              (patches
               (append (search-patches "glib-CVE-2021-27218.patch"
                                       "glib-CVE-2021-27219-01.patch"
                                       "glib-CVE-2021-27219-02.patch"
                                       "glib-CVE-2021-27219-03.patch"
                                       "glib-CVE-2021-27219-04.patch"
                                       "glib-CVE-2021-27219-05.patch"
                                       "glib-CVE-2021-27219-06.patch"
                                       "glib-CVE-2021-27219-07.patch"
                                       "glib-CVE-2021-27219-08.patch"
                                       "glib-CVE-2021-27219-09.patch"
                                       "glib-CVE-2021-27219-10.patch"
                                       "glib-CVE-2021-27219-11.patch"
                                       "glib-CVE-2021-27219-12.patch"
                                       "glib-CVE-2021-27219-13.patch"
                                       "glib-CVE-2021-27219-14.patch"
                                       "glib-CVE-2021-27219-15.patch"
                                       "glib-CVE-2021-27219-16.patch"
                                       "glib-CVE-2021-27219-17.patch"
                                       "glib-CVE-2021-27219-18.patch"
                                       "glib-CVE-2021-28153.patch")
                       (origin-patches (package-source glib))))))))
    (name "glib")
    (version "2.62.6")
    (source
     (origin
       (method url-fetch)
       (uri
        (string-append "mirror://gnome/sources/"
                       name "/" (string-take version 4) "/"
                       name "-" version ".tar.xz"))
       (sha256
        (base32 "174bsmbmcvaw69ff9g60q5sx0fn23rkhqcwqz17h5s7sprps4kqh"))
       (patches
        (search-patches "glib-tests-timer.patch" "glib-appinfo-watch.patch"))
       (modules '((guix build utils)))
       (snippet
        '(begin
           (substitute* "tests/spawn-test.c"
             (("/bin/sh") "sh"))
           #t))))
    (properties '((hidden? . #t)))
    (build-system meson-build-system)
    (outputs '("out"   ; everything
               "bin")) ; glib-mkenums, gtester, etc.; depends on Python
    (arguments
     `(#:disallowed-references (,tzdata-for-tests)
       #:phases
       (modify-phases %standard-phases
         ;; TODO: Remove the conditional in the next core-updates cycle.
         ;; Needed to build glib on slower ARM nodes.
         ,@(if (string-prefix? "arm" (%current-system))
               `((add-after 'unpack 'increase-test-timeout
                   (lambda _
                     (substitute* "meson.build"
                       (("test_timeout = 60")
                        "test_timeout = 90")
                       (("test_timeout_slow = 120")
                        "test_timeout_slow = 180")))))
               '())
         (add-after 'unpack 'patch-dbus-launch-path
           (lambda* (#:key inputs #:allow-other-keys)
             (let ((dbus (assoc-ref inputs "dbus")))
               (substitute* "gio/gdbusaddress.c"
                 (("command_line = g_strdup_printf \\(\"dbus-launch")
                  (string-append "command_line = g_strdup_printf (\""
                                 dbus "/bin/dbus-launch")))
               #t)))
         (add-after 'unpack 'patch-gio-launch-desktop
           (lambda* (#:key outputs #:allow-other-keys)
             (let ((out (assoc-ref outputs "out")))
               ;; See also <https://gitlab.gnome.org/GNOME/glib/issues/1633>
               ;; for another future fix.
               (substitute* "gio/gdesktopappinfo.c"
                 (("gio-launch-desktop")
                  (string-append out "/libexec/gio-launch-desktop")))
               #t)))
         (add-before 'build 'pre-build
           (lambda* (#:key inputs outputs #:allow-other-keys)
             ;; For tests/gdatetime.c.
             (setenv "TZDIR"
                     (string-append (assoc-ref inputs "tzdata")
                                    "/share/zoneinfo"))
             ;; Some tests want write access there.
             (setenv "HOME" (getcwd))
             (setenv "XDG_CACHE_HOME" (getcwd))
             #t))
         (add-after 'unpack 'disable-failing-tests
           (lambda _
             (let ((disable
                    (lambda (test-file test-paths)
                      (define pattern+procs
                        (map (lambda (test-path)
                               (cons
                                ;; XXX: only works for single line statements.
                                (format #f "g_test_add_func.*\"~a\".*" test-path)
                                (const "")))
                             test-paths))
                      (substitute test-file pattern+procs)))
                   (failing-tests
                    '(("glib/tests/thread.c"
                       ( ;; prlimit(2) returns ENOSYS on Linux 2.6.32-5-xen-amd64
                        ;; as found on hydra.gnu.org, and strace(1) doesn't
                        ;; recognize it.
                        "/thread/thread4"))
                      ;; This tries to find programs in FHS directories.
                      ("glib/tests/utils.c"
                       ("/utils/find-program"))
                      ;; This fails because "glib/tests/echo-script" cannot be
                      ;; found.
                      ("glib/tests/spawn-singlethread.c"
                       ("/gthread/spawn-script"))
                      ("glib/tests/timer.c"
                       ( ;; fails if compiler optimizations are enabled, which they
                        ;; are by default.
                        "/timer/stop"))
                      ("gio/tests/gapplication.c"
                       ( ;; XXX: proven to be unreliable.  See:
                        ;;  <https://bugs.debian.org/756273>
                        ;;  <http://bugs.gnu.org/18445>
                        "/gapplication/quit"
                        ;; XXX: fails randomly for unknown reason. See:
                        ;;  <https://lists.gnu.org/archive/html/guix-devel/2016-04/msg00215.html>
                        "/gapplication/local-actions"))
                      ("gio/tests/contenttype.c"
                       ( ;; XXX: requires shared-mime-info.
                        "/contenttype/guess"
                        "/contenttype/guess_svg_from_data"
                        "/contenttype/subtype"
                        "/contenttype/list"
                        "/contenttype/icon"
                        "/contenttype/symbolic-icon"
                        "/contenttype/tree"))
                      ("gio/tests/appinfo.c"
                       ( ;; XXX: requires update-desktop-database.
                        "/appinfo/associations"))
                      ("gio/tests/desktop-app-info.c"
                       ( ;; XXX: requires update-desktop-database.
                        "/desktop-app-info/delete"
                        "/desktop-app-info/default"
                        "/desktop-app-info/fallback"
                        "/desktop-app-info/lastused"
                        "/desktop-app-info/search"))
                      ("gio/tests/gdbus-peer.c"
                       ( ;; Requires /etc/machine-id.
                        "/gdbus/codegen-peer-to-peer"))
                      ("gio/tests/gdbus-address-get-session.c"
                       ( ;; Requires /etc/machine-id.
                        "/gdbus/x11-autolaunch"))
                      ("gio/tests/gsocketclient-slow.c"
                       ( ;; These tests tries to resolve "localhost", and fails.
                        "/socket-client/happy-eyeballs/slow"
                        "/socket-client/happy-eyeballs/cancellation/delayed")))))
               (for-each (lambda (x) (apply disable x)) failing-tests)
               #t)))
         (replace 'check
           (lambda _
             (setenv "MESON_TESTTHREADS"
                     (number->string (parallel-job-count)))
             ;; Do not run tests marked as "flaky".
             (invoke "meson" "test" "--no-suite" "flaky")))
         ;; TODO: meson does not permit the bindir to be outside of prefix.
         ;; See https://github.com/mesonbuild/meson/issues/2561
         ;; We can remove this once meson is patched.
         (add-after 'install 'move-executables
           (lambda* (#:key outputs #:allow-other-keys)
             (let ((out (assoc-ref outputs "out"))
                   (bin (assoc-ref outputs "bin")))
               (mkdir-p bin)
               (rename-file (string-append out "/bin")
                            (string-append bin "/bin"))
               ;; This one is an implementation detail of glib.
               ;; It is wrong that that's in "/bin" in the first place,
               ;; but that's what upstream is doing right now.
               ;; See <https://gitlab.gnome.org/GNOME/glib/issues/1633>.
               (mkdir (string-append out "/libexec"))
               (rename-file (string-append bin "/bin/gio-launch-desktop")
                            (string-append out "/libexec/gio-launch-desktop"))
               ;; Do not refer to "bindir", which points to "${prefix}/bin".
               ;; We don't patch "bindir" to point to "$bin/bin", because that
               ;; would create a reference cycle between the "out" and "bin"
               ;; outputs.
               (substitute* (list (string-append out "/lib/pkgconfig/gio-2.0.pc")
                                  (string-append out "/lib/pkgconfig/glib-2.0.pc"))
                 (("bindir=\\$\\{prefix\\}/bin") "")
                 (("=\\$\\{bindir\\}/") "="))
               #t))))))
    ;; TODO: see above for explanation.
    ;; #:configure-flags (list (string-append "--bindir="
    ;;                                        (assoc-ref %outputs "bin")
    ;;                                        "/bin"))
    (native-inputs
     `(("dbus" ,dbus)                   ; for GDBus tests
       ("gettext" ,gettext-minimal)
       ("m4" ,m4)                       ; for installing m4 macros
       ("perl" ,perl)                   ; needed by GIO tests
       ("pkg-config" ,pkg-config)
       ("python" ,python-wrapper)
       ("tzdata" ,tzdata-for-tests)))   ; for tests/gdatetime.c
    (propagated-inputs
     `(("libffi" ,libffi) ; in the Requires.private field of gobject-2.0.pc
       ("pcre" ,pcre)   ; in the Requires.private field of glib-2.0.pc
       ("util-linux" ,util-linux "lib") ;for libmount
       ("zlib" ,zlib))) ; in the Requires.private field of glib-2.0.pc
    (native-search-paths
     ;; This variable is not really "owned" by GLib, but several related
     ;; packages refer to it: gobject-introspection's tools use it as a search
     ;; path for .gir files, and it's also a search path for schemas produced
     ;; by 'glib-compile-schemas'.
     (list
      (search-path-specification
       (variable "XDG_DATA_DIRS")
       (files '("share")))
      ;; To load extra gio modules from glib-networking, etc.
      (search-path-specification
       (variable "GIO_EXTRA_MODULES")
       (files '("lib/gio/modules")))))
    (search-paths native-search-paths)
    (synopsis "Thread-safe general utility library; basis of GTK+ and GNOME")
    (description "GLib provides data structure handling for C, portability
wrappers, and interfaces for such runtime functionality as an event loop,
threads, dynamic loading, and an object system.")
    (home-page "https://developer.gnome.org/glib/")
    (license license:lgpl2.1+)))

(define-public glib-with-documentation
  ;; glib's doc must be built in a separate package since it requires gtk-doc,

D gnu/packages/patches/glib-CVE-2021-27218.patch => gnu/packages/patches/glib-CVE-2021-27218.patch +0 -132
@@ 1,132 0,0 @@
Backport of:

From 0f384c88a241bbbd884487b1c40b7b75f1e638d3 Mon Sep 17 00:00:00 2001
From: Krzesimir Nowak <qdlacz@gmail.com>
Date: Wed, 10 Feb 2021 23:51:07 +0100
Subject: [PATCH] gbytearray: Do not accept too large byte arrays

GByteArray uses guint for storing the length of the byte array, but it
also has a constructor (g_byte_array_new_take) that takes length as a
gsize. gsize may be larger than guint (64 bits for gsize vs 32 bits
for guint). It is possible to call the function with a value greater
than G_MAXUINT, which will result in silent length truncation. This
may happen as a result of unreffing GBytes into GByteArray, so rather
be loud about it.

(Test case tweaked by Philip Withnall.)

(Backport 2.66: Add #include gstrfuncsprivate.h in the test case for
`g_memdup2()`.)
---
 glib/garray.c      |  6 ++++++
 glib/gbytes.c      |  4 ++++
 glib/tests/bytes.c | 35 ++++++++++++++++++++++++++++++++++-
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/glib/garray.c b/glib/garray.c
index 942e74c9f..fb1a42aaf 100644
--- a/glib/garray.c
+++ b/glib/garray.c
@@ -2013,6 +2013,10 @@ g_byte_array_new (void)
  * Create byte array containing the data. The data will be owned by the array
  * and will be freed with g_free(), i.e. it could be allocated using g_strdup().
  *
+ * Do not use it if @len is greater than %G_MAXUINT. #GByteArray
+ * stores the length of its data in #guint, which may be shorter than
+ * #gsize.
+ *
  * Since: 2.32
  *
  * Returns: (transfer full): a new #GByteArray
@@ -2024,6 +2028,8 @@ g_byte_array_new_take (guint8 *data,
   GByteArray *array;
   GRealArray *real;
 
+  g_return_val_if_fail (len <= G_MAXUINT, NULL);
+
   array = g_byte_array_new ();
   real = (GRealArray *)array;
   g_assert (real->data == NULL);
diff --git a/glib/gbytes.c b/glib/gbytes.c
index 7b72886e5..d56abe6c3 100644
--- a/glib/gbytes.c
+++ b/glib/gbytes.c
@@ -519,6 +519,10 @@ g_bytes_unref_to_data (GBytes *bytes,
  * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all
  * other cases the data is copied.
  *
+ * Do not use it if @bytes contains more than %G_MAXUINT
+ * bytes. #GByteArray stores the length of its data in #guint, which
+ * may be shorter than #gsize, that @bytes is using.
+ *
  * Returns: (transfer full): a new mutable #GByteArray containing the same byte data
  *
  * Since: 2.32
diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
index 5ea5c2b35..15a6aaad6 100644
--- a/glib/tests/bytes.c
+++ b/glib/tests/bytes.c
@@ -10,12 +10,12 @@
  */
 
 #undef G_DISABLE_ASSERT
-#undef G_LOG_DOMAIN
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "glib.h"
+#include "glib/gstrfuncsprivate.h"
 
 /* Keep in sync with glib/gbytes.c */
 struct _GBytes
@@ -333,6 +333,38 @@ test_to_array_transferred (void)
   g_byte_array_unref (array);
 }
 
+static void
+test_to_array_transferred_oversize (void)
+{
+  g_test_message ("g_bytes_unref_to_array() can only take GBytes up to "
+                  "G_MAXUINT in length; test that longer ones are rejected");
+
+  if (sizeof (guint) >= sizeof (gsize))
+    {
+      g_test_skip ("Skipping test as guint is not smaller than gsize");
+    }
+  else if (g_test_undefined ())
+    {
+      GByteArray *array = NULL;
+      GBytes *bytes = NULL;
+      gpointer data = g_memdup2 (NYAN, N_NYAN);
+      gsize len = ((gsize) G_MAXUINT) + 1;
+
+      bytes = g_bytes_new_take (data, len);
+      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                             "g_byte_array_new_take: assertion 'len <= G_MAXUINT' failed");
+      array = g_bytes_unref_to_array (g_steal_pointer (&bytes));
+      g_test_assert_expected_messages ();
+      g_assert_null (array);
+
+      g_free (data);
+    }
+  else
+    {
+      g_test_skip ("Skipping test as testing undefined behaviour is disabled");
+    }
+}
+
 static void
 test_to_array_two_refs (void)
 {
@@ -410,6 +442,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred);
   g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
   g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
+  g_test_add_func ("/bytes/to-array/transferred/oversize", test_to_array_transferred_oversize);
   g_test_add_func ("/bytes/null", test_null);
 
   return g_test_run ();
-- 
2.30.1


D gnu/packages/patches/glib-CVE-2021-27219-01.patch => gnu/packages/patches/glib-CVE-2021-27219-01.patch +0 -176
@@ 1,176 0,0 @@
Backport of:

From 5e5f75a77e399c638be66d74e5daa8caeb433e00 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:30:52 +0000
Subject: [PATCH 01/11] gstrfuncs: Add internal g_memdup2() function
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This will replace the existing `g_memdup()` function for use within
GLib. It has an unavoidable security flaw of taking its `byte_size`
argument as a `guint` rather than as a `gsize`. Most callers will
expect it to be a `gsize`, and may pass in large values which could
silently be truncated, resulting in an undersize allocation compared
to what the caller expects.

This could lead to a classic buffer overflow vulnerability for many
callers of `g_memdup()`.

`g_memdup2()`, in comparison, takes its `byte_size` as a `gsize`.

Spotted by Kevin Backhouse of GHSL.

In GLib 2.68, `g_memdup2()` will be a new public API. In this version
for backport to older stable releases, it’s a new `static inline` API
in a private header, so that use of `g_memdup()` within GLib can be
fixed without adding a new API in a stable release series.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: GHSL-2021-045
Helps: #2319
---
 docs/reference/glib/meson.build |  1 +
 glib/gstrfuncsprivate.h         | 55 +++++++++++++++++++++++++++++++++
 glib/meson.build                |  1 +
 glib/tests/strfuncs.c           | 23 ++++++++++++++
 4 files changed, 80 insertions(+)
 create mode 100644 glib/gstrfuncsprivate.h

diff --git a/docs/reference/glib/meson.build b/docs/reference/glib/meson.build
index bba7649f0..ee39f6d04 100644
--- a/docs/reference/glib/meson.build
+++ b/docs/reference/glib/meson.build
@@ -22,6 +22,7 @@ if get_option('gtk_doc')
     'gprintfint.h',
     'gmirroringtable.h',
     'gscripttable.h',
+    'gstrfuncsprivate.h',
     'glib-mirroring-tab',
     'gnulib',
     'pcre',
diff --git a/glib/gstrfuncsprivate.h b/glib/gstrfuncsprivate.h
new file mode 100644
index 000000000..85c88328a
--- /dev/null
+++ b/glib/gstrfuncsprivate.h
@@ -0,0 +1,55 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+/*
+ * g_memdup2:
+ * @mem: (nullable): the memory to copy.
+ * @byte_size: the number of bytes to copy.
+ *
+ * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it
+ * from @mem. If @mem is %NULL it returns %NULL.
+ *
+ * This replaces g_memdup(), which was prone to integer overflows when
+ * converting the argument from a #gsize to a #guint.
+ *
+ * This static inline version is a backport of the new public API from
+ * GLib 2.68, kept internal to GLib for backport to older stable releases.
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2319.
+ *
+ * Returns: (nullable): a pointer to the newly-allocated copy of the memory,
+ *    or %NULL if @mem is %NULL.
+ * Since: 2.68
+ */
+static inline gpointer
+g_memdup2 (gconstpointer mem,
+           gsize         byte_size)
+{
+  gpointer new_mem;
+
+  if (mem && byte_size != 0)
+    {
+      new_mem = g_malloc (byte_size);
+      memcpy (new_mem, mem, byte_size);
+    }
+  else
+    new_mem = NULL;
+
+  return new_mem;
+}
diff --git a/glib/meson.build b/glib/meson.build
index aaf5f00f5..5a6eea397 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -268,6 +268,7 @@ glib_sources = files(
   'gslist.c',
   'gstdio.c',
   'gstrfuncs.c',
+  'gstrfuncsprivate.h',
   'gstring.c',
   'gstringchunk.c',
   'gtestutils.c',
diff --git a/glib/tests/strfuncs.c b/glib/tests/strfuncs.c
index e1f9619c7..d968afff9 100644
--- a/glib/tests/strfuncs.c
+++ b/glib/tests/strfuncs.c
@@ -32,6 +32,8 @@
 #include <string.h>
 #include "glib.h"
 
+#include "gstrfuncsprivate.h"
+
 #if defined (_MSC_VER) && (_MSC_VER <= 1800)
 #define isnan(x) _isnan(x)
 
@@ -219,6 +221,26 @@ test_memdup (void)
   g_free (str_dup);
 }
 
+/* Testing g_memdup2() function with various positive and negative cases */
+static void
+test_memdup2 (void)
+{
+  gchar *str_dup = NULL;
+  const gchar *str = "The quick brown fox jumps over the lazy dog";
+
+  /* Testing negative cases */
+  g_assert_null (g_memdup2 (NULL, 1024));
+  g_assert_null (g_memdup2 (str, 0));
+  g_assert_null (g_memdup2 (NULL, 0));
+
+  /* Testing normal usage cases */
+  str_dup = g_memdup2 (str, strlen (str) + 1);
+  g_assert_nonnull (str_dup);
+  g_assert_cmpstr (str, ==, str_dup);
+
+  g_free (str_dup);
+}
+
 /* Testing g_strpcpy() function with various positive and negative cases */
 static void
 test_stpcpy (void)
@@ -2523,6 +2545,7 @@ main (int   argc,
   g_test_add_func ("/strfuncs/has-prefix", test_has_prefix);
   g_test_add_func ("/strfuncs/has-suffix", test_has_suffix);
   g_test_add_func ("/strfuncs/memdup", test_memdup);
+  g_test_add_func ("/strfuncs/memdup2", test_memdup2);
   g_test_add_func ("/strfuncs/stpcpy", test_stpcpy);
   g_test_add_func ("/strfuncs/str_match_string", test_str_match_string);
   g_test_add_func ("/strfuncs/str_tokenize_and_fold", test_str_tokenize_and_fold);
-- 
2.30.1


D gnu/packages/patches/glib-CVE-2021-27219-02.patch => gnu/packages/patches/glib-CVE-2021-27219-02.patch +0 -264
@@ 1,264 0,0 @@
Backport of:

From be8834340a2d928ece82025463ae23dee2c333d0 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:37:56 +0000
Subject: [PATCH 02/11] gio: 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()`), 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.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gio/gdbusconnection.c                 | 5 +++--
 gio/gdbusinterfaceskeleton.c          | 3 ++-
 gio/gfile.c                           | 7 ++++---
 gio/gsettingsschema.c                 | 5 +++--
 gio/gwin32registrykey.c               | 8 +++++---
 gio/tests/async-close-output-stream.c | 6 ++++--
 gio/tests/gdbus-export.c              | 5 +++--
 gio/win32/gwinhttpfile.c              | 9 +++++----
 8 files changed, 29 insertions(+), 19 deletions(-)

diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index f1f0921d4..d56453486 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -110,6 +110,7 @@
 #include "gasyncinitable.h"
 #include "giostream.h"
 #include "gasyncresult.h"
+#include "gstrfuncsprivate.h"
 #include "gtask.h"
 #include "gmarshal-internal.h"
 
@@ -3997,7 +3998,7 @@ _g_dbus_interface_vtable_copy (const GDBusInterfaceVTable *vtable)
   /* Don't waste memory by copying padding - remember to update this
    * when changing struct _GDBusInterfaceVTable in gdbusconnection.h
    */
-  return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer));
+  return g_memdup2 ((gconstpointer) vtable, 3 * sizeof (gpointer));
 }
 
 static void
@@ -4014,7 +4015,7 @@ _g_dbus_subtree_vtable_copy (const GDBusSubtreeVTable *vtable)
   /* Don't waste memory by copying padding - remember to update this
    * when changing struct _GDBusSubtreeVTable in gdbusconnection.h
    */
-  return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer));
+  return g_memdup2 ((gconstpointer) vtable, 3 * sizeof (gpointer));
 }
 
 static void
diff --git a/gio/gdbusinterfaceskeleton.c b/gio/gdbusinterfaceskeleton.c
index 4a06516c1..4a4b719a5 100644
--- a/gio/gdbusinterfaceskeleton.c
+++ b/gio/gdbusinterfaceskeleton.c
@@ -28,6 +28,7 @@
 #include "gdbusmethodinvocation.h"
 #include "gdbusconnection.h"
 #include "gmarshal-internal.h"
+#include "gstrfuncsprivate.h"
 #include "gtask.h"
 #include "gioerror.h"
 
@@ -701,7 +702,7 @@ add_connection_locked (GDBusInterfaceSkeleton *interface_,
        * properly before building the hooked_vtable, so we create it
        * once at the last minute.
        */
-      interface_->priv->hooked_vtable = g_memdup (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
+      interface_->priv->hooked_vtable = g_memdup2 (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
       interface_->priv->hooked_vtable->method_call = skeleton_intercept_handle_method_call;
     }
 
diff --git a/gio/gfile.c b/gio/gfile.c
index ba93f7c75..88b341e7d 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -60,6 +60,7 @@
 #include "gasyncresult.h"
 #include "gioerror.h"
 #include "glibintl.h"
+#include "gstrfuncsprivate.h"
 
 
 /**
@@ -7884,7 +7885,7 @@ measure_disk_usage_progress (gboolean reporting,
   g_main_context_invoke_full (g_task_get_context (task),
                               g_task_get_priority (task),
                               measure_disk_usage_invoke_progress,
-                              g_memdup (&progress, sizeof progress),
+                              g_memdup2 (&progress, sizeof progress),
                               g_free);
 }
 
@@ -7902,7 +7903,7 @@ measure_disk_usage_thread (GTask        *task,
                                  data->progress_callback ? measure_disk_usage_progress : NULL, task,
                                  &result.disk_usage, &result.num_dirs, &result.num_files,
                                  &error))
-    g_task_return_pointer (task, g_memdup (&result, sizeof result), g_free);
+    g_task_return_pointer (task, g_memdup2 (&result, sizeof result), g_free);
   else
     g_task_return_error (task, error);
 }
@@ -7926,7 +7927,7 @@ g_file_real_measure_disk_usage_async (GFile                        *file,
 
   task = g_task_new (file, cancellable, callback, user_data);
   g_task_set_source_tag (task, g_file_real_measure_disk_usage_async);
-  g_task_set_task_data (task, g_memdup (&data, sizeof data), g_free);
+  g_task_set_task_data (task, g_memdup2 (&data, sizeof data), g_free);
   g_task_set_priority (task, io_priority);
 
   g_task_run_in_thread (task, measure_disk_usage_thread);
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 3a60b8c64..dded9b1ca 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -20,6 +20,7 @@
 
 #include "gsettingsschema-internal.h"
 #include "gsettings.h"
+#include "gstrfuncsprivate.h"
 
 #include "gvdb/gvdb-reader.h"
 #include "strinfo.c"
@@ -1058,9 +1059,9 @@ g_settings_schema_list_children (GSettingsSchema *schema)
 
       if (g_str_has_suffix (key, "/"))
         {
-          gint length = strlen (key);
+          gsize length = strlen (key);
 
-          strv[j] = g_memdup (key, length);
+          strv[j] = g_memdup2 (key, length);
           strv[j][length - 1] = '\0';
           j++;
         }
diff --git a/gio/gwin32registrykey.c b/gio/gwin32registrykey.c
index c19fede4e..619fd48af 100644
--- a/gio/gwin32registrykey.c
+++ b/gio/gwin32registrykey.c
@@ -28,6 +28,8 @@
 #include <ntstatus.h>
 #include <winternl.h>
 
+#include "gstrfuncsprivate.h"
+
 #ifndef _WDMDDK_
 typedef enum _KEY_INFORMATION_CLASS {
   KeyBasicInformation,
@@ -247,7 +249,7 @@ g_win32_registry_value_iter_copy (const GWin32RegistryValueIter *iter)
   new_iter->value_name_size = iter->value_name_size;
 
   if (iter->value_data != NULL)
-    new_iter->value_data = g_memdup (iter->value_data, iter->value_data_size);
+    new_iter->value_data = g_memdup2 (iter->value_data, iter->value_data_size);
 
   new_iter->value_data_size = iter->value_data_size;
 
@@ -268,8 +270,8 @@ g_win32_registry_value_iter_copy (const GWin32RegistryValueIter *iter)
   new_iter->value_data_expanded_charsize = iter->value_data_expanded_charsize;
 
   if (iter->value_data_expanded_u8 != NULL)
-    new_iter->value_data_expanded_u8 = g_memdup (iter->value_data_expanded_u8,
-                                                 iter->value_data_expanded_charsize);
+    new_iter->value_data_expanded_u8 = g_memdup2 (iter->value_data_expanded_u8,
+                                                  iter->value_data_expanded_charsize);
 
   new_iter->value_data_expanded_u8_size = iter->value_data_expanded_charsize;
 
diff --git a/gio/tests/async-close-output-stream.c b/gio/tests/async-close-output-stream.c
index 5f6620275..d3f97a119 100644
--- a/gio/tests/async-close-output-stream.c
+++ b/gio/tests/async-close-output-stream.c
@@ -24,6 +24,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "gstrfuncsprivate.h"
+
 #define DATA_TO_WRITE "Hello world\n"
 
 typedef struct
@@ -147,9 +149,9 @@ prepare_data (SetupData *data,
 
   data->expected_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (data->data_stream));
 
-  g_assert_cmpint (data->expected_size, >, 0);
+  g_assert_cmpuint (data->expected_size, >, 0);
 
-  data->expected_output = g_memdup (written, (guint)data->expected_size);
+  data->expected_output = g_memdup2 (written, data->expected_size);
 
   /* then recreate the streams and prepare them for the asynchronous close */
   destroy_streams (data);
diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c
index 506c7458a..5513306f8 100644
--- a/gio/tests/gdbus-export.c
+++ b/gio/tests/gdbus-export.c
@@ -23,6 +23,7 @@
 #include <string.h>
 
 #include "gdbus-tests.h"
+#include "gstrfuncsprivate.h"
 
 /* all tests rely on a shared mainloop */
 static GMainLoop *loop = NULL;
@@ -671,7 +672,7 @@ subtree_introspect (GDBusConnection       *connection,
       g_assert_not_reached ();
     }
 
-  return g_memdup (interfaces, 2 * sizeof (void *));
+  return g_memdup2 (interfaces, 2 * sizeof (void *));
 }
 
 static const GDBusInterfaceVTable *
@@ -727,7 +728,7 @@ dynamic_subtree_introspect (GDBusConnection       *connection,
 {
   const GDBusInterfaceInfo *interfaces[2] = { &dyna_interface_info, NULL };
 
-  return g_memdup (interfaces, 2 * sizeof (void *));
+  return g_memdup2 (interfaces, 2 * sizeof (void *));
 }
 
 static const GDBusInterfaceVTable *
diff --git a/gio/win32/gwinhttpfile.c b/gio/win32/gwinhttpfile.c
index cf5eed31d..040ee8564 100644
--- a/gio/win32/gwinhttpfile.c
+++ b/gio/win32/gwinhttpfile.c
@@ -29,6 +29,7 @@
 #include "gio/gfile.h"
 #include "gio/gfileattribute.h"
 #include "gio/gfileinfo.h"
+#include "gstrfuncsprivate.h"
 #include "gwinhttpfile.h"
 #include "gwinhttpfileinputstream.h"
 #include "gwinhttpfileoutputstream.h"
@@ -393,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_memdup (winhttp_file->url.lpszScheme, (winhttp_file->url.dwSchemeLength+1)*2);
-  child->url.lpszHostName = g_memdup (winhttp_file->url.lpszHostName, (winhttp_file->url.dwHostNameLength+1)*2);
-  child->url.lpszUserName = g_memdup (winhttp_file->url.lpszUserName, (winhttp_file->url.dwUserNameLength+1)*2);
-  child->url.lpszPassword = g_memdup (winhttp_file->url.lpszPassword, (winhttp_file->url.dwPasswordLength+1)*2);
+  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.lpszUrlPath = wnew_path;
   child->url.dwUrlPathLength = wcslen (wnew_path);
   child->url.lpszExtraInfo = NULL;
-- 
2.30.1


D gnu/packages/patches/glib-CVE-2021-27219-03.patch => gnu/packages/patches/glib-CVE-2021-27219-03.patch +0 -136
@@ 1,136 0,0 @@
From 6110caea45b235420b98cd41d845cc92238f6781 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Thu, 4 Feb 2021 13:39:25 +0000
Subject: [PATCH 03/11] gobject: 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()`), 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.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2319
---
 gobject/gsignal.c     | 3 ++-
 gobject/gtype.c       | 9 +++++----
 gobject/gtypemodule.c | 3 ++-
 gobject/tests/param.c | 4 +++-
 4 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/gobject/gsignal.c b/gobject/gsignal.c
index 77d8f211e..41c54ab57 100644
--- a/gobject/gsignal.c
+++ b/gobject/gsignal.c
@@ -28,6 +28,7 @@
 #include <signal.h>
 
 #include "gsignal.h"
+#include "gstrfuncsprivate.h"
 #include "gtype-private.h"
 #include "gbsearcharray.h"
 #include "gvaluecollector.h"
@@ -1730,7 +1731,7 @@ g_signal_newv (const gchar       *signal_name,
   node->single_va_closure_is_valid = FALSE;
   node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
   node->n_params = n_params;
-  node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
+  node->param_types = g_memdup2 (param_types, sizeof (GType) * n_params);
   node->return_type = return_type;
   node->class_closure_bsa = NULL;
   if (accumulator)
diff --git a/gobject/gtype.c b/gobject/gtype.c
index 7d3789400..8441b90e9 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -33,6 +33,7 @@
 
 #include "glib-private.h"
 #include "gconstructor.h"
+#include "gstrfuncsprivate.h"
 
 #ifdef G_OS_WIN32
 #include <windows.h>
@@ -1470,7 +1471,7 @@ type_add_interface_Wm (TypeNode             *node,
   iholder->next = iface_node_get_holders_L (iface);
   iface_node_set_holders_W (iface, iholder);
   iholder->instance_type = NODE_TYPE (node);
-  iholder->info = info ? g_memdup (info, sizeof (*info)) : NULL;
+  iholder->info = info ? g_memdup2 (info, sizeof (*info)) : NULL;
   iholder->plugin = plugin;
 
   /* create an iface entry for this type */
@@ -1731,7 +1732,7 @@ type_iface_retrieve_holder_info_Wm (TypeNode *iface,
         INVALID_RECURSION ("g_type_plugin_*", iholder->plugin, NODE_NAME (iface));
       
       check_interface_info_I (iface, instance_type, &tmp_info);
-      iholder->info = g_memdup (&tmp_info, sizeof (tmp_info));
+      iholder->info = g_memdup2 (&tmp_info, sizeof (tmp_info));
     }
   
   return iholder;	/* we don't modify write lock upon returning NULL */
@@ -2016,10 +2017,10 @@ type_iface_vtable_base_init_Wm (TypeNode *iface,
       IFaceEntry *pentry = type_lookup_iface_entry_L (pnode, iface);
       
       if (pentry)
-	vtable = g_memdup (pentry->vtable, iface->data->iface.vtable_size);
+	vtable = g_memdup2 (pentry->vtable, iface->data->iface.vtable_size);
     }
   if (!vtable)
-    vtable = g_memdup (iface->data->iface.dflt_vtable, iface->data->iface.vtable_size);
+    vtable = g_memdup2 (iface->data->iface.dflt_vtable, iface->data->iface.vtable_size);
   entry->vtable = vtable;
   vtable->g_type = NODE_TYPE (iface);
   vtable->g_instance_type = NODE_TYPE (node);
diff --git a/gobject/gtypemodule.c b/gobject/gtypemodule.c
index 4ecaf8c88..20911fafd 100644
--- a/gobject/gtypemodule.c
+++ b/gobject/gtypemodule.c
@@ -19,6 +19,7 @@
 
 #include <stdlib.h>
 
+#include "gstrfuncsprivate.h"
 #include "gtypeplugin.h"
 #include "gtypemodule.h"
 
@@ -436,7 +437,7 @@ g_type_module_register_type (GTypeModule     *module,
   module_type_info->loaded = TRUE;
   module_type_info->info = *type_info;
   if (type_info->value_table)
-    module_type_info->info.value_table = g_memdup (type_info->value_table,
+    module_type_info->info.value_table = g_memdup2 (type_info->value_table,
 						   sizeof (GTypeValueTable));
 
   return module_type_info->type;
diff --git a/gobject/tests/param.c b/gobject/tests/param.c
index 758289bf8..971cff162 100644
--- a/gobject/tests/param.c
+++ b/gobject/tests/param.c
@@ -2,6 +2,8 @@
 #include <glib-object.h>
 #include <stdlib.h>
 
+#include "gstrfuncsprivate.h"
+
 static void
 test_param_value (void)
 {
@@ -851,7 +853,7 @@ main (int argc, char *argv[])
             test_path = g_strdup_printf ("/param/implement/subprocess/%d-%d-%d-%d",
                                          data.change_this_flag, data.change_this_type,
                                          data.use_this_flag, data.use_this_type);
-            test_data = g_memdup (&data, sizeof (TestParamImplementData));
+            test_data = g_memdup2 (&data, sizeof (TestParamImplementData));
             g_test_add_data_func_full (test_path, test_data, test_param_implement_child, g_free);
             g_free (test_path);
           }
-- 
2.30.1


D gnu/packages/patches/glib-CVE-2021-27219-04.patch => gnu/packages/patches/glib-CVE-2021-27219-04.patch +0 -308
@@ 1,308 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-05.patch => gnu/packages/patches/glib-CVE-2021-27219-05.patch +0 -47
@@ 1,47 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-06.patch => gnu/packages/patches/glib-CVE-2021-27219-06.patch +0 -94
@@ 1,94 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-07.patch => gnu/packages/patches/glib-CVE-2021-27219-07.patch +0 -118
@@ 1,118 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-08.patch => gnu/packages/patches/glib-CVE-2021-27219-08.patch +0 -94
@@ 1,94 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-09.patch => gnu/packages/patches/glib-CVE-2021-27219-09.patch +0 -98
@@ 1,98 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-10.patch => gnu/packages/patches/glib-CVE-2021-27219-10.patch +0 -52
@@ 1,52 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-11.patch => gnu/packages/patches/glib-CVE-2021-27219-11.patch +0 -57
@@ 1,57 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-12.patch => gnu/packages/patches/glib-CVE-2021-27219-12.patch +0 -30
@@ 1,30 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-13.patch => gnu/packages/patches/glib-CVE-2021-27219-13.patch +0 -32
@@ 1,32 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-14.patch => gnu/packages/patches/glib-CVE-2021-27219-14.patch +0 -32
@@ 1,32 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-15.patch => gnu/packages/patches/glib-CVE-2021-27219-15.patch +0 -95
@@ 1,95 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-16.patch => gnu/packages/patches/glib-CVE-2021-27219-16.patch +0 -43
@@ 1,43 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-17.patch => gnu/packages/patches/glib-CVE-2021-27219-17.patch +0 -37
@@ 1,37 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-27219-18.patch => gnu/packages/patches/glib-CVE-2021-27219-18.patch +0 -232
@@ 1,232 0,0 @@
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


D gnu/packages/patches/glib-CVE-2021-28153.patch => gnu/packages/patches/glib-CVE-2021-28153.patch +0 -283
@@ 1,283 0,0 @@
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