diff --git a/base/rx/rx-cairo/cairo.spec b/base/rx/rx-cairo/cairo.spec index bb423b3..0caa4db 100644 --- a/base/rx/rx-cairo/cairo.spec +++ b/base/rx/rx-cairo/cairo.spec @@ -14,7 +14,7 @@ Name: rx-cairo Version: 1.17.4 -Release: 3%{?dist} +Release: 5%{?dist} Summary: A 2D graphics library License: LGPLv2 or MPLv1.1 @@ -187,6 +187,9 @@ find $RPM_BUILD_ROOT -name '*.la' -delete %{_libdir}/cairo/ %changelog +* Tue Oct 29 2024 Raven - 1.17.4-4 +- rebuilt with new rx-freetype + * Mon Jan 30 2023 Stewart Smith - 1.17.4-3.amzn2023.0.2 - Mass rebuild for AL2023 diff --git a/base/rx/rx-freerdp/freerdp.spec b/base/rx/rx-freerdp/freerdp.spec index fc6c175..b036324 100644 --- a/base/rx/rx-freerdp/freerdp.spec +++ b/base/rx/rx-freerdp/freerdp.spec @@ -4,7 +4,7 @@ # "--with=ffmpeg", or "--with=openh264" to mock/rpmbuild; or by globally # setting these variables: # https://bugzilla.redhat.com/show_bug.cgi?id=2242028 -%global _with_ffmpeg 1 +#global _with_ffmpeg 1 %global _with_openh264 1 # Can be rebuilt with OpenCL support enabled by passing # "--with=opencl" @@ -28,8 +28,8 @@ %endif Name: rx-freerdp -Version: 2.11.2 -Release: 3%{?dist} +Version: 2.11.7 +Release: 1%{?dist} Epoch: 2 Summary: Free implementation of the Remote Desktop Protocol (RDP) License: ASL 2.0 @@ -100,7 +100,6 @@ Obsoletes: %{name}-plugins < 1:1.1.0 Provides: %{name}-plugins = %{?epoch}:%{version}-%{release} Requires: (libwayland-client >= 1.22 or rx-libwayland-client) Requires: (libwayland-cursor >= 1.22 or rx-libwayland-cursor) -#Requires: (libxkbcommon_ng or libxkbcommon >= 1.0) %description libs libfreerdp-core can be embedded in applications. @@ -302,6 +301,9 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools2.pc %changelog +* Tue Oct 29 2024 Raven - 2:2.11.7-1 +- Update to 2.11.7 + * Fri Sep 13 2024 Raven - 2:2.11.2-3 - rebuilt with new OpenSSL diff --git a/base/rx/rx-freetype/freetype-2.10.1-debughook.patch b/base/rx/rx-freetype/freetype-2.10.1-debughook.patch deleted file mode 100644 index 4421401..0000000 --- a/base/rx/rx-freetype/freetype-2.10.1-debughook.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/include/freetype/ftmodapi.h b/include/freetype/ftmodapi.h -index 8d039c4f3..88488bfe8 100644 ---- a/include/freetype/ftmodapi.h -+++ b/include/freetype/ftmodapi.h -@@ -623,7 +623,7 @@ FT_BEGIN_HEADER - * it is bytecode interpreter's execution context, `TT_ExecContext`, - * which is declared in FreeType's internal header file `tttypes.h`. - */ -- typedef FT_Error -+ typedef void - (*FT_DebugHook_Func)( void* arg ); - - diff --git a/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch b/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch deleted file mode 100644 index 55c09bd..0000000 --- a/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 53dfdcd8198d2b3201a23c4bad9190519ba918db Mon Sep 17 00:00:00 2001 -From: Werner Lemberg -Date: Thu, 17 Mar 2022 19:24:16 +0100 -Subject: [PATCH] [sfnt] Avoid invalid face index. - -Fixes #1138. - -* src/sfnt/sfobjs.c (sfnt_init_face), src/sfnt/sfwoff2.c (woff2_open_font): -Check `face_index` before decrementing. ---- - src/sfnt/sfobjs.c | 2 +- - src/sfnt/sfwoff2.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c -index f9d4d3858..9771c35df 100644 ---- a/src/sfnt/sfobjs.c -+++ b/src/sfnt/sfobjs.c -@@ -566,7 +566,7 @@ - face_index = FT_ABS( face_instance_index ) & 0xFFFF; - - /* value -(N+1) requests information on index N */ -- if ( face_instance_index < 0 ) -+ if ( face_instance_index < 0 && face_index > 0 ) - face_index--; - - if ( face_index >= face->ttc_header.count ) -diff --git a/src/sfnt/sfwoff2.c b/src/sfnt/sfwoff2.c -index cb1e0664a..165b875e5 100644 ---- a/src/sfnt/sfwoff2.c -+++ b/src/sfnt/sfwoff2.c -@@ -2085,7 +2085,7 @@ - /* Validate requested face index. */ - *num_faces = woff2.num_fonts; - /* value -(N+1) requests information on index N */ -- if ( *face_instance_index < 0 ) -+ if ( *face_instance_index < 0 && face_index > 0 ) - face_index--; - - if ( face_index >= woff2.num_fonts ) --- -2.35.1 - diff --git a/base/rx/rx-freetype/freetype-2.10.4-covscan.patch b/base/rx/rx-freetype/freetype-2.10.4-covscan.patch deleted file mode 100644 index d42ca75..0000000 --- a/base/rx/rx-freetype/freetype-2.10.4-covscan.patch +++ /dev/null @@ -1,45 +0,0 @@ ---- freetype-2.10.4/builds/unix/freetype-config.in -+++ freetype-2.10.4/builds/unix/freetype-config.in -@@ -32,9 +32,6 @@ cflags=`%PKG_CONFIG% --cflags freetype2` - dynamic_libs=`pkgconf --libs freetype2` - static_libs=`pkgconf --static --libs freetype2` - --orig_prefix=$prefix --orig_exec_prefix=$exec_prefix -- - orig_includedir=$includedir - orig_libdir=$libdir - ---- freetype-2.10.4/ft2demos-2.10.4/src/ftbench.c -+++ freetype-2.10.4/ft2demos-2.10.4/src/ftbench.c -@@ -749,6 +749,7 @@ - { - fprintf( stderr, - "couldn't allocate memory to pre-load font file\n" ); -+ fclose( file ); - - return 1; - } -@@ -758,9 +759,12 @@ - fprintf( stderr, "read error\n" ); - free( memory_file ); - memory_file = NULL; -+ fclose( file ); - - return 1; - } -+ -+ fclose( file ); - } - - error = FT_New_Memory_Face( lib, ---- freetype-2.10.4/ft2demos-2.10.4/src/ftgrid.c -+++ freetype-2.10.4/ft2demos-2.10.4/src/ftgrid.c -@@ -662,6 +662,7 @@ - break; - - default: -+ free( t ); - return; - } - diff --git a/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch b/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch deleted file mode 100644 index 8a447ae..0000000 --- a/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0c2bdb01a2e1d24a3e592377a6d0822856e10df2 Mon Sep 17 00:00:00 2001 -From: Werner Lemberg -Date: Sat, 19 Mar 2022 09:37:28 +0100 -Subject: [PATCH] * src/base/ftobjs.c (FT_Request_Size): Guard `face->size`. - -Fixes #1140. ---- - src/base/ftobjs.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c -index 6492a1517..282c9121a 100644 ---- a/src/base/ftobjs.c -+++ b/src/base/ftobjs.c -@@ -3409,6 +3409,9 @@ - if ( !face ) - return FT_THROW( Invalid_Face_Handle ); - -+ if ( !face->size ) -+ return FT_THROW( Invalid_Size_Handle ); -+ - if ( !req || req->width < 0 || req->height < 0 || - req->type >= FT_SIZE_REQUEST_TYPE_MAX ) - return FT_THROW( Invalid_Argument ); --- -2.35.1 - diff --git a/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch b/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch deleted file mode 100644 index 52df99a..0000000 --- a/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 007c109b4594c5e63948bd08b4d5011ad76ffb10 Mon Sep 17 00:00:00 2001 -From: Ben Wagner -Date: Fri, 23 Oct 2020 08:29:14 +0200 -Subject: [PATCH] * src/sfnt/pngshim.c (Load_SBit_Png): Fix memory leak - (#59322). - -The issue is that `rows` is allocated but will not be freed in the -event that the call to `png_read_image` fails and calls `longjmp`. ---- - ChangeLog | 7 +++++++ - src/sfnt/pngshim.c | 1 + - 2 files changed, 8 insertions(+) - -diff --git a/ChangeLog b/ChangeLog -index 42f7c34ba..ff048b8ab 100644 ---- a/ChangeLog -+++ b/ChangeLog -@@ -1,3 +1,10 @@ -+2020-10-23 Ben Wagner -+ -+ * src/sfnt/pngshim.c (Load_SBit_Png): Fix memory leak (#59322). -+ -+ The issue is that `rows` is allocated but will not be freed in the -+ event that the call to `png_read_image` fails and calls `longjmp`. -+ - 2020-10-20 Werner Lemberg - - * Version 2.10.4 released. -diff --git a/src/sfnt/pngshim.c b/src/sfnt/pngshim.c -index f55016122..d4e43a9f4 100644 ---- a/src/sfnt/pngshim.c -+++ b/src/sfnt/pngshim.c -@@ -443,6 +443,7 @@ - png_read_end( png, info ); - - DestroyExit: -+ FT_FREE( rows ); - png_destroy_read_struct( &png, &info, NULL ); - FT_Stream_Close( &stream ); - --- -2.26.2 - diff --git a/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch b/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch deleted file mode 100644 index 92ae817..0000000 --- a/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 22a0cccb4d9d002f33c1ba7a4b36812c7d4f46b5 Mon Sep 17 00:00:00 2001 -From: Werner Lemberg -Date: Sat, 19 Mar 2022 06:40:17 +0100 -Subject: [PATCH] * src/base/ftobjs.c (ft_open_face_internal): Properly guard - `face_index`. - -We must ensure that the cast to `FT_Int` doesn't change the sign. - -Fixes #1139. ---- - src/base/ftobjs.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -From d014387ad4a5dd04d8e7f99587c7dacb70261924 Mon Sep 17 00:00:00 2001 -From: Werner Lemberg -Date: Sat, 19 Mar 2022 09:30:45 +0100 -Subject: [PATCH 2/2] * src/base/ftobjs.c (ft_open_face_internal): Thinko. - ---- - src/base/ftobjs.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c -index 2c0f0e6c9..10952a6c6 100644 ---- a/src/base/ftobjs.c -+++ b/src/base/ftobjs.c -@@ -2527,6 +2527,16 @@ - #endif - - -+ /* only use lower 31 bits together with sign bit */ -+ if ( face_index > 0 ) -+ face_index &= 0x7FFFFFFFL; -+ else -+ { -+ face_index = -face_index; -+ face_index &= 0x7FFFFFFFL; -+ face_index = -face_index; -+ } -+ - #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE3(( "FT_Open_Face: " )); - if ( face_index < 0 ) --- -2.35.1 - diff --git a/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch b/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch new file mode 100644 index 0000000..81d28f8 --- /dev/null +++ b/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch @@ -0,0 +1,75 @@ +diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c +index b7403fa65..1fd5a0be3 100644 +--- a/src/autofit/afglobal.c ++++ b/src/autofit/afglobal.c +@@ -245,6 +245,12 @@ + af_shaper_get_coverage( globals, style_class, gstyles, 0 ); + } + ++ if ( dflt >= sizeof (af_style_classes) / sizeof (AF_StyleClass) ) ++ { ++ error = FT_THROW( Invalid_Offset ); ++ goto Exit; ++ } ++ + /* ... and finally the default OpenType features of the default script */ + af_shaper_get_coverage( globals, af_style_classes[dflt], gstyles, 1 ); + +diff --git a/src/tools/apinames.c b/src/tools/apinames.c +index 5a49b0649..feefb4ee7 100644 +--- a/src/tools/apinames.c ++++ b/src/tools/apinames.c +@@ -182,6 +182,7 @@ names_dump( FILE* out, + case OUTPUT_WATCOM_LBC: + { + const char* dot; ++ char temp[512]; + + + if ( !dll_name ) +@@ -195,7 +196,6 @@ names_dump( FILE* out, + dot = strchr( dll_name, '.' ); + if ( dot ) + { +- char temp[512]; + int len = dot - dll_name; + + +diff --git a/src/ftbench.c b/src/ftbench.c +index ec5c46c..7d96f60 100644 +--- a/ft2demos-2.13.2/src/ftbench.c ++++ b/ft2demos-2.13.2/src/ftbench.c +@@ -907,6 +907,7 @@ + { + fprintf( stderr, + "couldn't allocate memory to pre-load font file\n" ); ++ fclose( file ); + + return 1; + } +@@ -916,9 +917,11 @@ + fprintf( stderr, "read error\n" ); + free( memory_file ); + memory_file = NULL; ++ fclose( file ); + + return 1; + } ++ fclose( file ); + } + + error = FT_New_Memory_Face( lib, +diff --git a/src/ftgrid.c b/src/ftgrid.c +index bae4826..1a8f421 100644 +--- a/ft2demos-2.13.2/src/ftgrid.c ++++ b/ft2demos-2.13.2/src/ftgrid.c +@@ -420,6 +420,9 @@ + if ( !line ) + return; + ++ if (bit->mode == gr_pixel_mode_mono) ++ memset( line, 0, (size_t)( pitch * bit->rows * scale * scale )); ++ + switch( bit->mode ) + { + case gr_pixel_mode_mono: diff --git a/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch b/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch index e737cb7..7e58be7 100644 --- a/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch +++ b/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch @@ -14,4 +14,4 @@ + EXES += fttimer # EXES += testname - exes: $(EXES:%=$(BIN_DIR_2)/%$E) + # Not all demo programs have a man page; we thus check for existence in a diff --git a/base/rx/rx-freetype/freetype.spec b/base/rx/rx-freetype/freetype.spec index 448bdfe..1d14a95 100644 --- a/base/rx/rx-freetype/freetype.spec +++ b/base/rx/rx-freetype/freetype.spec @@ -5,9 +5,9 @@ Summary: A free and portable font rendering engine Name: rx-freetype -Version: 2.10.4 -Release: 9%{?dist} -License: (FTL or GPLv2+) and BSD and MIT and Public Domain and zlib with acknowledgement +Version: 2.13.2 +Release: 7%{?dist} +License: (FTL OR GPL-2.0-or-later) AND BSD-3-Clause AND MIT AND MIT-Modern-Variant AND LicenseRef-Fedora-Public-Domain AND Zlib URL: http://www.freetype.org Source: http://download.savannah.gnu.org/releases/freetype/freetype-%{version}.tar.xz Source1: http://download.savannah.gnu.org/releases/freetype/freetype-doc-%{version}.tar.xz @@ -26,22 +26,8 @@ Patch3: freetype-2.6.5-libtool.patch Patch4: freetype-2.8-multilib.patch Patch5: freetype-2.10.0-internal-outline.patch -# Revert ABI/API change -Patch6: freetype-2.10.1-debughook.patch -Patch7: freetype-2.10.4-png-memory-leak.patch - -# https://bugzilla.redhat.com/show_bug.cgi?id=1964066 -Patch8: freetype-2.10.4-covscan.patch - -# https://bugzilla.redhat.com/show_bug.cgi?id=2077989 -Patch9: freetype-2.10.4-avoid-invalid-face-index.patch - -# https://bugzilla.redhat.com/show_bug.cgi?id=2077991 -Patch10: freetype-2.10.4-properly-guard-face_index.patch - -# https://bugzilla.redhat.com/show_bug.cgi?id=2077985 -Patch11: freetype-2.10.4-guard-face-size.patch +Patch6: freetype-2.13.2-SAST-findings.patch BuildRequires: gcc BuildRequires: libX11-devel @@ -52,14 +38,13 @@ BuildRequires: brotli-devel BuildRequires: make %if %{without bootstrap} BuildRequires: rx-harfbuzz-devel + +Requires: rx-harfbuzz %endif - -Provides: pkgconfig(freetype2) = %{version}-%{release} Provides: %{name}-bytecode Provides: %{name}-subpixel Obsoletes: freetype-freeworld < 2.9.1-2 -Requires: rx-harfbuzz %description The FreeType engine is a free and portable font rendering @@ -85,8 +70,6 @@ small utilities showing various capabilities of the FreeType library. Summary: FreeType development libraries and header files Requires: %{name} = %{version}-%{release} Requires: pkgconf%{?_isa} -#Conflicts: freetype-devel - %description devel The freetype-devel package includes the static libraries and header files @@ -109,14 +92,11 @@ popd %patch3 -p1 -b .libtool %patch4 -p1 -b .multilib %patch5 -p1 -b .internal-outline -%patch6 -p1 -b .debughook -%patch7 -p1 -b .png-memory-leak -%patch8 -p1 -b .covscan -%patch9 -p1 -b .avoid-invalid-face-index -%patch10 -p1 -b .properly-guard-face_index -%patch11 -p1 -b .guard-face-size +%patch6 -p1 -b .SAST-findings %build + +export PATH=%{_bindir}:$PATH export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} ${LDFLAGS}" export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} @@ -210,7 +190,7 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.{a,la} %files %{!?_licensedir:%global license %%doc} -%license docs/LICENSE.TXT docs/FTL.TXT docs/GPLv2.TXT +%license LICENSE.TXT docs/FTL.TXT docs/GPLv2.TXT %{_libdir}/libfreetype.so.* %doc README @@ -258,28 +238,70 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.{a,la} %{_mandir}/man1/* %changelog -* Tue May 31 2022 Marek Kasik - 2.10.4-9 -- Guard face->size -- Resolves: #2079280 +* Mon Sep 30 2024 Marek Kasik - 2.13.2-7 +- Fix SAST Automation findings +- Resolves: RHEL-44737 -* Mon May 30 2022 Marek Kasik - 2.10.4-8 -- Properly guard "face_index" -- Resolves: #2079262 +* Mon Jun 24 2024 Troy Dawson - 2.13.2-6 +- Bump release for June 2024 mass rebuild -* Thu May 26 2022 Marek Kasik - 2.10.4-7 -- Avoid invalid face index -- Resolves: #2079271 +* Tue Feb 6 2024 Marek Kasik - 2.13.2-5 +- Migrated to SPDX license -* Mon Aug 09 2021 Mohan Boddu - 2.10.4-6 -- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags - Related: rhbz#1991688 +* Tue Jan 30 2024 Marek Kasik - 2.13.2-4 +- Remove a patch which causes FTBFS +- Resolves: #2261113 -* Wed May 26 2021 Marek Kasik - 2.10.4-5 -- Backport fixes for issues found by Coverity scan -- Resolves: #1964066 +* Wed Jan 24 2024 Fedora Release Engineering - 2.13.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild -* Thu Apr 15 2021 Mohan Boddu - 2.10.4-4 -- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 +* Fri Jan 19 2024 Fedora Release Engineering - 2.13.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Tue Jan 16 2024 Marek Kasik - 2.13.2-1 +- Update to 2.13.2 +- Resolves: #2217137 + +* Wed Jul 19 2023 Fedora Release Engineering - 2.13.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue Jun 27 2023 Marek Kasik - 2.13.1-1 +- Update to 2.13.1 +- Resolves: #2217137 + +* Sat Feb 25 2023 Marek Kasik - 2.13.0-1 +- Update to 2.13.0 +- Resolves: #2168496 + +* Thu Jan 19 2023 Fedora Release Engineering - 2.12.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Thu Jul 21 2022 Fedora Release Engineering - 2.12.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Fri Jul 8 2022 Marek Kasik - 2.12.1-2 +- Clear correct flags for doc ownership +- Resolves: #2104570 + +* Mon May 2 2022 Marek Kasik - 2.12.1-1 +- Update to 2.12.1 +- Resolves: #2080714 + +* Mon Apr 25 2022 Marek Kasik - 2.12.0-1 +- Update to 2.12.0 +- Resolves: #2070686 + +* Thu Jan 20 2022 Fedora Release Engineering - 2.11.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Sat Dec 4 2021 Diego Herrera - 2.11.1-1 +- Update to 2.11.1 + +* Thu Jul 22 2021 Marek Kasik - 2.11.0-1 +- Update to 2.11.0 + +* Wed Jul 21 2021 Fedora Release Engineering - 2.10.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild * Fri Feb 5 2021 Akira TAGOH - 2.10.4-3 - Enable HarfBuzz support diff --git a/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch b/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch new file mode 100644 index 0000000..1315922 --- /dev/null +++ b/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch @@ -0,0 +1,1873 @@ +diff -pruN fribidi-1.0.14.orig/bin/Makefile.am fribidi-1.0.14/bin/Makefile.am +--- fribidi-1.0.14.orig/bin/Makefile.am 2020-07-06 04:17:23.000000000 +0900 ++++ fribidi-1.0.14/bin/Makefile.am 2024-05-07 21:40:04.500166714 +0900 +@@ -2,11 +2,9 @@ bin_PROGRAMS = fribidi + + noinst_PROGRAMS = fribidi-benchmark fribidi-bidi-types fribidi-caprtl2utf8 + +-getopt_SOURCES = getopt.c getopt1.c getopt_int.h getopt.h gettext.h ++fribidi_SOURCES = fribidi-main.c + +-fribidi_SOURCES = fribidi-main.c $(getopt_SOURCES) +- +-fribidi_benchmark_SOURCES = fribidi-benchmark.c $(getopt_SOURCES) ++fribidi_benchmark_SOURCES = fribidi-benchmark.c + + AM_CPPFLAGS = \ + @FRIBIDI_CPPFLAGS@ \ +diff -pruN fribidi-1.0.14.orig/bin/getopt.c fribidi-1.0.14/bin/getopt.c +--- fribidi-1.0.14.orig/bin/getopt.c 2015-08-05 03:49:07.000000000 +0900 ++++ fribidi-1.0.14/bin/getopt.c 1970-01-01 09:00:00.000000000 +0900 +@@ -1,1268 +0,0 @@ +-/* Getopt for GNU. +- NOTE: getopt is now part of the C library, so if you don't know what +- "Keep this file name-space clean" means, talk to drepper@gnu.org +- before changing it! +- Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004 +- Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2, or (at your option) +- any later version. +- +- This program 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 General Public License for more details. +- +- You should have received a copy of the GNU General Public License along +- with this program; if not, write to the Free Software Foundation, +- Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. */ +- +-/* This tells Alpha OSF/1 not to define a getopt prototype in . +- Ditto for AIX 3.2 and . */ +-#ifndef _NO_PROTO +-# define _NO_PROTO +-#endif +- +-#ifdef HAVE_CONFIG_H +-# include +-#endif +- +-#include +- +-/* Comment out all this code if we are using the GNU C Library, and are not +- actually compiling the library itself. This code is part of the GNU C +- Library, but also included in many other GNU distributions. Compiling +- and linking in this code is a waste when using the GNU C library +- (especially if it is a shared library). Rather than having every GNU +- program understand `configure --with-gnu-libc' and omit the object files, +- it is simpler to just do this in the source for each such file. */ +- +-#define GETOPT_INTERFACE_VERSION 2 +-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +-# include +-# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +-# define ELIDE_CODE +-# endif +-#endif +- +-#ifndef ELIDE_CODE +- +- +-/* This needs to come after some library #include +- to get __GNU_LIBRARY__ defined. */ +-#ifdef __GNU_LIBRARY__ +-/* Don't include stdlib.h for non-GNU C libraries because some of them +- contain conflicting prototypes for getopt. */ +-# include +-# include +-#endif /* GNU C library. */ +- +-#include +- +-#ifdef VMS +-# include +-#endif +- +-#ifdef _LIBC +-# include +-#else +-# include "gettext.h" +-# define _(msgid) gettext (msgid) +-#endif +- +-#if defined _LIBC && defined USE_IN_LIBIO +-# include +-#endif +- +-#ifndef attribute_hidden +-# define attribute_hidden +-#endif +- +-/* This version of `getopt' appears to the caller like standard Unix `getopt' +- but it behaves differently for the user, since it allows the user +- to intersperse the options with the other arguments. +- +- As `getopt' works, it permutes the elements of ARGV so that, +- when it is done, all the options precede everything else. Thus +- all application programs are extended to handle flexible argument order. +- +- Setting the environment variable POSIXLY_CORRECT disables permutation. +- Then the behavior is completely standard. +- +- GNU application programs can use a third alternative mode in which +- they can distinguish the relative order of options and other arguments. */ +- +-#include "getopt.h" +-#include "getopt_int.h" +- +-/* For communication from `getopt' to the caller. +- When `getopt' finds an option that takes an argument, +- the argument value is returned here. +- Also, when `ordering' is RETURN_IN_ORDER, +- each non-option ARGV-element is returned here. */ +- +-char *optarg; +- +-/* Index in ARGV of the next element to be scanned. +- This is used for communication to and from the caller +- and for communication between successive calls to `getopt'. +- +- On entry to `getopt', zero means this is the first call; initialize. +- +- When `getopt' returns -1, this is the index of the first of the +- non-option elements that the caller should itself scan. +- +- Otherwise, `optind' communicates from one call to the next +- how much of ARGV has been scanned so far. */ +- +-/* 1003.2 says this must be 1 before any call. */ +-int optind = 1; +- +-/* Callers store zero here to inhibit the error message +- for unrecognized options. */ +- +-int opterr = 1; +- +-/* Set to an option character which was unrecognized. +- This must be initialized on some systems to avoid linking in the +- system's own getopt implementation. */ +- +-int optopt = '?'; +- +-/* Keep a global copy of all internal members of getopt_data. */ +- +-static struct _getopt_data getopt_data; +- +- +-#ifndef __GNU_LIBRARY__ +- +-/* Avoid depending on library functions or files +- whose names are inconsistent. */ +- +-#ifndef getenv +-extern char *getenv ( +-); +-#endif +- +-#endif /* not __GNU_LIBRARY__ */ +- +-#ifdef _LIBC +-/* Stored original parameters. +- XXX This is no good solution. We should rather copy the args so +- that we can compare them later. But we must not use malloc(3). */ +-extern int __libc_argc; +-extern char **__libc_argv; +- +-/* Bash 2.0 gives us an environment variable containing flags +- indicating ARGV elements that should not be considered arguments. */ +- +-# ifdef USE_NONOPTION_FLAGS +-/* Defined in getopt_init.c */ +-extern char *__getopt_nonoption_flags; +-# endif +- +-# ifdef USE_NONOPTION_FLAGS +-# define SWAP_FLAGS(ch1, ch2) \ +- if (d->__nonoption_flags_len > 0) \ +- { \ +- char __tmp = __getopt_nonoption_flags[ch1]; \ +- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ +- __getopt_nonoption_flags[ch2] = __tmp; \ +- } +-# else +-# define SWAP_FLAGS(ch1, ch2) +-# endif +-#else /* !_LIBC */ +-# define SWAP_FLAGS(ch1, ch2) +-#endif /* _LIBC */ +- +-/* Exchange two adjacent subsequences of ARGV. +- One subsequence is elements [first_nonopt,last_nonopt) +- which contains all the non-options that have been skipped so far. +- The other is elements [last_nonopt,optind), which contains all +- the options processed since those non-options were skipped. +- +- `first_nonopt' and `last_nonopt' are relocated so that they describe +- the new indices of the non-options in ARGV after they are moved. */ +- +-static void +-exchange ( +- char **argv, +- struct _getopt_data *d +-) +-{ +- int bottom = d->__first_nonopt; +- int middle = d->__last_nonopt; +- int top = d->optind; +- char *tem; +- +- /* Exchange the shorter segment with the far end of the longer segment. +- That puts the shorter segment into the right place. +- It leaves the longer segment in the right place overall, +- but it consists of two parts that need to be swapped next. */ +- +-#if defined _LIBC && defined USE_NONOPTION_FLAGS +- /* First make sure the handling of the `__getopt_nonoption_flags' +- string can work normally. Our top argument must be in the range +- of the string. */ +- if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) +- { +- /* We must extend the array. The user plays games with us and +- presents new arguments. */ +- char *new_str = malloc (top + 1); +- if (new_str == NULL) +- d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; +- else +- { +- memset (__mempcpy (new_str, __getopt_nonoption_flags, +- d->__nonoption_flags_max_len), +- '\0', top + 1 - d->__nonoption_flags_max_len); +- d->__nonoption_flags_max_len = top + 1; +- __getopt_nonoption_flags = new_str; +- } +- } +-#endif +- +- while (top > middle && middle > bottom) +- { +- if (top - middle > middle - bottom) +- { +- /* Bottom segment is the short one. */ +- int len = middle - bottom; +- register int i; +- +- /* Swap it with the top part of the top segment. */ +- for (i = 0; i < len; i++) +- { +- tem = argv[bottom + i]; +- argv[bottom + i] = argv[top - (middle - bottom) + i]; +- argv[top - (middle - bottom) + i] = tem; +- SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); +- } +- /* Exclude the moved bottom segment from further swapping. */ +- top -= len; +- } +- else +- { +- /* Top segment is the short one. */ +- int len = top - middle; +- register int i; +- +- /* Swap it with the bottom part of the bottom segment. */ +- for (i = 0; i < len; i++) +- { +- tem = argv[bottom + i]; +- argv[bottom + i] = argv[middle + i]; +- argv[middle + i] = tem; +- SWAP_FLAGS (bottom + i, middle + i); +- } +- /* Exclude the moved top segment from further swapping. */ +- bottom += len; +- } +- } +- +- /* Update records for the slots the non-options now occupy. */ +- +- d->__first_nonopt += (d->optind - d->__last_nonopt); +- d->__last_nonopt = d->optind; +-} +- +-/* Initialize the internal data when the first call is made. */ +- +-static const char * +-_getopt_initialize ( +- int argc, +- char *const *argv, +- const char *optstring, +- struct _getopt_data *d +-) +-{ +- /* Start processing options with ARGV-element 1 (since ARGV-element 0 +- is the program name); the sequence of previously skipped +- non-option ARGV-elements is empty. */ +- +- d->__first_nonopt = d->__last_nonopt = d->optind; +- +- d->__nextchar = NULL; +- +- d->__posixly_correct = !!getenv ("POSIXLY_CORRECT"); +- +- /* Determine how to handle the ordering of options and nonoptions. */ +- +- if (optstring[0] == '-') +- { +- d->__ordering = RETURN_IN_ORDER; +- ++optstring; +- } +- else if (optstring[0] == '+') +- { +- d->__ordering = REQUIRE_ORDER; +- ++optstring; +- } +- else if (d->__posixly_correct) +- d->__ordering = REQUIRE_ORDER; +- else +- d->__ordering = PERMUTE; +- +-#if defined _LIBC && defined USE_NONOPTION_FLAGS +- if (!d->__posixly_correct && argc == __libc_argc && argv == __libc_argv) +- { +- if (d->__nonoption_flags_max_len == 0) +- { +- if (__getopt_nonoption_flags == NULL +- || __getopt_nonoption_flags[0] == '\0') +- d->__nonoption_flags_max_len = -1; +- else +- { +- const char *orig_str = __getopt_nonoption_flags; +- int len = d->__nonoption_flags_max_len = strlen (orig_str); +- if (d->__nonoption_flags_max_len < argc) +- d->__nonoption_flags_max_len = argc; +- __getopt_nonoption_flags = +- (char *) malloc (d->__nonoption_flags_max_len); +- if (__getopt_nonoption_flags == NULL) +- d->__nonoption_flags_max_len = -1; +- else +- memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), +- '\0', d->__nonoption_flags_max_len - len); +- } +- } +- d->__nonoption_flags_len = d->__nonoption_flags_max_len; +- } +- else +- d->__nonoption_flags_len = 0; +-#endif +- +- return optstring; +-} +- +-/* Scan elements of ARGV (whose length is ARGC) for option characters +- given in OPTSTRING. +- +- If an element of ARGV starts with '-', and is not exactly "-" or "--", +- then it is an option element. The characters of this element +- (aside from the initial '-') are option characters. If `getopt' +- is called repeatedly, it returns successively each of the option characters +- from each of the option elements. +- +- If `getopt' finds another option character, it returns that character, +- updating `optind' and `nextchar' so that the next call to `getopt' can +- resume the scan with the following option character or ARGV-element. +- +- If there are no more option characters, `getopt' returns -1. +- Then `optind' is the index in ARGV of the first ARGV-element +- that is not an option. (The ARGV-elements have been permuted +- so that those that are not options now come last.) +- +- OPTSTRING is a string containing the legitimate option characters. +- If an option character is seen that is not listed in OPTSTRING, +- return '?' after printing an error message. If you set `opterr' to +- zero, the error message is suppressed but we still return '?'. +- +- If a char in OPTSTRING is followed by a colon, that means it wants an arg, +- so the following text in the same ARGV-element, or the text of the following +- ARGV-element, is returned in `optarg'. Two colons mean an option that +- wants an optional arg; if there is text in the current ARGV-element, +- it is returned in `optarg', otherwise `optarg' is set to zero. +- +- If OPTSTRING starts with `-' or `+', it requests different methods of +- handling the non-option ARGV-elements. +- See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. +- +- Long-named options begin with `--' instead of `-'. +- Their names may be abbreviated as long as the abbreviation is unique +- or is an exact match for some defined option. If they have an +- argument, it follows the option name in the same ARGV-element, separated +- from the option name by a `=', or else the in next ARGV-element. +- When `getopt' finds a long-named option, it returns 0 if that option's +- `flag' field is nonzero, the value of the option's `val' field +- if the `flag' field is zero. +- +- The elements of ARGV aren't really const, because we permute them. +- But we pretend they're const in the prototype to be compatible +- with other systems. +- +- LONGOPTS is a vector of `struct option' terminated by an +- element containing a name which is zero. +- +- LONGIND returns the index in LONGOPT of the long-named option found. +- It is only valid when a long-named option has been found by the most +- recent call. +- +- If LONG_ONLY is nonzero, '-' as well as '--' can introduce +- long-named options. */ +- +-int +-_getopt_internal_r ( +- int argc, +- char *const *argv, +- const char *optstring, +- const struct option *longopts, +- int *longind, +- int long_only, +- struct _getopt_data *d +-) +-{ +- int print_errors = d->opterr; +- if (optstring[0] == ':') +- print_errors = 0; +- +- if (argc < 1) +- return -1; +- +- d->optarg = NULL; +- +- if (d->optind == 0 || !d->__initialized) +- { +- if (d->optind == 0) +- d->optind = 1; /* Don't scan ARGV[0], the program name. */ +- optstring = _getopt_initialize (argc, argv, optstring, d); +- d->__initialized = 1; +- } +- +- /* Test whether ARGV[optind] points to a non-option argument. +- Either it does not have option syntax, or there is an environment flag +- from the shell indicating it is not an option. The later information +- is only used when the used in the GNU libc. */ +-#if defined _LIBC && defined USE_NONOPTION_FLAGS +-# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ +- || (d->optind < d->__nonoption_flags_len \ +- && __getopt_nonoption_flags[d->optind] == '1')) +-#else +-# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') +-#endif +- +- if (d->__nextchar == NULL || *d->__nextchar == '\0') +- { +- /* Advance to the next ARGV-element. */ +- +- /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been +- moved back by the user (who may also have changed the arguments). */ +- if (d->__last_nonopt > d->optind) +- d->__last_nonopt = d->optind; +- if (d->__first_nonopt > d->optind) +- d->__first_nonopt = d->optind; +- +- if (d->__ordering == PERMUTE) +- { +- /* If we have just processed some options following some non-options, +- exchange them so that the options come first. */ +- +- if (d->__first_nonopt != d->__last_nonopt +- && d->__last_nonopt != d->optind) +- exchange ((char **) argv, d); +- else if (d->__last_nonopt != d->optind) +- d->__first_nonopt = d->optind; +- +- /* Skip any additional non-options +- and extend the range of non-options previously skipped. */ +- +- while (d->optind < argc && NONOPTION_P) +- d->optind++; +- d->__last_nonopt = d->optind; +- } +- +- /* The special ARGV-element `--' means premature end of options. +- Skip it like a null option, +- then exchange with previous non-options as if it were an option, +- then skip everything else like a non-option. */ +- +- if (d->optind != argc && !strcmp (argv[d->optind], "--")) +- { +- d->optind++; +- +- if (d->__first_nonopt != d->__last_nonopt +- && d->__last_nonopt != d->optind) +- exchange ((char **) argv, d); +- else if (d->__first_nonopt == d->__last_nonopt) +- d->__first_nonopt = d->optind; +- d->__last_nonopt = argc; +- +- d->optind = argc; +- } +- +- /* If we have done all the ARGV-elements, stop the scan +- and back over any non-options that we skipped and permuted. */ +- +- if (d->optind == argc) +- { +- /* Set the next-arg-index to point at the non-options +- that we previously skipped, so the caller will digest them. */ +- if (d->__first_nonopt != d->__last_nonopt) +- d->optind = d->__first_nonopt; +- return -1; +- } +- +- /* If we have come to a non-option and did not permute it, +- either stop the scan or describe it to the caller and pass it by. */ +- +- if (NONOPTION_P) +- { +- if (d->__ordering == REQUIRE_ORDER) +- return -1; +- d->optarg = argv[d->optind++]; +- return 1; +- } +- +- /* We have found another option-ARGV-element. +- Skip the initial punctuation. */ +- +- d->__nextchar = (argv[d->optind] + 1 +- + (longopts != NULL && argv[d->optind][1] == '-')); +- } +- +- /* Decode the current option-ARGV-element. */ +- +- /* Check whether the ARGV-element is a long option. +- +- If long_only and the ARGV-element has the form "-f", where f is +- a valid short option, don't consider it an abbreviated form of +- a long option that starts with f. Otherwise there would be no +- way to give the -f short option. +- +- On the other hand, if there's a long option "fubar" and +- the ARGV-element is "-fu", do consider that an abbreviation of +- the long option, just like "--fu", and not "-f" with arg "u". +- +- This distinction seems to be the most useful approach. */ +- +- if (longopts != NULL +- && (argv[d->optind][1] == '-' +- || (long_only && (argv[d->optind][2] +- || !strchr (optstring, argv[d->optind][1]))))) +- { +- char *nameend; +- const struct option *p; +- const struct option *pfound = NULL; +- int exact = 0; +- int ambig = 0; +- int indfound = -1; +- int option_index; +- +- for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) +- /* Do nothing. */ ; +- +- /* Test all long options for either exact match +- or abbreviated matches. */ +- for (p = longopts, option_index = 0; p->name; p++, option_index++) +- if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) +- { +- if ((unsigned int) (nameend - d->__nextchar) +- == (unsigned int) strlen (p->name)) +- { +- /* Exact match found. */ +- pfound = p; +- indfound = option_index; +- exact = 1; +- break; +- } +- else if (pfound == NULL) +- { +- /* First nonexact match found. */ +- pfound = p; +- indfound = option_index; +- } +- else if (long_only +- || pfound->has_arg != p->has_arg +- || pfound->flag != p->flag || pfound->val != p->val) +- /* Second or later nonexact match found. */ +- ambig = 1; +- } +- +- if (ambig && !exact) +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), +- argv[0], argv[d->optind]) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, _("%s: option `%s' is ambiguous\n"), +- argv[0], argv[d->optind]); +-#endif +- } +- d->__nextchar += strlen (d->__nextchar); +- d->optind++; +- d->optopt = 0; +- return '?'; +- } +- +- if (pfound != NULL) +- { +- option_index = indfound; +- d->optind++; +- if (*nameend) +- { +- /* Don't test has_arg with >, because some C compilers don't +- allow it to be used on enums. */ +- if (pfound->has_arg) +- d->optarg = nameend + 1; +- else +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- int n; +-#endif +- +- if (argv[d->optind - 1][1] == '-') +- { +- /* --option */ +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("\ +-%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); +-#else +- fprintf (stderr, _("\ +-%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); +-#endif +- } +- else +- { +- /* +option or -option */ +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("\ +-%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[d->optind - 1][0], pfound->name); +-#else +- fprintf (stderr, _("\ +-%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[d->optind - 1][0], pfound->name); +-#endif +- } +- +-#if defined _LIBC && defined USE_IN_LIBIO +- if (n >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 +- |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#endif +- } +- +- d->__nextchar += strlen (d->__nextchar); +- +- d->optopt = pfound->val; +- return '?'; +- } +- } +- else if (pfound->has_arg == 1) +- { +- if (d->optind < argc) +- d->optarg = argv[d->optind++]; +- else +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("\ +-%s: option `%s' requires an argument\n"), argv[0], argv[d->optind - 1]) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 +- |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, +- _("%s: option `%s' requires an argument\n"), +- argv[0], argv[d->optind - 1]); +-#endif +- } +- d->__nextchar += strlen (d->__nextchar); +- d->optopt = pfound->val; +- return optstring[0] == ':' ? ':' : '?'; +- } +- } +- d->__nextchar += strlen (d->__nextchar); +- if (longind != NULL) +- *longind = option_index; +- if (pfound->flag) +- { +- *(pfound->flag) = pfound->val; +- return 0; +- } +- return pfound->val; +- } +- +- /* Can't find it as a long option. If this is not getopt_long_only, +- or the option starts with '--' or is not a valid short +- option, then it's an error. +- Otherwise interpret it as a short option. */ +- if (!long_only || argv[d->optind][1] == '-' +- || strchr (optstring, *d->__nextchar) == NULL) +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- int n; +-#endif +- +- if (argv[d->optind][1] == '-') +- { +- /* --option */ +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), +- argv[0], d->__nextchar); +-#else +- fprintf (stderr, _("%s: unrecognized option `--%s'\n"), +- argv[0], d->__nextchar); +-#endif +- } +- else +- { +- /* +option or -option */ +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), +- argv[0], argv[d->optind][0], d->__nextchar); +-#else +- fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), +- argv[0], argv[d->optind][0], d->__nextchar); +-#endif +- } +- +-#if defined _LIBC && defined USE_IN_LIBIO +- if (n >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#endif +- } +- d->__nextchar = (char *) ""; +- d->optind++; +- d->optopt = 0; +- return '?'; +- } +- } +- +- /* Look at and handle the next short option-character. */ +- +- { +- char c = *d->__nextchar++; +- char *temp = strchr (optstring, c); +- +- /* Increment `optind' when we start to process its last character. */ +- if (*d->__nextchar == '\0') +- ++d->optind; +- +- if (temp == NULL || c == ':') +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- int n; +-#endif +- +- if (d->__posixly_correct) +- { +- /* 1003.2 specifies the format of this message. */ +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("%s: illegal option -- %c\n"), +- argv[0], c); +-#else +- fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); +-#endif +- } +- else +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- n = __asprintf (&buf, _("%s: invalid option -- %c\n"), +- argv[0], c); +-#else +- fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); +-#endif +- } +- +-#if defined _LIBC && defined USE_IN_LIBIO +- if (n >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#endif +- } +- d->optopt = c; +- return '?'; +- } +- /* Convenience. Treat POSIX -W foo same as long option --foo */ +- if (temp[0] == 'W' && temp[1] == ';') +- { +- char *nameend; +- const struct option *p; +- const struct option *pfound = NULL; +- int exact = 0; +- int ambig = 0; +- int indfound = 0; +- int option_index; +- +- /* This is an option that requires an argument. */ +- if (*d->__nextchar != '\0') +- { +- d->optarg = d->__nextchar; +- /* If we end this ARGV-element by taking the rest as an arg, +- we must advance to the next element now. */ +- d->optind++; +- } +- else if (d->optind == argc) +- { +- if (print_errors) +- { +- /* 1003.2 specifies the format of this message. */ +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, +- _("%s: option requires an argument -- %c\n"), +- argv[0], c) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, _("%s: option requires an argument -- %c\n"), +- argv[0], c); +-#endif +- } +- d->optopt = c; +- if (optstring[0] == ':') +- c = ':'; +- else +- c = '?'; +- return c; +- } +- else +- /* We already incremented `d->optind' once; +- increment it again when taking next ARGV-elt as argument. */ +- d->optarg = argv[d->optind++]; +- +- /* optarg is now the argument, see if it's in the +- table of longopts. */ +- +- for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; +- nameend++) +- /* Do nothing. */ ; +- +- /* Test all long options for either exact match +- or abbreviated matches. */ +- for (p = longopts, option_index = 0; p->name; p++, option_index++) +- if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) +- { +- if ((unsigned int) (nameend - d->__nextchar) == +- strlen (p->name)) +- { +- /* Exact match found. */ +- pfound = p; +- indfound = option_index; +- exact = 1; +- break; +- } +- else if (pfound == NULL) +- { +- /* First nonexact match found. */ +- pfound = p; +- indfound = option_index; +- } +- else +- /* Second or later nonexact match found. */ +- ambig = 1; +- } +- if (ambig && !exact) +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), +- argv[0], argv[d->optind]) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), +- argv[0], argv[d->optind]); +-#endif +- } +- d->__nextchar += strlen (d->__nextchar); +- d->optind++; +- return '?'; +- } +- if (pfound != NULL) +- { +- option_index = indfound; +- if (*nameend) +- { +- /* Don't test has_arg with >, because some C compilers don't +- allow it to be used on enums. */ +- if (pfound->has_arg) +- d->optarg = nameend + 1; +- else +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("\ +-%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 +- |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, _("\ +-%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); +-#endif +- } +- +- d->__nextchar += strlen (d->__nextchar); +- return '?'; +- } +- } +- else if (pfound->has_arg == 1) +- { +- if (d->optind < argc) +- d->optarg = argv[d->optind++]; +- else +- { +- if (print_errors) +- { +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("\ +-%s: option `%s' requires an argument\n"), argv[0], argv[d->optind - 1]) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 +- |= _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, +- _("%s: option `%s' requires an argument\n"), +- argv[0], argv[d->optind - 1]); +-#endif +- } +- d->__nextchar += strlen (d->__nextchar); +- return optstring[0] == ':' ? ':' : '?'; +- } +- } +- d->__nextchar += strlen (d->__nextchar); +- if (longind != NULL) +- *longind = option_index; +- if (pfound->flag) +- { +- *(pfound->flag) = pfound->val; +- return 0; +- } +- return pfound->val; +- } +- d->__nextchar = NULL; +- return 'W'; /* Let the application handle it. */ +- } +- if (temp[1] == ':') +- { +- if (temp[2] == ':') +- { +- /* This is an option that accepts an argument optionally. */ +- if (*d->__nextchar != '\0') +- { +- d->optarg = d->__nextchar; +- d->optind++; +- } +- else +- d->optarg = NULL; +- d->__nextchar = NULL; +- } +- else +- { +- /* This is an option that requires an argument. */ +- if (*d->__nextchar != '\0') +- { +- d->optarg = d->__nextchar; +- /* If we end this ARGV-element by taking the rest as an arg, +- we must advance to the next element now. */ +- d->optind++; +- } +- else if (d->optind == argc) +- { +- if (print_errors) +- { +- /* 1003.2 specifies the format of this message. */ +-#if defined _LIBC && defined USE_IN_LIBIO +- char *buf; +- +- if (__asprintf (&buf, _("\ +-%s: option requires an argument -- %c\n"), argv[0], c) >= 0) +- { +- _IO_flockfile (stderr); +- +- int old_flags2 = ((_IO_FILE *) stderr)->_flags2; +- ((_IO_FILE *) stderr)->_flags2 |= +- _IO_FLAGS2_NOTCANCEL; +- +- if (_IO_fwide (stderr, 0) > 0) +- __fwprintf (stderr, L"%s", buf); +- else +- fputs (buf, stderr); +- +- ((_IO_FILE *) stderr)->_flags2 = old_flags2; +- _IO_funlockfile (stderr); +- +- free (buf); +- } +-#else +- fprintf (stderr, +- _("%s: option requires an argument -- %c\n"), +- argv[0], c); +-#endif +- } +- d->optopt = c; +- if (optstring[0] == ':') +- c = ':'; +- else +- c = '?'; +- } +- else +- /* We already incremented `optind' once; +- increment it again when taking next ARGV-elt as argument. */ +- d->optarg = argv[d->optind++]; +- d->__nextchar = NULL; +- } +- } +- return c; +- } +-} +- +-int +-_getopt_internal ( +- int argc, +- char *const *argv, +- const char *optstring, +- const struct option *longopts, +- int *longind, +- int long_only +-) +-{ +- int result; +- +- getopt_data.optind = optind; +- getopt_data.opterr = opterr; +- +- result = _getopt_internal_r (argc, argv, optstring, longopts, +- longind, long_only, &getopt_data); +- +- optind = getopt_data.optind; +- optarg = getopt_data.optarg; +- optopt = getopt_data.optopt; +- +- return result; +-} +- +-int +-getopt ( +- int argc, +- char *const *argv, +- const char *optstring +-) +-{ +- return _getopt_internal (argc, argv, optstring, +- (const struct option *) 0, (int *) 0, 0); +-} +- +-#endif /* Not ELIDE_CODE. */ +- +-#ifdef TEST +- +-/* Compile with -DTEST to make an executable for use in testing +- the above definition of `getopt'. */ +- +-int +-main ( +- int argc, +- char **argv +-) +-{ +- int c; +- int digit_optind = 0; +- +- while (1) +- { +- int this_option_optind = optind ? optind : 1; +- +- c = getopt (argc, argv, "abc:d:0123456789"); +- if (c == -1) +- break; +- +- switch (c) +- { +- case '0': +- case '1': +- case '2': +- case '3': +- case '4': +- case '5': +- case '6': +- case '7': +- case '8': +- case '9': +- if (digit_optind != 0 && digit_optind != this_option_optind) +- printf ("digits occur in two different argv-elements.\n"); +- digit_optind = this_option_optind; +- printf ("option %c\n", c); +- break; +- +- case 'a': +- printf ("option a\n"); +- break; +- +- case 'b': +- printf ("option b\n"); +- break; +- +- case 'c': +- printf ("option c with value `%s'\n", optarg); +- break; +- +- case '?': +- break; +- +- default: +- printf ("?? getopt returned character code 0%o ??\n", c); +- } +- } +- +- if (optind < argc) +- { +- printf ("non-option ARGV-elements: "); +- while (optind < argc) +- printf ("%s ", argv[optind++]); +- printf ("\n"); +- } +- +- exit (0); +-} +- +-#endif /* TEST */ +diff -pruN fribidi-1.0.14.orig/bin/getopt.h fribidi-1.0.14/bin/getopt.h +--- fribidi-1.0.14.orig/bin/getopt.h 2015-08-05 03:49:07.000000000 +0900 ++++ fribidi-1.0.14/bin/getopt.h 1970-01-01 09:00:00.000000000 +0900 +@@ -1,187 +0,0 @@ +-/* Declarations for getopt. +- Copyright (C) 1989-1994,1996-1999,2001,2003,2004 +- Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2, or (at your option) +- any later version. +- +- This program 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 General Public License for more details. +- +- You should have received a copy of the GNU General Public License along +- with this program; if not, write to the Free Software Foundation, +- Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. */ +- +-#ifndef _GETOPT_H +- +-#ifndef __need_getopt +-# define _GETOPT_H 1 +-#endif +- +-/* If __GNU_LIBRARY__ is not already defined, either we are being used +- standalone, or this is the first header included in the source file. +- If we are being used with glibc, we need to include , but +- that does not exist if we are standalone. So: if __GNU_LIBRARY__ is +- not defined, include , which will pull in for us +- if it's from glibc. (Why ctype.h? It's guaranteed to exist and it +- doesn't flood the namespace with stuff the way some other headers do.) */ +-#if !defined __GNU_LIBRARY__ +-# include +-#endif +- +-#ifndef __THROW +-# ifndef __GNUC_PREREQ +-# define __GNUC_PREREQ(maj, min) (0) +-# endif +-# if defined __cplusplus && __GNUC_PREREQ (2,8) +-# define __THROW throw () +-# else +-# define __THROW +-# endif +-#endif +- +-#ifdef __cplusplus +-extern "C" +-{ +-#endif +- +-/* For communication from `getopt' to the caller. +- When `getopt' finds an option that takes an argument, +- the argument value is returned here. +- Also, when `ordering' is RETURN_IN_ORDER, +- each non-option ARGV-element is returned here. */ +- +- extern char *optarg; +- +-/* Index in ARGV of the next element to be scanned. +- This is used for communication to and from the caller +- and for communication between successive calls to `getopt'. +- +- On entry to `getopt', zero means this is the first call; initialize. +- +- When `getopt' returns -1, this is the index of the first of the +- non-option elements that the caller should itself scan. +- +- Otherwise, `optind' communicates from one call to the next +- how much of ARGV has been scanned so far. */ +- +- extern int optind; +- +-/* Callers store zero here to inhibit the error message `getopt' prints +- for unrecognized options. */ +- +- extern int opterr; +- +-/* Set to an option character which was unrecognized. */ +- +- extern int optopt; +- +-#ifndef __need_getopt +-/* Describe the long-named options requested by the application. +- The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector +- of `struct option' terminated by an element containing a name which is +- zero. +- +- The field `has_arg' is: +- no_argument (or 0) if the option does not take an argument, +- required_argument (or 1) if the option requires an argument, +- optional_argument (or 2) if the option takes an optional argument. +- +- If the field `flag' is not NULL, it points to a variable that is set +- to the value given in the field `val' when the option is found, but +- left unchanged if the option is not found. +- +- To have a long-named option do something other than set an `int' to +- a compiled-in constant, such as set a value from `optarg', set the +- option's `flag' field to zero and its `val' field to a nonzero +- value (the equivalent single-letter option character, if there is +- one). For long options that have a zero `flag' field, `getopt' +- returns the contents of the `val' field. */ +- +- struct option +- { +- const char *name; +- /* has_arg can't be an enum because some compilers complain about +- type mismatches in all the code that assumes it is an int. */ +- int has_arg; +- int *flag; +- int val; +- }; +- +-/* Names for the values of the `has_arg' field of `struct option'. */ +- +-# define no_argument 0 +-# define required_argument 1 +-# define optional_argument 2 +-#endif /* need getopt */ +- +- +-/* Get definitions and prototypes for functions to process the +- arguments in ARGV (ARGC of them, minus the program name) for +- options given in OPTS. +- +- Return the option character from OPTS just read. Return -1 when +- there are no more options. For unrecognized options, or options +- missing arguments, `optopt' is set to the option letter, and '?' is +- returned. +- +- The OPTS string is a list of characters which are recognized option +- letters, optionally followed by colons, specifying that that letter +- takes an argument, to be placed in `optarg'. +- +- If a letter in OPTS is followed by two colons, its argument is +- optional. This behavior is specific to the GNU `getopt'. +- +- The argument `--' causes premature termination of argument +- scanning, explicitly telling `getopt' that there are no more +- options. +- +- If OPTS begins with `--', then non-option arguments are treated as +- arguments to the option '\0'. This behavior is specific to the GNU +- `getopt'. */ +- +-#ifdef __GNU_LIBRARY__ +-/* Many other libraries have conflicting prototypes for getopt, with +- differences in the consts, in stdlib.h. To avoid compilation +- errors, only prototype getopt for the GNU C library. */ +- extern int getopt ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts +- ) __THROW; +-#else /* not __GNU_LIBRARY__ */ +- extern int getopt ( +- ); +-#endif /* __GNU_LIBRARY__ */ +- +-#ifndef __need_getopt +- extern int getopt_long ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind +- ) __THROW; +- extern int getopt_long_only ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind +- ) __THROW; +- +-#endif +- +-#ifdef __cplusplus +-} +-#endif +- +-/* Make sure we later can get all the definitions and declarations. */ +-#undef __need_getopt +- +-#endif /* getopt.h */ +diff -pruN fribidi-1.0.14.orig/bin/getopt1.c fribidi-1.0.14/bin/getopt1.c +--- fribidi-1.0.14.orig/bin/getopt1.c 2015-08-05 03:49:07.000000000 +0900 ++++ fribidi-1.0.14/bin/getopt1.c 1970-01-01 09:00:00.000000000 +0900 +@@ -1,213 +0,0 @@ +-/* getopt_long and getopt_long_only entry points for GNU getopt. +- Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004 +- Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2, or (at your option) +- any later version. +- +- This program 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 General Public License for more details. +- +- You should have received a copy of the GNU General Public License along +- with this program; if not, write to the Free Software Foundation, +- Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. */ +- +-#ifdef HAVE_CONFIG_H +-#include +-#endif +- +-#ifdef _LIBC +-# include +-#else +-# include "getopt.h" +-#endif +-#include "getopt_int.h" +- +-#include +- +-/* Comment out all this code if we are using the GNU C Library, and are not +- actually compiling the library itself. This code is part of the GNU C +- Library, but also included in many other GNU distributions. Compiling +- and linking in this code is a waste when using the GNU C library +- (especially if it is a shared library). Rather than having every GNU +- program understand `configure --with-gnu-libc' and omit the object files, +- it is simpler to just do this in the source for each such file. */ +- +-#define GETOPT_INTERFACE_VERSION 2 +-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +-#include +-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +-#define ELIDE_CODE +-#endif +-#endif +- +-#ifndef ELIDE_CODE +- +- +-/* This needs to come after some library #include +- to get __GNU_LIBRARY__ defined. */ +-#ifdef __GNU_LIBRARY__ +-#include +-#endif +- +-#ifndef NULL +-#define NULL 0 +-#endif +- +-int +-getopt_long ( +- int argc, +- char *const *argv, +- const char *options, +- const struct option *long_options, +- int *opt_index +-) +-{ +- return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +-} +- +-int +-_getopt_long_r ( +- int argc, +- char *const *argv, +- const char *options, +- const struct option *long_options, +- int *opt_index, +- struct _getopt_data *d +-) +-{ +- return _getopt_internal_r (argc, argv, options, long_options, opt_index, +- 0, d); +-} +- +-/* Like getopt_long, but '-' as well as '--' can indicate a long option. +- If an option that starts with '-' (not '--') doesn't match a long option, +- but does match a short option, it is parsed as a short option +- instead. */ +- +-int +-getopt_long_only ( +- int argc, +- char *const *argv, +- const char *options, +- const struct option *long_options, +- int *opt_index +-) +-{ +- return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +-} +- +-int +-_getopt_long_only_r ( +- int argc, +- char *const *argv, +- const char *options, +- const struct option *long_options, +- int *opt_index, +- struct _getopt_data *d +-) +-{ +- return _getopt_internal_r (argc, argv, options, long_options, opt_index, +- 1, d); +-} +- +-#endif /* Not ELIDE_CODE. */ +- +-#ifdef TEST +- +-#include +- +-int +-main ( +- int argc, +- char **argv +-) +-{ +- int c; +- int digit_optind = 0; +- +- while (1) +- { +- int this_option_optind = optind ? optind : 1; +- int option_index = 0; +- static struct option long_options[] = { +- {"add", 1, 0, 0}, +- {"append", 0, 0, 0}, +- {"delete", 1, 0, 0}, +- {"verbose", 0, 0, 0}, +- {"create", 0, 0, 0}, +- {"file", 1, 0, 0}, +- {0, 0, 0, 0} +- }; +- +- c = getopt_long (argc, argv, "abc:d:0123456789", +- long_options, &option_index); +- if (c == -1) +- break; +- +- switch (c) +- { +- case 0: +- printf ("option %s", long_options[option_index].name); +- if (optarg) +- printf (" with arg %s", optarg); +- printf ("\n"); +- break; +- +- case '0': +- case '1': +- case '2': +- case '3': +- case '4': +- case '5': +- case '6': +- case '7': +- case '8': +- case '9': +- if (digit_optind != 0 && digit_optind != this_option_optind) +- printf ("digits occur in two different argv-elements.\n"); +- digit_optind = this_option_optind; +- printf ("option %c\n", c); +- break; +- +- case 'a': +- printf ("option a\n"); +- break; +- +- case 'b': +- printf ("option b\n"); +- break; +- +- case 'c': +- printf ("option c with value `%s'\n", optarg); +- break; +- +- case 'd': +- printf ("option d with value `%s'\n", optarg); +- break; +- +- case '?': +- break; +- +- default: +- printf ("?? getopt returned character code 0%o ??\n", c); +- } +- } +- +- if (optind < argc) +- { +- printf ("non-option ARGV-elements: "); +- while (optind < argc) +- printf ("%s ", argv[optind++]); +- printf ("\n"); +- } +- +- exit (0); +-} +- +-#endif /* TEST */ +diff -pruN fribidi-1.0.14.orig/bin/getopt_int.h fribidi-1.0.14/bin/getopt_int.h +--- fribidi-1.0.14.orig/bin/getopt_int.h 2015-08-05 03:49:07.000000000 +0900 ++++ fribidi-1.0.14/bin/getopt_int.h 1970-01-01 09:00:00.000000000 +0900 +@@ -1,145 +0,0 @@ +-/* Internal declarations for getopt. +- Copyright (C) 1989-1994,1996-1999,2001,2003,2004 +- Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2, or (at your option) +- any later version. +- +- This program 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 General Public License for more details. +- +- You should have received a copy of the GNU General Public License along +- with this program; if not, write to the Free Software Foundation, +- Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA. */ +- +-#ifndef _GETOPT_INT_H +-#define _GETOPT_INT_H 1 +- +-extern int _getopt_internal ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind, +- int __long_only +-); +- +- +-/* Reentrant versions which can handle parsing multiple argument +- vectors at the same time. */ +- +-/* Data type for reentrant functions. */ +-struct _getopt_data +-{ +- /* These have exactly the same meaning as the corresponding global +- variables, except that they are used for the reentrant +- versions of getopt. */ +- int optind; +- int opterr; +- int optopt; +- char *optarg; +- +- /* Internal members. */ +- +- /* True if the internal members have been initialized. */ +- int __initialized; +- +- /* The next char to be scanned in the option-element +- in which the last option character we returned was found. +- This allows us to pick up the scan where we left off. +- +- If this is zero, or a null string, it means resume the scan +- by advancing to the next ARGV-element. */ +- char *__nextchar; +- +- /* Describe how to deal with options that follow non-option ARGV-elements. +- +- If the caller did not specify anything, +- the default is REQUIRE_ORDER if the environment variable +- POSIXLY_CORRECT is defined, PERMUTE otherwise. +- +- REQUIRE_ORDER means don't recognize them as options; +- stop option processing when the first non-option is seen. +- This is what Unix does. +- This mode of operation is selected by either setting the environment +- variable POSIXLY_CORRECT, or using `+' as the first character +- of the list of option characters. +- +- PERMUTE is the default. We permute the contents of ARGV as we +- scan, so that eventually all the non-options are at the end. +- This allows options to be given in any order, even with programs +- that were not written to expect this. +- +- RETURN_IN_ORDER is an option available to programs that were +- written to expect options and other ARGV-elements in any order +- and that care about the ordering of the two. We describe each +- non-option ARGV-element as if it were the argument of an option +- with character code 1. Using `-' as the first character of the +- list of option characters selects this mode of operation. +- +- The special argument `--' forces an end of option-scanning regardless +- of the value of `ordering'. In the case of RETURN_IN_ORDER, only +- `--' can cause `getopt' to return -1 with `optind' != ARGC. */ +- +- enum +- { +- REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +- } __ordering; +- +- /* If the POSIXLY_CORRECT environment variable is set. */ +- int __posixly_correct; +- +- +- /* Handle permutation of arguments. */ +- +- /* Describe the part of ARGV that contains non-options that have +- been skipped. `first_nonopt' is the index in ARGV of the first +- of them; `last_nonopt' is the index after the last of them. */ +- +- int __first_nonopt; +- int __last_nonopt; +- +-#if defined _LIBC && defined USE_NONOPTION_FLAGS +- int __nonoption_flags_max_len; +- int __nonoption_flags_len; +-# endif +-}; +- +-/* The initializer is necessary to set OPTIND and OPTERR to their +- default values and to clear the initialization flag. */ +-#define _GETOPT_DATA_INITIALIZER { 1, 1 } +- +-extern int _getopt_internal_r ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind, +- int __long_only, +- struct _getopt_data *__data +-); +- +-extern int _getopt_long_r ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind, +- struct _getopt_data *__data +-); +- +-extern int _getopt_long_only_r ( +- int ___argc, +- char *const *___argv, +- const char *__shortopts, +- const struct option *__longopts, +- int *__longind, +- struct _getopt_data *__data +-); +- +-#endif /* getopt_int.h */ +diff -pruN fribidi-1.0.14.orig/bin/gettext.h fribidi-1.0.14/bin/gettext.h +--- fribidi-1.0.14.orig/bin/gettext.h 2015-08-05 03:49:07.000000000 +0900 ++++ fribidi-1.0.14/bin/gettext.h 1970-01-01 09:00:00.000000000 +0900 +@@ -1,2 +0,0 @@ +-#undef gettext +-#define gettext +diff -pruN fribidi-1.0.14.orig/bin/meson.build fribidi-1.0.14/bin/meson.build +--- fribidi-1.0.14.orig/bin/meson.build 2024-03-18 03:10:09.000000000 +0900 ++++ fribidi-1.0.14/bin/meson.build 2024-05-07 21:40:55.573086578 +0900 +@@ -1,7 +1,7 @@ + # The fribidi binary is used by the test setup, so if bin=false we still + # need to build it for internal usage, we just won't install it. + fribidi = executable('fribidi', +- 'fribidi-main.c', 'getopt.c', 'getopt1.c', fribidi_unicode_version_h, ++ 'fribidi-main.c', fribidi_unicode_version_h, + c_args: ['-DHAVE_CONFIG_H'] + fribidi_static_cargs, + include_directories: incs, + link_with: libfribidi, +@@ -12,7 +12,7 @@ if not get_option('bin') + endif + + executable('fribidi-benchmark', +- 'fribidi-benchmark.c', 'getopt.c', 'getopt1.c', fribidi_unicode_version_h, ++ 'fribidi-benchmark.c', fribidi_unicode_version_h, + c_args: ['-DHAVE_CONFIG_H'] + fribidi_static_cargs, + include_directories: incs, + link_with: libfribidi, diff --git a/base/rx/rx-fribidi/fribidi.spec b/base/rx/rx-fribidi/fribidi.spec new file mode 100644 index 0000000..d384381 --- /dev/null +++ b/base/rx/rx-fribidi/fribidi.spec @@ -0,0 +1,60 @@ +%global realname fribidi +%global _prefix /opt/rx + +Summary: Library implementing the Unicode Bidirectional Algorithm +Name: rx-fribidi +Version: 1.0.14 +Release: 1%{?dist} +URL: https://github.com/fribidi/fribidi/ +Source: https://github.com/%{realname}/%{realname}/releases/download/v%{version}/%{realname}-%{version}.tar.xz +License: LGPL-2.1-or-later AND Unicode-DFS-2016 +BuildRequires: gcc +BuildRequires: meson +BuildRequires: make +Patch0: fribidi-drop-bundled-gnulib.patch + +%description +A library to handle bidirectional scripts (for example Hebrew, Arabic), +so that the display is done in the proper way; while the text data itself +is always written in logical order. + +%package devel +Summary: Libraries and include files for FriBidi +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +Include files and libraries needed for developing applications which use +FriBidi. + +%prep +%autosetup -p1 -n %{realname}-%{version} + +%build +export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} +export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} $LDFLAGS" + +%meson -Ddocs=false +%meson_build + +%check +%meson_test + + +%install +%meson_install + +rm -f $RPM_BUILD_ROOT%{_libdir}/*.la + +%ldconfig_scriptlets + +%files +%doc README AUTHORS ChangeLog THANKS NEWS TODO +%license COPYING +%{_bindir}/fribidi +%{_libdir}/libfribidi.so.0* + +%files devel +%{_includedir}/fribidi +%{_libdir}/libfribidi.so +%{_libdir}/pkgconfig/*.pc +#%%{_mandir}/man3/*.gz \ No newline at end of file diff --git a/base/rx/rx-glib2/1965.patch b/base/rx/rx-glib2/1965.patch new file mode 100644 index 0000000..59d7cb8 --- /dev/null +++ b/base/rx/rx-glib2/1965.patch @@ -0,0 +1,222 @@ +From 1248b642ad32b0bdf296211c1a0a8817bebf1c66 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 25 Feb 2021 10:35:36 +0000 +Subject: [PATCH 1/2] gversionmacros: Add version macros for GLib 2.70 + +Signed-off-by: Simon McVittie +--- + docs/reference/gio/gio-docs.xml | 4 +++ + docs/reference/glib/glib-docs.xml | 4 +++ + docs/reference/glib/glib-sections.txt | 14 ++++++++ + docs/reference/gobject/gobject-docs.xml | 4 +++ + docs/reference/meson.build | 2 +- + glib/gversionmacros.h | 44 +++++++++++++++++++++++++ + 6 files changed, 71 insertions(+), 1 deletion(-) + +diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml +index 9cd3d0e39..a09d6d31d 100644 +--- a/docs/reference/gio/gio-docs.xml ++++ b/docs/reference/gio/gio-docs.xml +@@ -389,6 +389,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml +index e464fb792..2f5de9e31 100644 +--- a/docs/reference/glib/glib-docs.xml ++++ b/docs/reference/glib/glib-docs.xml +@@ -288,6 +288,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt +index 460a299bf..75994e889 100644 +--- a/docs/reference/glib/glib-sections.txt ++++ b/docs/reference/glib/glib-sections.txt +@@ -138,6 +138,7 @@ GLIB_VERSION_2_62 + GLIB_VERSION_2_64 + GLIB_VERSION_2_66 + GLIB_VERSION_2_68 ++GLIB_VERSION_2_70 + GLIB_VERSION_CUR_STABLE + GLIB_VERSION_PREV_STABLE + GLIB_VERSION_MIN_REQUIRED +@@ -168,6 +169,7 @@ GLIB_AVAILABLE_ENUMERATOR_IN_2_62 + GLIB_AVAILABLE_ENUMERATOR_IN_2_64 + GLIB_AVAILABLE_ENUMERATOR_IN_2_66 + GLIB_AVAILABLE_ENUMERATOR_IN_2_68 ++GLIB_AVAILABLE_ENUMERATOR_IN_2_70 + GLIB_AVAILABLE_IN_ALL + GLIB_AVAILABLE_IN_2_26 + GLIB_AVAILABLE_IN_2_28 +@@ -191,6 +193,7 @@ GLIB_AVAILABLE_IN_2_62 + GLIB_AVAILABLE_IN_2_64 + GLIB_AVAILABLE_IN_2_66 + GLIB_AVAILABLE_IN_2_68 ++GLIB_AVAILABLE_IN_2_70 + GLIB_AVAILABLE_MACRO_IN_2_26 + GLIB_AVAILABLE_MACRO_IN_2_28 + GLIB_AVAILABLE_MACRO_IN_2_30 +@@ -213,12 +216,14 @@ GLIB_AVAILABLE_MACRO_IN_2_62 + GLIB_AVAILABLE_MACRO_IN_2_64 + GLIB_AVAILABLE_MACRO_IN_2_66 + GLIB_AVAILABLE_MACRO_IN_2_68 ++GLIB_AVAILABLE_MACRO_IN_2_70 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_44 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_60 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_62 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_64 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_66 + GLIB_AVAILABLE_STATIC_INLINE_IN_2_68 ++GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 + GLIB_AVAILABLE_TYPE_IN_2_26 + GLIB_AVAILABLE_TYPE_IN_2_28 + GLIB_AVAILABLE_TYPE_IN_2_30 +@@ -241,6 +246,7 @@ GLIB_AVAILABLE_TYPE_IN_2_62 + GLIB_AVAILABLE_TYPE_IN_2_64 + GLIB_AVAILABLE_TYPE_IN_2_66 + GLIB_AVAILABLE_TYPE_IN_2_68 ++GLIB_AVAILABLE_TYPE_IN_2_70 + GLIB_DEPRECATED_ENUMERATOR + GLIB_DEPRECATED_ENUMERATOR_FOR + GLIB_DEPRECATED_ENUMERATOR_IN_2_26 +@@ -287,6 +293,8 @@ GLIB_DEPRECATED_ENUMERATOR_IN_2_66 + GLIB_DEPRECATED_ENUMERATOR_IN_2_66_FOR + GLIB_DEPRECATED_ENUMERATOR_IN_2_68 + GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR ++GLIB_DEPRECATED_ENUMERATOR_IN_2_70 ++GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR + GLIB_DEPRECATED_IN_2_26 + GLIB_DEPRECATED_IN_2_26_FOR + GLIB_DEPRECATED_IN_2_28 +@@ -331,6 +339,8 @@ GLIB_DEPRECATED_IN_2_66 + GLIB_DEPRECATED_IN_2_66_FOR + GLIB_DEPRECATED_IN_2_68 + GLIB_DEPRECATED_IN_2_68_FOR ++GLIB_DEPRECATED_IN_2_70 ++GLIB_DEPRECATED_IN_2_70_FOR + GLIB_DEPRECATED_MACRO + GLIB_DEPRECATED_MACRO_FOR + GLIB_DEPRECATED_MACRO_IN_2_26 +@@ -377,6 +387,8 @@ GLIB_DEPRECATED_MACRO_IN_2_66 + GLIB_DEPRECATED_MACRO_IN_2_66_FOR + GLIB_DEPRECATED_MACRO_IN_2_68 + GLIB_DEPRECATED_MACRO_IN_2_68_FOR ++GLIB_DEPRECATED_MACRO_IN_2_70 ++GLIB_DEPRECATED_MACRO_IN_2_70_FOR + GLIB_DEPRECATED_TYPE + GLIB_DEPRECATED_TYPE_FOR + GLIB_DEPRECATED_TYPE_IN_2_26 +@@ -423,6 +435,8 @@ GLIB_DEPRECATED_TYPE_IN_2_66 + GLIB_DEPRECATED_TYPE_IN_2_66_FOR + GLIB_DEPRECATED_TYPE_IN_2_68 + GLIB_DEPRECATED_TYPE_IN_2_68_FOR ++GLIB_DEPRECATED_TYPE_IN_2_70 ++GLIB_DEPRECATED_TYPE_IN_2_70_FOR + GLIB_VERSION_CUR_STABLE + GLIB_VERSION_PREV_STABLE + +diff --git a/docs/reference/gobject/gobject-docs.xml b/docs/reference/gobject/gobject-docs.xml +index ddbc9f274..e8e7c76d9 100644 +--- a/docs/reference/gobject/gobject-docs.xml ++++ b/docs/reference/gobject/gobject-docs.xml +@@ -208,6 +208,10 @@ + Index of new symbols in 2.68 + + ++ ++ Index of new symbols in 2.70 ++ ++ + + + +diff --git a/docs/reference/meson.build b/docs/reference/meson.build +index 3f09be555..53ca12ff8 100644 +--- a/docs/reference/meson.build ++++ b/docs/reference/meson.build +@@ -7,7 +7,7 @@ + stable_2_series_versions = [ + '26', '28', '30', '32', '34', '36', '38', + '40', '42', '44', '46', '48', '50', '52', '54', '56', '58', +- '60', '62', '64', '66', '68', ++ '60', '62', '64', '66', '68', '70', + ] + + ignore_decorators = [ +diff --git a/glib/gversionmacros.h b/glib/gversionmacros.h +index 77486eafb..d052709cf 100644 +--- a/glib/gversionmacros.h ++++ b/glib/gversionmacros.h +@@ -255,6 +255,16 @@ + */ + #define GLIB_VERSION_2_68 (G_ENCODE_VERSION (2, 68)) + ++/** ++ * GLIB_VERSION_2_70: ++ * ++ * A macro that evaluates to the 2.70 version of GLib, in a format ++ * that can be used by the C pre-processor. ++ * ++ * Since: 2.70 ++ */ ++#define GLIB_VERSION_2_70 (G_ENCODE_VERSION (2, 70)) ++ + /** + * GLIB_VERSION_CUR_STABLE: + * +@@ -1076,4 +1086,38 @@ + # define GLIB_AVAILABLE_TYPE_IN_2_68 + #endif + ++#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_70 ++# define GLIB_DEPRECATED_IN_2_70 GLIB_DEPRECATED ++# define GLIB_DEPRECATED_IN_2_70_FOR(f) GLIB_DEPRECATED_FOR(f) ++# define GLIB_DEPRECATED_MACRO_IN_2_70 GLIB_DEPRECATED_MACRO ++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f) GLIB_DEPRECATED_MACRO_FOR(f) ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70 GLIB_DEPRECATED_ENUMERATOR ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f) GLIB_DEPRECATED_ENUMERATOR_FOR(f) ++# define GLIB_DEPRECATED_TYPE_IN_2_70 GLIB_DEPRECATED_TYPE ++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f) GLIB_DEPRECATED_TYPE_FOR(f) ++#else ++# define GLIB_DEPRECATED_IN_2_70 _GLIB_EXTERN ++# define GLIB_DEPRECATED_IN_2_70_FOR(f) _GLIB_EXTERN ++# define GLIB_DEPRECATED_MACRO_IN_2_70 ++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f) ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70 ++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f) ++# define GLIB_DEPRECATED_TYPE_IN_2_70 ++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f) ++#endif ++ ++#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_70 ++# define GLIB_AVAILABLE_IN_2_70 GLIB_UNAVAILABLE(2, 70) ++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 GLIB_UNAVAILABLE_STATIC_INLINE(2, 70) ++# define GLIB_AVAILABLE_MACRO_IN_2_70 GLIB_UNAVAILABLE_MACRO(2, 70) ++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70 GLIB_UNAVAILABLE_ENUMERATOR(2, 70) ++# define GLIB_AVAILABLE_TYPE_IN_2_70 GLIB_UNAVAILABLE_TYPE(2, 70) ++#else ++# define GLIB_AVAILABLE_IN_2_70 _GLIB_EXTERN ++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70 ++# define GLIB_AVAILABLE_MACRO_IN_2_70 ++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70 ++# define GLIB_AVAILABLE_TYPE_IN_2_70 ++#endif ++ + #endif /* __G_VERSION_MACROS_H__ */ +-- +GitLab diff --git a/base/rx/rx-glib2/1968.patch b/base/rx/rx-glib2/1968.patch new file mode 100644 index 0000000..1809214 --- /dev/null +++ b/base/rx/rx-glib2/1968.patch @@ -0,0 +1,1052 @@ +From 9e69f8b280afe8eccd9188cc53b8117e1b238db7 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 12 Oct 2021 15:52:18 -0500 +Subject: [PATCH 01/10] gspawn: use close_and_invalidate more + +--- + glib/gspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index a15fb1ca1..5d8422869 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1710,7 +1710,7 @@ do_exec (gint child_err_report_fd, + child_err_report_fd = safe_dup (child_err_report_fd); + + safe_dup2 (source_fds[i], target_fds[i]); +- (void) close (source_fds[i]); ++ close_and_invalidate (&source_fds[i]); + } + } + } +-- +2.33.1 + +From fe2148fd5dd4f2e5c413c5cc0bb56c4a19304887 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 10:43:52 -0500 +Subject: [PATCH 02/10] gspawn: Improve error message when dup fails + +This error message is no longer accurate now that we allow arbitrary fd +remapping. +--- + glib/gspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 5d8422869..e214a3998 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -2363,7 +2363,7 @@ fork_exec (gboolean intermediate_child, + g_set_error (error, + G_SPAWN_ERROR, + G_SPAWN_ERROR_FAILED, +- _("Failed to redirect output or input of child process (%s)"), ++ _("Failed to duplicate file descriptor for child process (%s)"), + g_strerror (buf[1])); + + break; +-- +2.33.1 + +From 566eccdb0a2594b4d3ec13c7443028d968b41af8 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 12 Oct 2021 15:33:59 -0500 +Subject: [PATCH 03/10] gspawn: fix hangs when duping child_err_report_fd + +In case child_err_report_fd conflicts with one of the target_fds, the +code here is careful to dup child_err_report_fd in order to avoid +conflating the two. It was a good idea, but evidently was not tested, +because the newly-created fd is not created with CLOEXEC set. This means +it stays open in the child process, causing the parent to hang forever +waiting to read from the other end of the pipe. Oops! + +The fix is simple: just set CLOEXEC. This removes our only usage of the +safe_dup() function, so it can be dropped. + +Fixes #2506 +--- + glib/gspawn.c | 16 +--------------- + 1 file changed, 1 insertion(+), 15 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index e214a3998..8bbe573f7 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1500,20 +1500,6 @@ safe_closefrom (int lowfd) + #endif + } + +-/* This function is called between fork() and exec() and hence must be +- * async-signal-safe (see signal-safety(7)). */ +-static gint +-safe_dup (gint fd) +-{ +- gint ret; +- +- do +- ret = dup (fd); +- while (ret < 0 && (errno == EINTR || errno == EBUSY)); +- +- return ret; +-} +- + /* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ + static gint +@@ -1707,7 +1693,7 @@ do_exec (gint child_err_report_fd, + else + { + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = safe_dup (child_err_report_fd); ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd); + + safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); +-- +2.33.1 + +From b703fa8b760ac9272c5a0ed3e3763b2f71ecf574 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 10:44:57 -0500 +Subject: [PATCH 04/10] gspawn: fix fd remapping conflation issue + +We currently dup all source fds to avoid possible conflation with the +target fds, but fail to consider that the result of a dup might itself +conflict with one of the target fds. Solve this the easy way by duping +all source_fds to values that are greater than the largest fd in +target_fds. + +Fixes #2503 +--- + glib/gspawn.c | 43 +++++++++++++++++++++++++------------------ + 1 file changed, 25 insertions(+), 18 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 8bbe573f7..2b48b5600 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1258,13 +1258,13 @@ unset_cloexec (int fd) + /* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ + static int +-dupfd_cloexec (int parent_fd) ++dupfd_cloexec (int old_fd, int new_fd_min) + { + int fd, errsv; + #ifdef F_DUPFD_CLOEXEC + do + { +- fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3); ++ fd = fcntl (old_fd, F_DUPFD_CLOEXEC, new_fd_min); + errsv = errno; + } + while (fd == -1 && errsv == EINTR); +@@ -1275,7 +1275,7 @@ dupfd_cloexec (int parent_fd) + int result, flags; + do + { +- fd = fcntl (parent_fd, F_DUPFD, 3); ++ fd = fcntl (old_fd, F_DUPFD, new_fd_min); + errsv = errno; + } + while (fd == -1 && errsv == EINTR); +@@ -1563,6 +1563,7 @@ do_exec (gint child_err_report_fd, + gpointer user_data) + { + gsize i; ++ gint max_target_fd = 0; + + if (working_directory && chdir (working_directory) < 0) + write_err_and_exit (child_err_report_fd, +@@ -1661,39 +1662,45 @@ do_exec (gint child_err_report_fd, + /* + * Work through the @source_fds and @target_fds mapping. + * +- * Based on code derived from ++ * Based on code originally derived from + * gnome-terminal:src/terminal-screen.c:terminal_screen_child_setup(), +- * used under the LGPLv2+ with permission from author. ++ * used under the LGPLv2+ with permission from author. (The code has ++ * since migrated to vte:src/spawn.cc:SpawnContext::exec and is no longer ++ * terribly similar to what we have here.) + */ + +- /* Basic fd assignments (where source == target) we can just unset FD_CLOEXEC +- * +- * If we're doing remapping fd assignments, we need to handle +- * the case where the user has specified e.g.: +- * 5 -> 4, 4 -> 6 +- * +- * We do this by duping the source fds temporarily in a first pass. +- * +- * If any of the @target_fds conflict with @child_err_report_fd, dup the +- * latter so it doesn’t get conflated. +- */ + if (n_fds > 0) + { ++ for (i = 0; i < n_fds; i++) ++ max_target_fd = MAX (max_target_fd, target_fds[i]); ++ ++ /* If we're doing remapping fd assignments, we need to handle ++ * the case where the user has specified e.g. 5 -> 4, 4 -> 6. ++ * We do this by duping all source fds, taking care to ensure the new ++ * fds are larger than any target fd to avoid introducing new conflicts. ++ */ + for (i = 0; i < n_fds; i++) + { + if (source_fds[i] != target_fds[i]) +- source_fds[i] = dupfd_cloexec (source_fds[i]); ++ source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); + } ++ + for (i = 0; i < n_fds; i++) + { ++ /* For basic fd assignments (where source == target), we can just ++ * unset FD_CLOEXEC. ++ */ + if (source_fds[i] == target_fds[i]) + { + unset_cloexec (source_fds[i]); + } + else + { ++ /* If any of the @target_fds conflict with @child_err_report_fd, ++ * dup it so it doesn’t get conflated. ++ */ + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = dupfd_cloexec (child_err_report_fd); ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); + + safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); +-- +2.33.1 + +From ecc3538a942760e8b403c319d359711c8e166778 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 25 Feb 2021 12:20:39 -0600 +Subject: [PATCH 05/10] gspawn: Implement fd remapping for posix_spawn codepath + +This means that GSubprocess will (sometimes) be able to use the +optimized posix_spawn codepath instead of having to fall back to +fork/exec. +--- + glib/gspawn.c | 65 +++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 58 insertions(+), 7 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 2b48b5600..9ef78dbe1 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1786,9 +1786,14 @@ do_posix_spawn (const gchar * const *argv, + gint *child_close_fds, + gint stdin_fd, + gint stdout_fd, +- gint stderr_fd) ++ gint stderr_fd, ++ const gint *source_fds, ++ const gint *target_fds, ++ gsize n_fds) + { + pid_t pid; ++ gint *duped_source_fds = NULL; ++ gint max_target_fd = 0; + const gchar * const *argv_pass; + posix_spawnattr_t attr; + posix_spawn_file_actions_t file_actions; +@@ -1797,7 +1802,8 @@ do_posix_spawn (const gchar * const *argv, + GSList *child_close = NULL; + GSList *elem; + sigset_t mask; +- int i, r; ++ size_t i; ++ int r; + + if (*argv[0] == '\0') + { +@@ -1911,6 +1917,43 @@ do_posix_spawn (const gchar * const *argv, + goto out_close_fds; + } + ++ /* If source_fds[i] != target_fds[i], we need to handle the case ++ * where the user has specified, e.g., 5 -> 4, 4 -> 6. We do this ++ * by duping the source fds, taking care to ensure the new fds are ++ * larger than any target fd to avoid introducing new conflicts. ++ * ++ * If source_fds[i] == target_fds[i], then we just need to leak ++ * the fd into the child process, which we *could* do by temporarily ++ * unsetting CLOEXEC and then setting it again after we spawn if ++ * it was originally set. POSIX requires that the addup2 action unset ++ * CLOEXEC if source and target are identical, so you'd think doing it ++ * manually wouldn't be needed, but unfortunately as of 2021 many ++ * libcs still don't do so. Example nonconforming libcs: ++ * Bionic: https://android.googlesource.com/platform/bionic/+/f6e5b582604715729b09db3e36a7aeb8c24b36a4/libc/bionic/spawn.cpp#71 ++ * uclibc-ng: https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/librt/spawn.c?id=7c36bcae09d66bbaa35cbb02253ae0556f42677e#n88 ++ * ++ * Anyway, unsetting CLOEXEC ourselves would open a small race window ++ * where the fd could be inherited into a child process if another ++ * thread spawns something at the same time, because we have not ++ * called fork() and are multithreaded here. This race is avoidable by ++ * using dupfd_cloexec, which we already have to do to handle the ++ * source_fds[i] != target_fds[i] case. So let's always do it! ++ */ ++ ++ for (i = 0; i < n_fds; i++) ++ max_target_fd = MAX (max_target_fd, target_fds[i]); ++ ++ duped_source_fds = g_new (gint, n_fds); ++ for (i = 0; i < n_fds; i++) ++ duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ ++ for (i = 0; i < n_fds; i++) ++ { ++ r = posix_spawn_file_actions_adddup2 (&file_actions, duped_source_fds[i], target_fds[i]); ++ if (r != 0) ++ goto out_close_fds; ++ } ++ + /* Intentionally close the fds in the child as the last file action, + * having been careful not to add the same fd to this list twice. + * +@@ -1940,9 +1983,16 @@ do_posix_spawn (const gchar * const *argv, + *child_pid = pid; + + out_close_fds: +- for (i = 0; i < num_parent_close_fds; i++) ++ for (i = 0; i < (size_t) num_parent_close_fds; i++) + close_and_invalidate (&parent_close_fds [i]); + ++ if (duped_source_fds != NULL) ++ { ++ for (i = 0; i < n_fds; i++) ++ close_and_invalidate (&duped_source_fds[i]); ++ g_free (duped_source_fds); ++ } ++ + posix_spawn_file_actions_destroy (&file_actions); + out_free_spawnattr: + posix_spawnattr_destroy (&attr); +@@ -2030,10 +2080,8 @@ fork_exec (gboolean intermediate_child, + child_close_fds[n_child_close_fds++] = -1; + + #ifdef POSIX_SPAWN_AVAILABLE +- /* FIXME: Handle @source_fds and @target_fds in do_posix_spawn() using the +- * file actions API. */ + if (!intermediate_child && working_directory == NULL && !close_descriptors && +- !search_path_from_envp && child_setup == NULL && n_fds == 0) ++ !search_path_from_envp && child_setup == NULL) + { + g_trace_mark (G_TRACE_CURRENT_TIME, 0, + "GLib", "posix_spawn", +@@ -2050,7 +2098,10 @@ fork_exec (gboolean intermediate_child, + child_close_fds, + stdin_fd, + stdout_fd, +- stderr_fd); ++ stderr_fd, ++ source_fds, ++ target_fds, ++ n_fds); + if (status == 0) + goto success; + +-- +2.33.1 + +From 731d6c32105dc97f2b777ef9a34c6b76d1489c04 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 25 Feb 2021 12:21:38 -0600 +Subject: [PATCH 06/10] gsubprocess: ensure we test fd remapping on the + posix_spawn() codepath + +We should run test_pass_fd twice, once using gspawn's fork/exec codepath +and once attempting to use its posix_spawn() codepath. There's no +guarantee we'll actually get the posix_spawn() codepath, but it works +for now on Linux. + +For good measure, run it a third time with no flags at all. + +This causes the test to fail if I separately break the fd remapping +implementation. Without this, we fail to test fd remapping on the +posix_spawn() codepath. +--- + gio/tests/gsubprocess.c | 44 ++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 41 insertions(+), 3 deletions(-) + +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index 7e22678ec..ba49c1c43 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -1697,7 +1697,8 @@ test_child_setup (void) + } + + static void +-test_pass_fd (void) ++do_test_pass_fd (GSubprocessFlags flags, ++ GSpawnChildSetupFunc child_setup) + { + GError *local_error = NULL; + GError **error = &local_error; +@@ -1722,9 +1723,11 @@ test_pass_fd (void) + needdup_fd_str = g_strdup_printf ("%d", needdup_pipefds[1] + 1); + + args = get_test_subprocess_args ("write-to-fds", basic_fd_str, needdup_fd_str, NULL); +- launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); ++ launcher = g_subprocess_launcher_new (flags); + g_subprocess_launcher_take_fd (launcher, basic_pipefds[1], basic_pipefds[1]); + g_subprocess_launcher_take_fd (launcher, needdup_pipefds[1], needdup_pipefds[1] + 1); ++ if (child_setup != NULL) ++ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); + proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); + g_ptr_array_free (args, TRUE); + g_assert_no_error (local_error); +@@ -1754,6 +1757,39 @@ test_pass_fd (void) + g_object_unref (proc); + } + ++static void ++test_pass_fd (void) ++{ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, NULL); ++} ++ ++static void ++empty_child_setup (gpointer user_data) ++{ ++} ++ ++static void ++test_pass_fd_empty_child_setup (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++} ++ ++static void ++test_pass_fd_inherit_fds (void) ++{ ++ /* Try to test the optimized posix_spawn codepath instead of ++ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's ++ * posix_spawn codepath does not currently handle closing ++ * non-inherited fds. Note that using INHERIT_FDS means our testing of ++ * g_subprocess_launcher_take_fd() is less-comprehensive than when ++ * using G_SUBPROCESS_FLAGS_NONE. ++ */ ++ do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++} ++ + #endif + + static void +@@ -1891,7 +1927,9 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file); + g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd); + g_test_add_func ("/gsubprocess/child-setup", test_child_setup); +- g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd); ++ g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd); ++ g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup); ++ g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + +From 4608940466a04a32d4e6e71dbe872cfecb136118 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Thu, 14 Oct 2021 11:01:33 -0500 +Subject: [PATCH 07/10] gspawn: Check from errors from safe_dup2() and + dupfd_cloexec() + +Although unlikely, these functions can fail, e.g. if we run out of file +descriptors. Check for errors to improve robustness. This is especially +important now that I changed our use of dupfd_cloexec() to avoid +returning fds smaller than the largest fd in target_fds. An application +that attempts to remap to the highest-allowed fd value deserves at least +some sort of attempt at error reporting, not silent failure. +--- + glib/gspawn.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 9ef78dbe1..7ef678047 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1572,7 +1572,6 @@ do_exec (gint child_err_report_fd, + /* Redirect pipes as required */ + if (stdin_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stdin_fd, 0) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1588,13 +1587,14 @@ do_exec (gint child_err_report_fd, + if (read_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (read_null, 0); ++ if (safe_dup2 (read_null, 0) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&read_null); + } + + if (stdout_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stdout_fd, 1) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1609,13 +1609,14 @@ do_exec (gint child_err_report_fd, + if (write_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (write_null, 1); ++ if (safe_dup2 (write_null, 1) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&write_null); + } + + if (stderr_fd >= 0) + { +- /* dup2 can't actually fail here I don't think */ + if (safe_dup2 (stderr_fd, 2) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1630,7 +1631,9 @@ do_exec (gint child_err_report_fd, + if (write_null < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +- safe_dup2 (write_null, 2); ++ if (safe_dup2 (write_null, 2) < 0) ++ write_err_and_exit (child_err_report_fd, ++ CHILD_DUP2_FAILED); + close_and_invalidate (&write_null); + } + +@@ -1643,7 +1646,8 @@ do_exec (gint child_err_report_fd, + { + if (child_setup == NULL && n_fds == 0) + { +- safe_dup2 (child_err_report_fd, 3); ++ if (safe_dup2 (child_err_report_fd, 3) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + set_cloexec (GINT_TO_POINTER (0), 3); + safe_closefrom (4); + child_err_report_fd = 3; +@@ -1682,7 +1686,11 @@ do_exec (gint child_err_report_fd, + for (i = 0; i < n_fds; i++) + { + if (source_fds[i] != target_fds[i]) +- source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ { ++ source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ if (source_fds[i] < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); ++ } + } + + for (i = 0; i < n_fds; i++) +@@ -1700,9 +1708,15 @@ do_exec (gint child_err_report_fd, + * dup it so it doesn’t get conflated. + */ + if (target_fds[i] == child_err_report_fd) +- child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); ++ { ++ child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1); ++ if (child_err_report_fd < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); ++ } ++ ++ if (safe_dup2 (source_fds[i], target_fds[i]) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + +- safe_dup2 (source_fds[i], target_fds[i]); + close_and_invalidate (&source_fds[i]); + } + } +@@ -1945,7 +1959,11 @@ do_posix_spawn (const gchar * const *argv, + + duped_source_fds = g_new (gint, n_fds); + for (i = 0; i < n_fds; i++) +- duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ { ++ duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1); ++ if (duped_source_fds[i] < 0) ++ goto out_close_fds; ++ } + + for (i = 0; i < n_fds; i++) + { +-- +2.33.1 + +From 0198b6a1c8c215f524d7c6ed2d240fb1b31d9865 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 20 Oct 2021 16:51:44 -0500 +Subject: [PATCH 08/10] gspawn: add new error message for open() failures + +Reporting these as dup2() failures is bogus. +--- + glib/gspawn.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index 7ef678047..c2fe306dc 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1532,6 +1532,7 @@ enum + { + CHILD_CHDIR_FAILED, + CHILD_EXEC_FAILED, ++ CHILD_OPEN_FAILED, + CHILD_DUP2_FAILED, + CHILD_FORK_FAILED + }; +@@ -1586,7 +1587,7 @@ do_exec (gint child_err_report_fd, + gint read_null = safe_open ("/dev/null", O_RDONLY); + if (read_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (read_null, 0) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1608,7 +1609,7 @@ do_exec (gint child_err_report_fd, + gint write_null = safe_open ("/dev/null", O_WRONLY); + if (write_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (write_null, 1) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -1630,7 +1631,7 @@ do_exec (gint child_err_report_fd, + gint write_null = safe_open ("/dev/null", O_WRONLY); + if (write_null < 0) + write_err_and_exit (child_err_report_fd, +- CHILD_DUP2_FAILED); ++ CHILD_OPEN_FAILED); + if (safe_dup2 (write_null, 2) < 0) + write_err_and_exit (child_err_report_fd, + CHILD_DUP2_FAILED); +@@ -2420,7 +2421,15 @@ fork_exec (gboolean intermediate_child, + g_strerror (buf[1])); + + break; +- ++ ++ case CHILD_OPEN_FAILED: ++ g_set_error (error, ++ G_SPAWN_ERROR, ++ G_SPAWN_ERROR_FAILED, ++ _("Failed to open file to remap file descriptor (%s)"), ++ g_strerror (buf[1])); ++ break; ++ + case CHILD_DUP2_FAILED: + g_set_error (error, + G_SPAWN_ERROR, +-- +2.33.1 + +From e4abb5f3db85b2f730e192e6398f26934e41ba21 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Tue, 26 Oct 2021 21:27:15 -0500 +Subject: [PATCH 09/10] Add tests for GSubprocess fd conflation issues + +This tests for #2503. It's fragile, but there is no non-fragile way to +test this. If the test breaks in the future, it will pass without +successfully testing the bug, not fail spuriously, so I think this is +OK. +--- + gio/tests/gsubprocess-testprog.c | 53 +++++++++++- + gio/tests/gsubprocess.c | 144 +++++++++++++++++++++++++++++++ + 2 files changed, 195 insertions(+), 2 deletions(-) + +diff --git a/gio/tests/gsubprocess-testprog.c b/gio/tests/gsubprocess-testprog.c +index c9b06c2a2..58cb1c71d 100644 +--- a/gio/tests/gsubprocess-testprog.c ++++ b/gio/tests/gsubprocess-testprog.c +@@ -5,8 +5,6 @@ + #include + #ifdef G_OS_UNIX + #include +-#include +-#include + #else + #include + #endif +@@ -150,6 +148,55 @@ write_to_fds (int argc, char **argv) + return 0; + } + ++static int ++read_from_fd (int argc, char **argv) ++{ ++ int fd; ++ const char expectedResult[] = "Yay success!"; ++ guint8 buf[sizeof (expectedResult) + 1]; ++ gsize bytes_read; ++ FILE *f; ++ ++ if (argc != 3) ++ { ++ g_print ("Usage: %s read-from-fd FD\n", argv[0]); ++ return 1; ++ } ++ ++ fd = atoi (argv[2]); ++ if (fd == 0) ++ { ++ g_warning ("Argument \"%s\" does not look like a valid nonzero file descriptor", argv[2]); ++ return 1; ++ } ++ ++ f = fdopen (fd, "r"); ++ if (f == NULL) ++ { ++ g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno)); ++ return 1; ++ } ++ ++ bytes_read = fread (buf, 1, sizeof (buf), f); ++ if (bytes_read != sizeof (expectedResult)) ++ { ++ g_warning ("Read %zu bytes, but expected %zu", bytes_read, sizeof (expectedResult)); ++ return 1; ++ } ++ ++ if (memcmp (expectedResult, buf, sizeof (expectedResult)) != 0) ++ { ++ buf[sizeof (expectedResult)] = '\0'; ++ g_warning ("Expected \"%s\" but read \"%s\"", expectedResult, (char *)buf); ++ return 1; ++ } ++ ++ if (fclose (f) == -1) ++ g_assert_not_reached (); ++ ++ return 0; ++} ++ + static int + env_mode (int argc, char **argv) + { +@@ -242,6 +289,8 @@ main (int argc, char **argv) + return sleep_forever_mode (argc, argv); + else if (strcmp (mode, "write-to-fds") == 0) + return write_to_fds (argc, argv); ++ else if (strcmp (mode, "read-from-fd") == 0) ++ return read_from_fd (argc, argv); + else if (strcmp (mode, "env") == 0) + return env_mode (argc, argv); + else if (strcmp (mode, "cwd") == 0) +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index ba49c1c43..a6e24c2e8 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1790,6 +1791,146 @@ test_pass_fd_inherit_fds (void) + do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); + } + ++static void ++do_test_fd_conflation (GSubprocessFlags flags, ++ GSpawnChildSetupFunc child_setup) ++{ ++ char success_message[] = "Yay success!"; ++ GError *error = NULL; ++ GOutputStream *output_stream; ++ GSubprocessLauncher *launcher; ++ GSubprocess *proc; ++ GPtrArray *args; ++ int unused_pipefds[2]; ++ int pipefds[2]; ++ gsize bytes_written; ++ gboolean success; ++ char *fd_str; ++ ++ /* This test must run in a new process because it is extremely sensitive to ++ * order of opened fds. ++ */ ++ if (!g_test_subprocess ()) ++ { ++ g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); ++ g_test_trap_assert_passed (); ++ return; ++ } ++ ++ g_unix_open_pipe (unused_pipefds, FD_CLOEXEC, &error); ++ g_assert_no_error (error); ++ ++ g_unix_open_pipe (pipefds, FD_CLOEXEC, &error); ++ g_assert_no_error (error); ++ ++ /* The fds should be sequential since we are in a new process. */ ++ g_assert_cmpint (unused_pipefds[0] /* 3 */, ==, unused_pipefds[1] - 1); ++ g_assert_cmpint (unused_pipefds[1] /* 4 */, ==, pipefds[0] - 1); ++ g_assert_cmpint (pipefds[0] /* 5 */, ==, pipefds[1] /* 6 */ - 1); ++ ++ /* Because GSubprocess allows arbitrary remapping of fds, it has to be careful ++ * to avoid fd conflation issues, e.g. it should properly handle 5 -> 4 and ++ * 4 -> 5 at the same time. GIO previously attempted to handle this by naively ++ * dup'ing the source fds, but this was not good enough because it was ++ * possible that the dup'ed result could still conflict with one of the target ++ * fds. For example: ++ * ++ * source_fd 5 -> target_fd 9, source_fd 3 -> target_fd 7 ++ * ++ * dup(5) -> dup returns 8 ++ * dup(3) -> dup returns 9 ++ * ++ * After dup'ing, we wind up with: 8 -> 9, 9 -> 7. That means that after we ++ * dup2(8, 9), we have clobbered fd 9 before we dup2(9, 7). The end result is ++ * we have remapped 5 -> 9 as expected, but then remapped 5 -> 7 instead of ++ * 3 -> 7 as the application intended. ++ * ++ * This issue has been fixed in the simplest way possible, by passing a ++ * minimum fd value when using F_DUPFD_CLOEXEC that is higher than any of the ++ * target fds, to guarantee all source fds are different than all target fds, ++ * eliminating any possibility of conflation. ++ * ++ * Anyway, that is why we have the unused_pipefds here. We need to open fds in ++ * a certain order in order to trick older GSubprocess into conflating the ++ * fds. The primary goal of this test is to ensure this particular conflation ++ * issue is not reintroduced. See glib#2503. ++ * ++ * Be aware this test is necessarily extremely fragile. To reproduce these ++ * bugs, it relies on internals of gspawn and gmain that will likely change ++ * in the future, eventually causing this test to no longer test the the bugs ++ * it was originally designed to test. That is OK! If the test fails, at ++ * least you know *something* is wrong. ++ */ ++ launcher = g_subprocess_launcher_new (flags); ++ g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */); ++ g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */); ++ if (child_setup != NULL) ++ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); ++ fd_str = g_strdup_printf ("%d", pipefds[1] + 3); ++ args = get_test_subprocess_args ("read-from-fd", fd_str, NULL); ++ proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (proc); ++ g_ptr_array_free (args, TRUE); ++ g_object_unref (launcher); ++ g_free (fd_str); ++ ++ /* Close the read ends of the pipes. */ ++ close (unused_pipefds[0]); ++ close (pipefds[0]); ++ ++ /* Also close the write end of the unused pipe. */ ++ close (unused_pipefds[1]); ++ ++ /* So now pipefds[0] should be inherited into the subprocess as ++ * pipefds[1] + 2, and unused_pipefds[0] should be inherited as ++ * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify ++ * that it reads the expected data. But older broken GIO will accidentally ++ * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess ++ * to hang trying to read from the wrong pipe. ++ */ ++ output_stream = g_unix_output_stream_new (pipefds[1], TRUE); ++ success = g_output_stream_write_all (output_stream, ++ success_message, sizeof (success_message), ++ &bytes_written, ++ NULL, ++ &error); ++ g_assert_no_error (error); ++ g_assert_cmpint (bytes_written, ==, sizeof (success_message)); ++ g_assert_true (success); ++ g_object_unref (output_stream); ++ ++ success = g_subprocess_wait_check (proc, NULL, &error); ++ g_assert_no_error (error); ++ g_object_unref (proc); ++} ++ ++static void ++test_fd_conflation (void) ++{ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL); ++} ++ ++static void ++test_fd_conflation_empty_child_setup (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++} ++ ++static void ++test_fd_conflation_inherit_fds (void) ++{ ++ /* Try to test the optimized posix_spawn codepath instead of ++ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's ++ * posix_spawn codepath does not currently handle closing ++ * non-inherited fds. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++} ++ + #endif + + static void +@@ -1930,6 +2071,9 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd); + g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup); + g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds); ++ g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation); ++ g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup); ++ g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + +From 5542612c805857a244561ec160e904dd302ae799 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 27 Oct 2021 18:30:47 -0500 +Subject: [PATCH 10/10] Add test for child_err_report_fd conflation with target + fds + +This tests for glib#2506. +--- + gio/tests/gsubprocess.c | 42 ++++++++++++++++++++++++++++++++++------- + 1 file changed, 35 insertions(+), 7 deletions(-) + +diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c +index a6e24c2e8..4629cdea7 100644 +--- a/gio/tests/gsubprocess.c ++++ b/gio/tests/gsubprocess.c +@@ -1793,7 +1793,8 @@ test_pass_fd_inherit_fds (void) + + static void + do_test_fd_conflation (GSubprocessFlags flags, +- GSpawnChildSetupFunc child_setup) ++ GSpawnChildSetupFunc child_setup, ++ gboolean test_child_err_report_fd) + { + char success_message[] = "Yay success!"; + GError *error = NULL; +@@ -1803,6 +1804,7 @@ do_test_fd_conflation (GSubprocessFlags flags, + GPtrArray *args; + int unused_pipefds[2]; + int pipefds[2]; ++ int fd_to_pass_to_child; + gsize bytes_written; + gboolean success; + char *fd_str; +@@ -1855,18 +1857,26 @@ do_test_fd_conflation (GSubprocessFlags flags, + * fds. The primary goal of this test is to ensure this particular conflation + * issue is not reintroduced. See glib#2503. + * ++ * This test also has an alternate mode of operation where it instead tests ++ * for conflation with gspawn's child_err_report_fd, glib#2506. ++ * + * Be aware this test is necessarily extremely fragile. To reproduce these + * bugs, it relies on internals of gspawn and gmain that will likely change + * in the future, eventually causing this test to no longer test the the bugs + * it was originally designed to test. That is OK! If the test fails, at + * least you know *something* is wrong. + */ ++ if (test_child_err_report_fd) ++ fd_to_pass_to_child = pipefds[1] + 2 /* 8 */; ++ else ++ fd_to_pass_to_child = pipefds[1] + 3 /* 9 */; ++ + launcher = g_subprocess_launcher_new (flags); +- g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */); ++ g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, fd_to_pass_to_child); + g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */); + if (child_setup != NULL) + g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL); +- fd_str = g_strdup_printf ("%d", pipefds[1] + 3); ++ fd_str = g_strdup_printf ("%d", fd_to_pass_to_child); + args = get_test_subprocess_args ("read-from-fd", fd_str, NULL); + proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error); + g_assert_no_error (error); +@@ -1882,12 +1892,20 @@ do_test_fd_conflation (GSubprocessFlags flags, + /* Also close the write end of the unused pipe. */ + close (unused_pipefds[1]); + +- /* So now pipefds[0] should be inherited into the subprocess as ++ /* If doing our normal test: ++ * ++ * So now pipefds[0] should be inherited into the subprocess as + * pipefds[1] + 2, and unused_pipefds[0] should be inherited as + * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify + * that it reads the expected data. But older broken GIO will accidentally + * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess + * to hang trying to read from the wrong pipe. ++ * ++ * If testing conflation with child_err_report_fd: ++ * ++ * We are actually already done. The real test succeeded if we made it this ++ * far without hanging while spawning the child. But let's continue with our ++ * write and read anyway, to ensure things are good. + */ + output_stream = g_unix_output_stream_new (pipefds[1], TRUE); + success = g_output_stream_write_all (output_stream, +@@ -1908,7 +1926,7 @@ do_test_fd_conflation (GSubprocessFlags flags, + static void + test_fd_conflation (void) + { +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL, FALSE); + } + + static void +@@ -1917,7 +1935,7 @@ test_fd_conflation_empty_child_setup (void) + /* Using a child setup function forces gspawn to use fork/exec + * rather than posix_spawn. + */ +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, FALSE); + } + + static void +@@ -1928,7 +1946,16 @@ test_fd_conflation_inherit_fds (void) + * posix_spawn codepath does not currently handle closing + * non-inherited fds. + */ +- do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL); ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL, FALSE); ++} ++ ++static void ++test_fd_conflation_child_err_report_fd (void) ++{ ++ /* Using a child setup function forces gspawn to use fork/exec ++ * rather than posix_spawn. ++ */ ++ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, TRUE); + } + + #endif +@@ -2074,6 +2101,7 @@ main (int argc, char **argv) + g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation); + g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup); + g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds); ++ g_test_add_func ("/gsubprocess/fd-conflation/child-err-report-fd", test_fd_conflation_child_err_report_fd); + #endif + g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment); + +-- +2.33.1 + diff --git a/base/rx/rx-glib2/2194.patch b/base/rx/rx-glib2/2194.patch new file mode 100644 index 0000000..5c9aaf2 --- /dev/null +++ b/base/rx/rx-glib2/2194.patch @@ -0,0 +1,920 @@ +From 2e500304e304e45042a59855319ff0379b1978b3 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Tue, 27 Jul 2021 17:24:17 +0200 +Subject: [PATCH 1/4] tests: Remove unused constant in GMemoryMonitor test + +--- + gio/tests/memory-monitor-dbus.py.in | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in +index cd16cf4e3..7823e7309 100755 +--- a/gio/tests/memory-monitor-dbus.py.in ++++ b/gio/tests/memory-monitor-dbus.py.in +@@ -31,9 +31,6 @@ try: + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +- # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") +- XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" +- + class TestLowMemoryMonitor(dbusmock.DBusTestCase): + '''Test GMemoryMonitorDBus''' + +-- +GitLab + + +From a7000cd989438b01e599b2cfa8b6d5a360bfd102 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 28 Jul 2021 15:10:16 +0200 +Subject: [PATCH 2/4] gio: g_clear_signal_handler() can handle NULL args + +--- + gio/gmemorymonitordbus.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c +index a34a58d3b..08dc53df1 100644 +--- a/gio/gmemorymonitordbus.c ++++ b/gio/gmemorymonitordbus.c +@@ -115,8 +115,7 @@ lmm_vanished_cb (GDBusConnection *connection, + { + GMemoryMonitorDBus *dbus = user_data; + +- if (dbus->proxy != NULL) +- g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + } + +@@ -143,8 +142,7 @@ g_memory_monitor_dbus_finalize (GObject *object) + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object); + +- if (dbus->proxy != NULL) +- g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); + +-- +GitLab + + +From 92399e7114e590f0371b1a5d71f478f840cb4074 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 28 Jul 2021 15:30:15 +0200 +Subject: [PATCH 3/4] gio: Do not block when low-memory-monitor daemon appears + +--- + gio/gmemorymonitordbus.c | 42 +++++++++++++++++++++++++++------------- + 1 file changed, 29 insertions(+), 13 deletions(-) + +diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c +index 08dc53df1..739b83214 100644 +--- a/gio/gmemorymonitordbus.c ++++ b/gio/gmemorymonitordbus.c +@@ -25,6 +25,7 @@ + #include "giomodule-priv.h" + #include "glibintl.h" + #include "glib/gstdio.h" ++#include "gcancellable.h" + #include "gdbusproxy.h" + #include "gdbusnamewatching.h" + +@@ -38,6 +39,7 @@ struct _GMemoryMonitorDBus + GObject parent_instance; + + guint watch_id; ++ GCancellable *cancellable; + GDBusProxy *proxy; + gulong signal_id; + }; +@@ -77,24 +79,15 @@ proxy_signal_cb (GDBusProxy *proxy, + } + + static void +-lmm_appeared_cb (GDBusConnection *connection, +- const gchar *name, +- const gchar *name_owner, +- gpointer user_data) ++lmm_proxy_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) + { + GMemoryMonitorDBus *dbus = user_data; + GDBusProxy *proxy; + GError *error = NULL; + +- proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, +- G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, +- NULL, +- "org.freedesktop.LowMemoryMonitor", +- "/org/freedesktop/LowMemoryMonitor", +- "org.freedesktop.LowMemoryMonitor", +- NULL, +- &error); +- ++ proxy = g_dbus_proxy_new_finish (res, &error); + if (!proxy) + { + g_debug ("Failed to create LowMemoryMonitor D-Bus proxy: %s", +@@ -106,6 +99,26 @@ lmm_appeared_cb (GDBusConnection *connection, + dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal", + G_CALLBACK (proxy_signal_cb), dbus); + dbus->proxy = proxy; ++ ++} ++ ++static void ++lmm_appeared_cb (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ GMemoryMonitorDBus *dbus = user_data; ++ ++ g_dbus_proxy_new (connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ NULL, ++ "org.freedesktop.LowMemoryMonitor", ++ "/org/freedesktop/LowMemoryMonitor", ++ "org.freedesktop.LowMemoryMonitor", ++ dbus->cancellable, ++ lmm_proxy_cb, ++ dbus); + } + + static void +@@ -126,6 +139,7 @@ g_memory_monitor_dbus_initable_init (GInitable *initable, + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (initable); + ++ dbus->cancellable = g_cancellable_new (); + dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, + "org.freedesktop.LowMemoryMonitor", + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, +@@ -142,6 +156,8 @@ g_memory_monitor_dbus_finalize (GObject *object) + { + GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object); + ++ g_cancellable_cancel (dbus->cancellable); ++ g_clear_object (&dbus->cancellable); + g_clear_signal_handler (&dbus->signal_id, dbus->proxy); + g_clear_object (&dbus->proxy); + g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); +-- +GitLab + + +From 889bdb994fed44344a84ad01aa5633a1b1b62b19 Mon Sep 17 00:00:00 2001 +From: Patrick Griffis +Date: Tue, 20 Jul 2021 16:04:31 -0500 +Subject: [PATCH 4/4] Add GPowerProfileMonitor + +--- + docs/reference/gio/gio-docs.xml | 1 + + docs/reference/gio/gio-sections-common.txt | 18 ++ + docs/reference/gio/meson.build | 1 + + gio/gio.h | 1 + + gio/giomodule.c | 7 + + gio/gpowerprofilemonitor.c | 141 ++++++++++++ + gio/gpowerprofilemonitor.h | 63 ++++++ + gio/gpowerprofilemonitordbus.c | 240 +++++++++++++++++++++ + gio/gpowerprofilemonitordbus.h | 32 +++ + gio/meson.build | 3 + + gio/tests/meson.build | 1 + + gio/tests/power-profile-monitor.c | 79 +++++++ + 12 files changed, 587 insertions(+) + create mode 100644 gio/gpowerprofilemonitor.c + create mode 100644 gio/gpowerprofilemonitor.h + create mode 100644 gio/gpowerprofilemonitordbus.c + create mode 100644 gio/gpowerprofilemonitordbus.h + create mode 100644 gio/tests/power-profile-monitor.c + +diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml +index a09d6d31d..b01133900 100644 +--- a/docs/reference/gio/gio-docs.xml ++++ b/docs/reference/gio/gio-docs.xml +@@ -238,6 +238,7 @@ + + + ++ + + + Extending GIO +diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt +index 250491a42..a7addedc2 100644 +--- a/docs/reference/gio/gio-sections-common.txt ++++ b/docs/reference/gio/gio-sections-common.txt +@@ -4247,6 +4247,24 @@ G_NETWORK_MONITOR_GET_INTERFACE + g_network_connectivity_get_type + + ++
++gpowerprofilemonitor ++GPowerProfileMonitor ++GPowerProfileMonitor ++GPowerProfileMonitorInterface ++G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME ++g_power_profile_monitor_dup_default ++g_power_profile_monitor_get_power_saver_enabled ++ ++g_power_profile_monitor_get_type ++G_TYPE_POWER_PROFILE_MONITOR ++G_POWER_PROFILE_MONITOR ++G_IS_POWER_PROFILE_MONITOR ++G_POWER_PROFILE_MONITOR_GET_INTERFACE ++G_TYPE_POWER_PROFILE_LEVEL ++g_power_profile_level_get_type ++
++ +
+ gmenuexporter + g_dbus_connection_export_menu_model +diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build +index 4d0364819..fbabd25ca 100644 +--- a/docs/reference/gio/meson.build ++++ b/docs/reference/gio/meson.build +@@ -65,6 +65,7 @@ if get_option('gtk_doc') + 'gopenuriportal.h', + 'gpollfilemonitor.h', + 'gportalsupport.h', ++ 'gpowerprofilemonitordbus.h', + 'gproxyresolverportal.h', + 'gregistrysettingsbackend.h', + 'gresourcefile.h', +diff --git a/gio/gio.h b/gio/gio.h +index f5d2dd5a3..e9afab666 100644 +--- a/gio/gio.h ++++ b/gio/gio.h +@@ -120,6 +120,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/gio/giomodule.c b/gio/giomodule.c +index c1d451b5c..dfd895717 100644 +--- a/gio/giomodule.c ++++ b/gio/giomodule.c +@@ -48,6 +48,8 @@ + #include "gmemorymonitor.h" + #include "gmemorymonitorportal.h" + #include "gmemorymonitordbus.h" ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitordbus.h" + #ifdef G_OS_WIN32 + #include "gregistrysettingsbackend.h" + #include "giowin32-priv.h" +@@ -1077,6 +1079,7 @@ extern GType _g_network_monitor_nm_get_type (void); + + extern GType g_memory_monitor_dbus_get_type (void); + extern GType g_memory_monitor_portal_get_type (void); ++extern GType g_power_profile_monitor_dbus_get_type (void); + + #ifdef G_OS_UNIX + extern GType g_fdo_notification_backend_get_type (void); +@@ -1187,6 +1190,9 @@ _g_io_modules_ensure_extension_points_registered (void) + + ep = g_io_extension_point_register (G_MEMORY_MONITOR_EXTENSION_POINT_NAME); + g_io_extension_point_set_required_type (ep, G_TYPE_MEMORY_MONITOR); ++ ++ ep = g_io_extension_point_register (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME); ++ g_io_extension_point_set_required_type (ep, G_TYPE_POWER_PROFILE_MONITOR); + } + + G_UNLOCK (registered_extensions); +@@ -1272,6 +1278,7 @@ _g_io_modules_ensure_loaded (void) + g_type_ensure (g_null_settings_backend_get_type ()); + g_type_ensure (g_memory_settings_backend_get_type ()); + g_type_ensure (g_keyfile_settings_backend_get_type ()); ++ g_type_ensure (g_power_profile_monitor_dbus_get_type ()); + #if defined(HAVE_INOTIFY_INIT1) + g_type_ensure (g_inotify_file_monitor_get_type ()); + #endif +diff --git a/gio/gpowerprofilemonitor.c b/gio/gpowerprofilemonitor.c +new file mode 100644 +index 000000000..f5028b3e8 +--- /dev/null ++++ b/gio/gpowerprofilemonitor.c +@@ -0,0 +1,141 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc ++ * Copyright 2021 Igalia S.L. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++#include "glib.h" ++#include "glibintl.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "ginetaddress.h" ++#include "ginetsocketaddress.h" ++#include "ginitable.h" ++#include "gioenumtypes.h" ++#include "giomodule-priv.h" ++#include "gtask.h" ++ ++/** ++ * SECTION:gpowerprofilemonitor ++ * @title: GPowerProfileMonitor ++ * @short_description: Power profile monitor ++ * @include: gio/gio.h ++ * ++ * #GPowerProfileMonitor makes it possible for applications as well as OS components ++ * to monitor system power profiles and act upon them. It currently only exports ++ * whether the system is in “Power Saver” mode (known as “Low Power” mode on ++ * some systems). ++ * ++ * When in “Low Power” mode, it is recommended that applications: ++ * - disabling automatic downloads ++ * - reduce the rate of refresh from online sources such as calendar or ++ * email synchronisation ++ * - if the application has expensive visual effects, reduce them ++ * ++ * It is also likely that OS components providing services to applications will ++ * lower their own background activity, for the sake of the system. ++ * ++ * There are a variety of tools that exist for power consumption analysis, but those ++ * usually depend on the OS and hardware used. On Linux, one could use `upower` to ++ * monitor the battery discharge rate, `powertop` to check on the background activity ++ * or activity at all), `sysprof` to inspect CPU usage, and `intel_gpu_time` to ++ * profile GPU usage. ++ * ++ * Don't forget to disconnect the #GPowerProfileMonitor::notify::power-saver-enabled ++ * signal, and unref the #GPowerProfileMonitor itself when exiting. ++ * ++ * Since: 2.70 ++ */ ++ ++/** ++ * GPowerProfileMonitor: ++ * ++ * #GPowerProfileMonitor monitors system power profile and notifies on ++ * changes. ++ * ++ * Since: 2.70 ++ */ ++ ++/** ++ * GPowerProfileMonitorInterface: ++ * @g_iface: The parent interface. ++ * ++ * The virtual function table for #GPowerProfileMonitor. ++ * ++ * Since: 2.70 ++ */ ++ ++G_DEFINE_INTERFACE_WITH_CODE (GPowerProfileMonitor, g_power_profile_monitor, G_TYPE_OBJECT, ++ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE)) ++ ++ ++/** ++ * g_power_profile_monitor_dup_default: ++ * ++ * Gets a reference to the default #GPowerProfileMonitor for the system. ++ * ++ * Returns: (not nullable) (transfer full): a new reference to the default #GPowerProfileMonitor ++ * ++ * Since: 2.70 ++ */ ++GPowerProfileMonitor * ++g_power_profile_monitor_dup_default (void) ++{ ++ return g_object_ref (_g_io_module_get_default (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ "GIO_USE_POWER_PROFILE_MONITOR", ++ NULL)); ++} ++ ++/** ++ * g_power_profile_monitor_get_power_saver_enabled: ++ * @monitor: a #GPowerProfileMonitor ++ * ++ * Gets whether the system is in “Power Saver” mode. ++ * ++ * You are expected to listen to the ++ * #GPowerProfileMonitor::notify::power-saver-enabled signal to know when the profile has ++ * changed. ++ * ++ * Returns: Whether the system is in “Power Saver” mode. ++ * ++ * Since: 2.70 ++ */ ++gboolean ++g_power_profile_monitor_get_power_saver_enabled (GPowerProfileMonitor *monitor) ++{ ++ gboolean enabled; ++ g_object_get (monitor, "power-saver-enabled", &enabled, NULL); ++ return enabled; ++} ++ ++static void ++g_power_profile_monitor_default_init (GPowerProfileMonitorInterface *iface) ++{ ++ /** ++ * GPowerProfileMonitor:power-saver-enabled: ++ * ++ * Whether “Power Saver” mode is enabled on the system. ++ * ++ * Since: 2.70 ++ */ ++ g_object_interface_install_property (iface, ++ g_param_spec_boolean ("power-saver-enabled", ++ "power-saver-enabled", ++ "Power Saver Enabled", ++ FALSE, ++ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); ++} +diff --git a/gio/gpowerprofilemonitor.h b/gio/gpowerprofilemonitor.h +new file mode 100644 +index 000000000..0891fc3dc +--- /dev/null ++++ b/gio/gpowerprofilemonitor.h +@@ -0,0 +1,63 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyright 2021 Igalia S.L. ++ * ++ * 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 . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_H__ ++#define __G_POWER_PROFILE_MONITOR_H__ ++ ++#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) ++#error "Only can be included directly." ++#endif ++ ++#include ++ ++G_BEGIN_DECLS ++ ++/** ++ * G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME: ++ * ++ * Extension point for power profile usage monitoring functionality. ++ * See [Extending GIO][extending-gio]. ++ * ++ * Since: 2.70 ++ */ ++#define G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME "gio-power-profile-monitor" ++ ++#define G_TYPE_POWER_PROFILE_MONITOR (g_power_profile_monitor_get_type ()) ++GLIB_AVAILABLE_IN_2_70 ++G_DECLARE_INTERFACE (GPowerProfileMonitor, g_power_profile_monitor, g, power_profile_monitor, GObject) ++ ++#define G_POWER_PROFILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitor)) ++#define G_IS_POWER_PROFILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_POWER_PROFILE_MONITOR)) ++#define G_POWER_PROFILE_MONITOR_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitorInterface)) ++ ++struct _GPowerProfileMonitorInterface ++{ ++ /*< private >*/ ++ GTypeInterface g_iface; ++}; ++ ++GLIB_AVAILABLE_IN_2_70 ++GPowerProfileMonitor *g_power_profile_monitor_dup_default (void); ++ ++GLIB_AVAILABLE_IN_2_70 ++gboolean g_power_profile_monitor_get_power_saver_enabled (GPowerProfileMonitor *monitor); ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_H__ */ +diff --git a/gio/gpowerprofilemonitordbus.c b/gio/gpowerprofilemonitordbus.c +new file mode 100644 +index 000000000..8bbfe3acc +--- /dev/null ++++ b/gio/gpowerprofilemonitordbus.c +@@ -0,0 +1,240 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyrgith 2021 Igalia S.L. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitordbus.h" ++#include "gioerror.h" ++#include "ginitable.h" ++#include "giomodule-priv.h" ++#include "glibintl.h" ++#include "glib/gstdio.h" ++#include "gcancellable.h" ++#include "gdbusproxy.h" ++#include "gdbusnamewatching.h" ++ ++#define G_POWER_PROFILE_MONITOR_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) ++ ++static void g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *iface); ++static void g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface); ++ ++struct _GPowerProfileMonitorDBus ++{ ++ GObject parent_instance; ++ ++ guint watch_id; ++ GCancellable *cancellable; ++ GDBusProxy *proxy; ++ gulong signal_id; ++ ++ gboolean power_saver_enabled; ++}; ++ ++typedef enum ++{ ++ PROP_POWER_SAVER_ENABLED = 1, ++} GPowerProfileMonitorDBusProperty; ++ ++#define POWERPROFILES_DBUS_NAME "net.hadess.PowerProfiles" ++#define POWERPROFILES_DBUS_IFACE "net.hadess.PowerProfiles" ++#define POWERPROFILES_DBUS_PATH "/net/hadess/PowerProfiles" ++ ++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ++ g_power_profile_monitor_dbus_initable_iface_init) ++ G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR, ++ g_power_profile_monitor_dbus_iface_init) ++ _g_io_modules_ensure_extension_points_registered (); ++ g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ g_define_type_id, ++ "dbus", ++ 30)) ++ ++static void ++g_power_profile_monitor_dbus_init (GPowerProfileMonitorDBus *dbus) ++{ ++ dbus->power_saver_enabled = FALSE; ++} ++ ++static void ++ppd_properties_changed_cb (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv *invalidated_properties, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ const char *active_profile; ++ gboolean enabled; ++ ++ if (!g_variant_lookup (changed_properties, "ActiveProfile", "&s", &active_profile)) ++ return; ++ ++ enabled = g_strcmp0 (active_profile, "power-saver") == 0; ++ if (enabled == dbus->power_saver_enabled) ++ return; ++ ++ dbus->power_saver_enabled = enabled; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++} ++ ++static void ++ppd_proxy_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ GVariant *active_profile_variant; ++ GDBusProxy *proxy; ++ GError *error = NULL; ++ const char *active_profile; ++ gboolean power_saver_enabled; ++ ++ proxy = g_dbus_proxy_new_finish (res, &error); ++ if (!proxy) ++ { ++ g_debug ("GPowerProfileMonitorDBus: Failed to create PowerProfiles D-Bus proxy: %s", ++ error->message); ++ g_error_free (error); ++ return; ++ } ++ ++ active_profile_variant = g_dbus_proxy_get_cached_property (proxy, "ActiveProfile"); ++ if (g_variant_is_of_type (active_profile_variant, G_VARIANT_TYPE_STRING)) ++ { ++ active_profile = g_variant_get_string (active_profile_variant, NULL); ++ power_saver_enabled = g_strcmp0 (active_profile, "power-saver") == 0; ++ if (power_saver_enabled != dbus->power_saver_enabled) ++ { ++ dbus->power_saver_enabled = power_saver_enabled; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++ } ++ } ++ ++ dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed", ++ G_CALLBACK (ppd_properties_changed_cb), dbus); ++ dbus->proxy = g_steal_pointer (&proxy); ++} ++ ++static void ++ppd_appeared_cb (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ ++ g_dbus_proxy_new (connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ NULL, ++ POWERPROFILES_DBUS_NAME, ++ POWERPROFILES_DBUS_PATH, ++ POWERPROFILES_DBUS_IFACE, ++ dbus->cancellable, ++ ppd_proxy_cb, ++ dbus); ++} ++ ++static void ++ppd_vanished_cb (GDBusConnection *connection, ++ const gchar *name, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorDBus *dbus = user_data; ++ ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_object (&dbus->proxy); ++ ++ dbus->power_saver_enabled = FALSE; ++ g_object_notify (G_OBJECT (dbus), "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_dbus_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object); ++ ++ switch ((GPowerProfileMonitorDBusProperty) prop_id) ++ { ++ case PROP_POWER_SAVER_ENABLED: ++ g_value_set_boolean (value, dbus->power_saver_enabled); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static gboolean ++g_power_profile_monitor_dbus_initable_init (GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (initable); ++ ++ dbus->cancellable = g_cancellable_new (); ++ dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, ++ POWERPROFILES_DBUS_NAME, ++ G_BUS_NAME_WATCHER_FLAGS_AUTO_START, ++ ppd_appeared_cb, ++ ppd_vanished_cb, ++ dbus, ++ NULL); ++ ++ return TRUE; ++} ++ ++static void ++g_power_profile_monitor_dbus_finalize (GObject *object) ++{ ++ GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object); ++ ++ g_cancellable_cancel (dbus->cancellable); ++ g_clear_object (&dbus->cancellable); ++ g_clear_signal_handler (&dbus->signal_id, dbus->proxy); ++ g_clear_object (&dbus->proxy); ++ g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name); ++ ++ G_OBJECT_CLASS (g_power_profile_monitor_dbus_parent_class)->finalize (object); ++} ++ ++static void ++g_power_profile_monitor_dbus_class_init (GPowerProfileMonitorDBusClass *nl_class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); ++ ++ gobject_class->get_property = g_power_profile_monitor_dbus_get_property; ++ gobject_class->finalize = g_power_profile_monitor_dbus_finalize; ++ ++ g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *monitor_iface) ++{ ++} ++ ++static void ++g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface) ++{ ++ iface->init = g_power_profile_monitor_dbus_initable_init; ++} +diff --git a/gio/gpowerprofilemonitordbus.h b/gio/gpowerprofilemonitordbus.h +new file mode 100644 +index 000000000..ecf7246d1 +--- /dev/null ++++ b/gio/gpowerprofilemonitordbus.h +@@ -0,0 +1,32 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2019 Red Hat, Inc. ++ * Copyright 2021 Igalia S.L. ++ * ++ * 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 . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_DBUS_H__ ++#define __G_POWER_PROFILE_MONITOR_DBUS_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_POWER_PROFILE_MONITOR_DBUS (g_power_profile_monitor_dbus_get_type ()) ++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G, POWER_PROFILE_MONITOR_DBUS, GObject) ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_DBUS_H__ */ +diff --git a/gio/meson.build b/gio/meson.build +index 49a37a7bd..d5838ed8a 100644 +--- a/gio/meson.build ++++ b/gio/meson.build +@@ -533,6 +533,8 @@ gio_sources = files( + 'gpollableoutputstream.c', + 'gpollableutils.c', + 'gpollfilemonitor.c', ++ 'gpowerprofilemonitor.c', ++ 'gpowerprofilemonitordbus.c', + 'gproxy.c', + 'gproxyaddress.c', + 'gproxyaddressenumerator.c', +@@ -673,6 +675,7 @@ gio_headers = files( + 'gpollableinputstream.h', + 'gpollableoutputstream.h', + 'gpollableutils.h', ++ 'gpowerprofilemonitor.h', + 'gproxy.h', + 'gproxyaddress.h', + 'gproxyaddressenumerator.h', +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index 98d1401d0..fc2055101 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -75,6 +75,7 @@ gio_tests = { + 'network-monitor-race' : {}, + 'permission' : {}, + 'pollable' : {'dependencies' : [libdl_dep]}, ++ 'power-profile-monitor' : {}, + 'proxy-test' : {}, + 'readwrite' : {}, + 'simple-async-result' : {}, +diff --git a/gio/tests/power-profile-monitor.c b/gio/tests/power-profile-monitor.c +new file mode 100644 +index 000000000..bb32f181f +--- /dev/null ++++ b/gio/tests/power-profile-monitor.c +@@ -0,0 +1,79 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Igalia S.L. ++ * ++ * 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 . ++ */ ++ ++#include ++ ++static void ++test_dup_default (void) ++{ ++ GPowerProfileMonitor *monitor; ++ ++ monitor = g_power_profile_monitor_dup_default (); ++ g_assert_nonnull (monitor); ++ g_object_unref (monitor); ++} ++ ++static void ++power_saver_enabled_cb (GPowerProfileMonitor *monitor, ++ GParamSpec *pspec, ++ gpointer user_data) ++{ ++ gboolean enabled; ++ ++ enabled = g_power_profile_monitor_get_power_saver_enabled (monitor); ++ g_debug ("Power Saver %s (%d)", enabled ? "enabled" : "disabled", enabled); ++} ++ ++static void ++do_watch_power_profile (void) ++{ ++ GPowerProfileMonitor *monitor; ++ GMainLoop *loop; ++ gulong signal_id; ++ ++ monitor = g_power_profile_monitor_dup_default (); ++ signal_id = g_signal_connect (G_OBJECT (monitor), "notify::power-saver-enabled", ++ G_CALLBACK (power_saver_enabled_cb), NULL); ++ ++ loop = g_main_loop_new (NULL, TRUE); ++ g_main_loop_run (loop); ++ ++ g_signal_handler_disconnect (monitor, signal_id); ++ g_object_unref (monitor); ++ g_main_loop_unref (loop); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int ret; ++ ++ if (argc == 2 && !strcmp (argv[1], "--watch")) ++ { ++ do_watch_power_profile (); ++ return 0; ++ } ++ ++ g_test_init (&argc, &argv, NULL); ++ ++ g_test_add_func ("/power-profile-monitor/default", test_dup_default); ++ ++ ret = g_test_run (); ++ ++ return ret; ++} +-- +GitLab + diff --git a/base/rx/rx-glib2/2222.patch b/base/rx/rx-glib2/2222.patch new file mode 100644 index 0000000..0b6106f --- /dev/null +++ b/base/rx/rx-glib2/2222.patch @@ -0,0 +1,739 @@ +From 9645cbffa8ba1a08b73fdae50b31125d11aa5684 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Mon, 9 Aug 2021 23:19:17 +0200 +Subject: [PATCH 1/4] gio: Add portal version of GPowerProfileMonitor + +--- + docs/reference/gio/meson.build | 1 + + gio/giomodule.c | 2 + + gio/gpowerprofilemonitorportal.c | 182 +++++++++++++++++++++++++++++++ + gio/gpowerprofilemonitorportal.h | 31 ++++++ + gio/meson.build | 1 + + 5 files changed, 217 insertions(+) + create mode 100644 gio/gpowerprofilemonitorportal.c + create mode 100644 gio/gpowerprofilemonitorportal.h + +diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build +index fbabd25ca..9aaafeed5 100644 +--- a/docs/reference/gio/meson.build ++++ b/docs/reference/gio/meson.build +@@ -66,6 +66,7 @@ if get_option('gtk_doc') + 'gpollfilemonitor.h', + 'gportalsupport.h', + 'gpowerprofilemonitordbus.h', ++ 'gpowerprofilemonitorportal.h', + 'gproxyresolverportal.h', + 'gregistrysettingsbackend.h', + 'gresourcefile.h', +diff --git a/gio/giomodule.c b/gio/giomodule.c +index dfd895717..d34037a45 100644 +--- a/gio/giomodule.c ++++ b/gio/giomodule.c +@@ -50,6 +50,7 @@ + #include "gmemorymonitordbus.h" + #include "gpowerprofilemonitor.h" + #include "gpowerprofilemonitordbus.h" ++#include "gpowerprofilemonitorportal.h" + #ifdef G_OS_WIN32 + #include "gregistrysettingsbackend.h" + #include "giowin32-priv.h" +@@ -1305,6 +1306,7 @@ _g_io_modules_ensure_loaded (void) + g_type_ensure (g_memory_monitor_dbus_get_type ()); + g_type_ensure (g_memory_monitor_portal_get_type ()); + g_type_ensure (g_network_monitor_portal_get_type ()); ++ g_type_ensure (g_power_profile_monitor_portal_get_type ()); + g_type_ensure (g_proxy_resolver_portal_get_type ()); + #endif + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +diff --git a/gio/gpowerprofilemonitorportal.c b/gio/gpowerprofilemonitorportal.c +new file mode 100644 +index 000000000..bb1b4fd15 +--- /dev/null ++++ b/gio/gpowerprofilemonitorportal.c +@@ -0,0 +1,182 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Red Hat, Inc. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include "gpowerprofilemonitor.h" ++#include "gpowerprofilemonitorportal.h" ++#include "gdbuserror.h" ++#include "gdbusproxy.h" ++#include "ginitable.h" ++#include "gioerror.h" ++#include "giomodule-priv.h" ++#include "gportalsupport.h" ++ ++#define G_POWER_PROFILE_MONITOR_PORTAL_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) ++ ++static void g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *iface); ++static void g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface); ++ ++typedef enum ++{ ++ PROP_POWER_SAVER_ENABLED = 1, ++} GPowerProfileMonitorPortalProperty; ++ ++struct _GPowerProfileMonitorPortal ++{ ++ GObject parent_instance; ++ ++ GDBusProxy *proxy; ++ gulong signal_id; ++ gboolean power_saver_enabled; ++}; ++ ++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G_TYPE_OBJECT, ++ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ++ g_power_profile_monitor_portal_initable_iface_init) ++ G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR, ++ g_power_profile_monitor_portal_iface_init) ++ _g_io_modules_ensure_extension_points_registered (); ++ g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME, ++ g_define_type_id, ++ "portal", ++ 40)) ++ ++static void ++g_power_profile_monitor_portal_init (GPowerProfileMonitorPortal *portal) ++{ ++} ++ ++static void ++proxy_properties_changed (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ gpointer user_data) ++{ ++ GPowerProfileMonitorPortal *ppm = user_data; ++ gboolean power_saver_enabled; ++ ++ if (!g_variant_lookup (changed_properties, "power-saver-enabled", "b", &power_saver_enabled)) ++ return; ++ ++ if (power_saver_enabled == ppm->power_saver_enabled) ++ return; ++ ++ ppm->power_saver_enabled = power_saver_enabled; ++ g_object_notify (G_OBJECT (ppm), "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_portal_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object); ++ ++ switch ((GPowerProfileMonitorPortalProperty) prop_id) ++ { ++ case PROP_POWER_SAVER_ENABLED: ++ g_value_set_boolean (value, ppm->power_saver_enabled); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static gboolean ++g_power_profile_monitor_portal_initable_init (GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (initable); ++ GDBusProxy *proxy; ++ gchar *name_owner; ++ ++ if (!glib_should_use_portal ()) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not using portals"); ++ return FALSE; ++ } ++ ++ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ NULL, ++ "org.freedesktop.portal.Desktop", ++ "/org/freedesktop/portal/desktop", ++ "org.freedesktop.portal.PowerProfileMonitor", ++ cancellable, ++ error); ++ if (!proxy) ++ return FALSE; ++ ++ name_owner = g_dbus_proxy_get_name_owner (proxy); ++ ++ if (name_owner == NULL) ++ { ++ g_object_unref (proxy); ++ g_set_error (error, ++ G_DBUS_ERROR, ++ G_DBUS_ERROR_NAME_HAS_NO_OWNER, ++ "Desktop portal not found"); ++ return FALSE; ++ } ++ ++ g_free (name_owner); ++ ++ ppm->signal_id = g_signal_connect (proxy, "g-properties-changed", ++ G_CALLBACK (proxy_properties_changed), ppm); ++ ++ ppm->proxy = g_steal_pointer (&proxy); ++ ++ return TRUE; ++} ++ ++static void ++g_power_profile_monitor_portal_finalize (GObject *object) ++{ ++ GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object); ++ ++ g_clear_signal_handler (&ppm->signal_id, ppm->proxy); ++ g_clear_object (&ppm->proxy); ++ ++ G_OBJECT_CLASS (g_power_profile_monitor_portal_parent_class)->finalize (object); ++} ++ ++static void ++g_power_profile_monitor_portal_class_init (GPowerProfileMonitorPortalClass *nl_class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); ++ ++ gobject_class->get_property = g_power_profile_monitor_portal_get_property; ++ gobject_class->finalize = g_power_profile_monitor_portal_finalize; ++ ++ g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled"); ++} ++ ++static void ++g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *monitor_iface) ++{ ++} ++ ++static void ++g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface) ++{ ++ iface->init = g_power_profile_monitor_portal_initable_init; ++} +diff --git a/gio/gpowerprofilemonitorportal.h b/gio/gpowerprofilemonitorportal.h +new file mode 100644 +index 000000000..b91a14610 +--- /dev/null ++++ b/gio/gpowerprofilemonitorportal.h +@@ -0,0 +1,31 @@ ++/* GIO - GLib Input, Output and Streaming Library ++ * ++ * Copyright 2021 Red Hat, Inc. ++ * ++ * 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 . ++ */ ++ ++#ifndef __G_POWER_PROFILE_MONITOR_PORTAL_H__ ++#define __G_POWER_PROFILE_MONITOR_PORTAL_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_POWER_PROFILE_MONITOR_PORTAL (g_power_profile_monitor_portal_get_type ()) ++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G, POWER_PROFILE_MONITOR_PORTAL, GObject) ++ ++G_END_DECLS ++ ++#endif /* __G_POWER_PROFILE_MONITOR_PORTAL_H__ */ +diff --git a/gio/meson.build b/gio/meson.build +index d5838ed8a..ac3373f2b 100644 +--- a/gio/meson.build ++++ b/gio/meson.build +@@ -383,6 +383,7 @@ if host_system != 'windows' + 'gopenuriportal.c', + 'gmemorymonitorportal.c', + 'gnetworkmonitorportal.c', ++ 'gpowerprofilemonitorportal.c', + 'gproxyresolverportal.c', + 'gtrashportal.c', + 'gportalsupport.c', +-- +GitLab + + +From 18eb29897d80bf662d58bd11a89617ddd7ebfeed Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Tue, 10 Aug 2021 10:58:53 +0200 +Subject: [PATCH 2/4] gio: Add GPowerProfileMonitor tests + +Tests both the portal and direct D-Bus variants. +--- + gio/tests/meson.build | 14 ++- + gio/tests/power-profile-monitor-dbus.py.in | 107 ++++++++++++++++ + gio/tests/power-profile-monitor-portal.py.in | 126 +++++++++++++++++++ + 3 files changed, 241 insertions(+), 6 deletions(-) + create mode 100755 gio/tests/power-profile-monitor-dbus.py.in + create mode 100755 gio/tests/power-profile-monitor-portal.py.in + +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index fc2055101..5dbfb8e60 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -541,27 +541,29 @@ if installed_tests_enabled + install_subdir('static-link', install_dir : installed_tests_execdir) + install_data('static-link.py', install_dir : installed_tests_execdir) + +- memory_monitor_tests = [ ++ monitor_tests = [ + 'memory-monitor-dbus', + 'memory-monitor-portal', ++ 'power-profile-monitor-dbus', ++ 'power-profile-monitor-portal' + ] + +- foreach memory_monitor_test : memory_monitor_tests ++ foreach monitor_test : monitor_tests + cdata = configuration_data() + cdata.set('installed_tests_dir', installed_tests_execdir) +- cdata.set('program', memory_monitor_test + '.py') ++ cdata.set('program', monitor_test + '.py') + cdata.set('env', '') + configure_file( + input: installed_tests_template_tap, +- output: memory_monitor_test + '.test', ++ output: monitor_test + '.test', + install_dir: installed_tests_metadir, + configuration: cdata + ) + cdata = configuration_data() + cdata.set('libexecdir', join_paths(glib_prefix, get_option('libexecdir'))) + configure_file( +- input: memory_monitor_test + '.py.in', +- output: memory_monitor_test + '.py', ++ input: monitor_test + '.py.in', ++ output: monitor_test + '.py', + install_dir : installed_tests_execdir, + configuration: cdata, + ) +diff --git a/gio/tests/power-profile-monitor-dbus.py.in b/gio/tests/power-profile-monitor-dbus.py.in +new file mode 100755 +index 000000000..06e594f4a +--- /dev/null ++++ b/gio/tests/power-profile-monitor-dbus.py.in +@@ -0,0 +1,107 @@ ++#!/usr/bin/python3 ++ ++# This program 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 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++__author__ = 'Bastien Nocera' ++__email__ = 'hadess@hadess.net' ++__copyright__ = '(c) 2019, 2021 Red Hat Inc.' ++__license__ = 'LGPL 3+' ++ ++import unittest ++import sys ++import subprocess ++import fcntl ++import os ++import time ++ ++import taptestrunner ++ ++try: ++ # Do all non-standard imports here so we can skip the tests if any ++ # needed packages are not available. ++ import dbus ++ import dbus.mainloop.glib ++ import dbusmock ++ from gi.repository import GLib ++ from gi.repository import Gio ++ ++ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) ++ ++ class TestPowerProfileMonitor(dbusmock.DBusTestCase): ++ '''Test GPowerProfileMonitorDBus''' ++ ++ @classmethod ++ def setUpClass(klass): ++ klass.start_system_bus() ++ klass.dbus_con = klass.get_dbus(True) ++ ++ def setUp(self): ++ try: ++ Gio.PowerProfileMonitor ++ except AttributeError: ++ raise unittest.SkipTest('Power Profile Monitor not in ' ++ 'introspection data. Requires ' ++ 'GObject-Introspection ≥ 1.63.2') # FIXME version ++ try: ++ (self.p_mock, self.obj_ppd) = self.spawn_server_template( ++ 'power_profiles_daemon', {}, stdout=subprocess.PIPE) ++ except ModuleNotFoundError: ++ raise unittest.SkipTest("power-profiles-daemon dbusmock template not " ++ "found. Requires dbusmock > 0.23.1.") # FIXME version ++ # set log to nonblocking ++ flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) ++ fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) ++ self.power_saver_enabled = False ++ self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) ++ self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() ++ self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb) ++ self.mainloop = GLib.MainLoop() ++ self.main_context = self.mainloop.get_context() ++ ++ def tearDown(self): ++ self.p_mock.terminate() ++ self.p_mock.wait() ++ ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ ++ def power_saver_enabled_cb(self, spec, data): ++ self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() ++ self.main_context.wakeup() ++ ++ def test_power_profile_power_saver_enabled(self): ++ '''power-saver-enabled property''' ++ ++ self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) ++ ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) ++ ++except ImportError as e: ++ @unittest.skip("Cannot import %s" % e.name) ++ class TestPowerProfileMonitor(unittest.TestCase): ++ def test_power_profile_power_saver_enabled(self): ++ pass ++ ++if __name__ == '__main__': ++ unittest.main(testRunner=taptestrunner.TAPTestRunner()) +diff --git a/gio/tests/power-profile-monitor-portal.py.in b/gio/tests/power-profile-monitor-portal.py.in +new file mode 100755 +index 000000000..960a62232 +--- /dev/null ++++ b/gio/tests/power-profile-monitor-portal.py.in +@@ -0,0 +1,126 @@ ++#!/usr/bin/python3 ++ ++# This program 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 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++__author__ = 'Bastien Nocera' ++__email__ = 'hadess@hadess.net' ++__copyright__ = '(c) 2021 Red Hat Inc.' ++__license__ = 'LGPL 3+' ++ ++import unittest ++import sys ++import subprocess ++import fcntl ++import os ++import time ++ ++import taptestrunner ++ ++try: ++ # Do all non-standard imports here so we can skip the tests if any ++ # needed packages are not available. ++ import dbus ++ import dbus.mainloop.glib ++ import dbusmock ++ from gi.repository import GLib ++ from gi.repository import Gio ++ ++ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) ++ ++ # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") ++ XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" ++ ++ class TestPowerProfileMonitorPortal(dbusmock.DBusTestCase): ++ '''Test GPowerProfileMonitorPortal''' ++ ++ @classmethod ++ def setUpClass(klass): ++ klass.start_system_bus() ++ klass.dbus_con = klass.get_dbus(True) ++ # Start session bus so that xdg-desktop-portal can run on it ++ klass.start_session_bus() ++ ++ def setUp(self): ++ try: ++ Gio.PowerProfileMonitor ++ except AttributeError: ++ raise unittest.SkipTest('Power Profile Monitor not in ' ++ 'introspection data. Requires ' ++ 'GObject-Introspection > 1.69.0') ++ try: ++ (self.p_mock, self.obj_ppd) = self.spawn_server_template( ++ 'power_profiles_daemon', {}, stdout=subprocess.PIPE) ++ except ModuleNotFoundError: ++ raise unittest.SkipTest("power-profiles-daemon dbusmock template not " ++ "found. Requires dbusmock > 0.23.1.") ++ # set log to nonblocking ++ flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) ++ fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) ++ self.power_saver_enabled = False ++ self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE) ++ try: ++ self.xdp = subprocess.Popen([XDG_DESKTOP_PORTAL_PATH]) ++ except FileNotFoundError: ++ raise unittest.SkipTest("xdg-desktop-portal not available") ++ ++ try: ++ self.wait_for_bus_object('org.freedesktop.portal.Desktop', ++ '/org/freedesktop/portal/desktop') ++ except: ++ raise ++ # subprocess.Popen(['gdbus', 'monitor', '--session', '--dest', 'org.freedesktop.portal.Desktop']) ++ ++ os.environ['GTK_USE_PORTAL'] = "1" ++ self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default() ++ assert("GPowerProfileMonitorPortal" in str(self.power_profile_monitor)) ++ self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb) ++ self.mainloop = GLib.MainLoop() ++ self.main_context = self.mainloop.get_context() ++ ++ def tearDown(self): ++ self.p_mock.terminate() ++ self.p_mock.wait() ++ ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ ++ def power_saver_enabled_cb(self, spec, data): ++ self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled() ++ self.main_context.wakeup() ++ ++ def test_power_profile_power_saver_enabled_portal(self): ++ '''power-saver-enabled property''' ++ ++ self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False) ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10) ++ ++ self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1)) ++ self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10) ++ ++except ImportError as e: ++ @unittest.skip("Cannot import %s" % e.name) ++ class TestPowerProfileMonitorPortal(unittest.TestCase): ++ def test_power_profile_power_saver_enabled_portal(self): ++ pass ++ ++if __name__ == '__main__': ++ unittest.main(testRunner=taptestrunner.TAPTestRunner()) +-- +GitLab + + +From 66acea8418eb3d8e46bb6f93dc0c3f13a1f7822b Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 11 Aug 2021 15:37:40 +0200 +Subject: [PATCH 3/4] gio: Remove left-over debug statement from memory monitor + portal test + +--- + gio/tests/memory-monitor-portal.py.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in +index cb4a960eb..f5fd2283f 100755 +--- a/gio/tests/memory-monitor-portal.py.in ++++ b/gio/tests/memory-monitor-portal.py.in +@@ -31,7 +31,6 @@ try: + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + +- # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal") + XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal" + + class TestLowMemoryMonitorPortal(dbusmock.DBusTestCase): +-- +GitLab + + +From 2e9842cafc73a7fb94cfde7937e125e1a91f35f8 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Wed, 11 Aug 2021 15:38:12 +0200 +Subject: [PATCH 4/4] gio: Simplify memory monitor tests by using + assertEventually() helper + +assertEventually is a helper used in a number of projects that use +dbusmock. + +See https://github.com/martinpitt/python-dbusmock/issues/82 +--- + gio/tests/memory-monitor-dbus.py.in | 31 ++++++++++++++++----------- + gio/tests/memory-monitor-portal.py.in | 31 ++++++++++++++++----------- + 2 files changed, 38 insertions(+), 24 deletions(-) + +diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in +index 7823e7309..e8ac28faf 100755 +--- a/gio/tests/memory-monitor-dbus.py.in ++++ b/gio/tests/memory-monitor-dbus.py.in +@@ -66,6 +66,23 @@ try: + self.p_mock.terminate() + self.p_mock.wait() + ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ + def memory_warning_cb(self, monitor, level): + self.last_warning = level + self.main_context.wakeup() +@@ -82,21 +99,11 @@ try: + + self.dbusmock.EmitWarning(100) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 100: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 100) ++ self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20) + + self.dbusmock.EmitWarning(255) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 255: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 255) ++ self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20) + + except ImportError as e: + @unittest.skip("Cannot import %s" % e.name) +diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in +index f5fd2283f..36d5094d3 100755 +--- a/gio/tests/memory-monitor-portal.py.in ++++ b/gio/tests/memory-monitor-portal.py.in +@@ -84,6 +84,23 @@ try: + self.p_mock.terminate() + self.p_mock.wait() + ++ def assertEventually(self, condition, message=None, timeout=50): ++ '''Assert that condition function eventually returns True. ++ ++ Timeout is in deciseconds, defaulting to 50 (5 seconds). message is ++ printed on failure. ++ ''' ++ while timeout >= 0: ++ context = GLib.MainContext.default() ++ while context.iteration(False): ++ pass ++ if condition(): ++ break ++ timeout -= 1 ++ time.sleep(0.1) ++ else: ++ self.fail(message or 'timed out waiting for ' + str(condition)) ++ + def portal_memory_warning_cb(self, monitor, level): + self.last_warning = level + self.main_context.wakeup() +@@ -100,21 +117,11 @@ try: + + self.dbusmock.EmitWarning(100) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 100: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 100) ++ self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20) + + self.dbusmock.EmitWarning(255) + # Wait 2 seconds or until warning +- timeout = 2 +- while timeout > 0 and self.last_warning != 255: +- time.sleep(0.5) +- timeout -= 0.5 +- self.main_context.iteration(False) +- self.assertEqual(self.last_warning, 255) ++ self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20) + + except ImportError as e: + @unittest.skip("Cannot import %s" % e.name) +-- +GitLab + diff --git a/base/rx/rx-glib2/2244.patch b/base/rx/rx-glib2/2244.patch new file mode 100644 index 0000000..cb55031 --- /dev/null +++ b/base/rx/rx-glib2/2244.patch @@ -0,0 +1,49 @@ +From b6036e23b0477be147211b4e21a6b49cd4d6c9a0 Mon Sep 17 00:00:00 2001 +From: Jamie Bainbridge +Date: Wed, 8 Sep 2021 12:08:17 +1000 +Subject: [PATCH] gutils: Avoid segfault in g_get_user_database_entry + +g_get_user_database_entry() uses variable pwd to store the contents of +the call to getpwnam_r(), then capitalises the first letter of pw_name +with g_ascii_toupper (pw->pw_name[0]). + +However, as per the getpwnam manpage, the result of that call "may point +to a static area". When this happens, GLib is trying to edit static +memory which belongs to a shared library, so segfaults. + +Instead, copy pw_name off to a temporary variable, set uppercase on +that variable, and use the variable to join into the desired string. +Free the new variable after it is no longer needed. + +Signed-off-by: Jamie Bainbridge +--- + glib/gutils.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/glib/gutils.c b/glib/gutils.c +index b7a2113d4..4bccd7229 100644 +--- a/glib/gutils.c ++++ b/glib/gutils.c +@@ -692,14 +692,17 @@ g_get_user_database_entry (void) + { + gchar **gecos_fields; + gchar **name_parts; ++ gchar *uppercase_pw_name; + + /* split the gecos field and substitute '&' */ + gecos_fields = g_strsplit (pw->pw_gecos, ",", 0); + name_parts = g_strsplit (gecos_fields[0], "&", 0); +- pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]); +- e.real_name = g_strjoinv (pw->pw_name, name_parts); ++ uppercase_pw_name = g_strdup (pw->pw_name); ++ uppercase_pw_name[0] = g_ascii_toupper (uppercase_pw_name[0]); ++ e.real_name = g_strjoinv (uppercase_pw_name, name_parts); + g_strfreev (gecos_fields); + g_strfreev (name_parts); ++ g_free (uppercase_pw_name); + } + #endif + +-- +GitLab + diff --git a/base/rx/rx-glib2/2291.patch b/base/rx/rx-glib2/2291.patch new file mode 100644 index 0000000..2e4e44d --- /dev/null +++ b/base/rx/rx-glib2/2291.patch @@ -0,0 +1,129 @@ +From f419966808475cb6c0f0ba2f63967876218ffdaf Mon Sep 17 00:00:00 2001 +From: Julian Andres Klode +Date: Tue, 12 Oct 2021 12:01:50 +0200 +Subject: [PATCH 1/2] gnetworkmonitornm: Stop using removed PropertiesChanged + signal + +Use the org.freedesktop.DBus.Properties interface to listen +to PropertiesChanged signals on /org/freedesktop/NetworkManager. + +NetworkManager used to provide its own legacy PropertiesChanged +signal, but that was dropped in +https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/853 + +This requires NetworkManager >= 1.2 (2016) + +Fixes: #2505 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1946196 +--- + gio/gnetworkmonitornm.c | 29 +++++++---------------------- + 1 file changed, 7 insertions(+), 22 deletions(-) + +diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c +index 5a36a0ba1..6a6d1d666 100644 +--- a/gio/gnetworkmonitornm.c ++++ b/gio/gnetworkmonitornm.c +@@ -267,29 +267,14 @@ update_cached_property (GDBusProxy *proxy, + } + + static void +-proxy_signal_cb (GDBusProxy *proxy, +- const gchar *sender_name, +- const gchar *signal_name, +- GVariant *parameters, +- GNetworkMonitorNM *nm) ++proxy_properties_changed_cb (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ GNetworkMonitorNM *nm) + { +- GVariant *asv; + GVariantDict *dict; + +- if (g_strcmp0 (signal_name, "PropertiesChanged") != 0) +- return; +- +- g_variant_get (parameters, "(@a{sv})", &asv); +- if (!asv) +- return; +- +- dict = g_variant_dict_new (asv); +- g_variant_unref (asv); +- if (!dict) +- { +- g_warning ("Failed to handle PropertiesChanged signal from NetworkManager"); +- return; +- } ++ dict = g_variant_dict_new (changed_properties); + + update_cached_property (nm->priv->proxy, "Connectivity", dict); + +@@ -361,8 +346,8 @@ g_network_monitor_nm_initable_init (GInitable *initable, + return FALSE; + } + +- nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal", +- G_CALLBACK (proxy_signal_cb), nm); ++ nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed", ++ G_CALLBACK (proxy_properties_changed_cb), nm); + nm->priv->proxy = proxy; + sync_properties (nm, FALSE); + +-- +GitLab + + +From 643fc7ea49e818310f6b3f6e4ebe621c7a4d6bd7 Mon Sep 17 00:00:00 2001 +From: Julian Andres Klode +Date: Tue, 12 Oct 2021 17:31:42 +0200 +Subject: [PATCH 2/2] gnetworkmonitornm: Do not re-update cached property + +GDBusProxy already takes care of updating the cached property +before emitting the signal, so there is no need to do this +a second time ourselves. +--- + gio/gnetworkmonitornm.c | 22 ---------------------- + 1 file changed, 22 deletions(-) + +diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c +index 6a6d1d666..a8040fb36 100644 +--- a/gio/gnetworkmonitornm.c ++++ b/gio/gnetworkmonitornm.c +@@ -252,34 +252,12 @@ sync_properties (GNetworkMonitorNM *nm, + } + } + +-static void +-update_cached_property (GDBusProxy *proxy, +- const char *property_name, +- GVariantDict *dict) +-{ +- GVariant *v; +- +- v = g_variant_dict_lookup_value (dict, property_name, NULL); +- if (!v) +- return; +- g_dbus_proxy_set_cached_property (proxy, property_name, v); +- g_variant_unref (v); +-} +- + static void + proxy_properties_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + GNetworkMonitorNM *nm) + { +- GVariantDict *dict; +- +- dict = g_variant_dict_new (changed_properties); +- +- update_cached_property (nm->priv->proxy, "Connectivity", dict); +- +- g_variant_dict_unref (dict); +- + sync_properties (nm, TRUE); + } + +-- +GitLab + diff --git a/base/rx/rx-glib2/2408.patch b/base/rx/rx-glib2/2408.patch new file mode 100644 index 0000000..90d8953 --- /dev/null +++ b/base/rx/rx-glib2/2408.patch @@ -0,0 +1,391 @@ +From 0bbd63bf1945c6f3e1c88232521e1618c21d44f2 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 23 Dec 2021 17:45:51 +0000 +Subject: [PATCH 1/4] gmain: Use waitid() on pidfds rather than a global + SIGCHLD handler +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When the system supports it (as all Linux kernels ≥ 5.3 should), it’s +preferable to use `pidfd_open()` and `waitid()` to be notified of +child processes exiting or being signalled, rather than installing a +default `SIGCHLD` handler. + +A default `SIGCHLD` handler is global, and can never interact well with +other code (from the application or other libraries) which also wants to +install a `SIGCHLD` handler. + +This use of `pidfd_open()` is racy (the PID may be reused between +`g_child_watch_source_new()` being called and `pidfd_open()` being +called), so it doesn’t improve behaviour there. For that, we’d need +continuous use of pidfds throughout GLib, from fork/spawn time until +here. See #1866 for that. + +The use of `waitid()` to get the process exit status could be expanded +in future to also work for stopped or continued processes (as per #175) +by adding `WSTOPPED | WCONTINUED` into the flags. That’s a behaviour +change which is outside the strict scope of adding pidfd support, +though. + +Signed-off-by: Philip Withnall + +Helps: #1866 +Fixes: #2216 +--- + glib/gmain.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++--- + meson.build | 14 ++++++ + 2 files changed, 125 insertions(+), 6 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index 15581ee7a..e9965f7f6 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -67,6 +67,12 @@ + #include + #include + ++#ifdef HAVE_PIDFD ++#include ++#include ++#include /* P_PIDFD */ ++#endif /* HAVE_PIDFD */ ++ + #ifdef G_OS_WIN32 + #define STRICT + #include +@@ -329,10 +335,11 @@ struct _GChildWatchSource + GSource source; + GPid pid; + gint child_status; +-#ifdef G_OS_WIN32 ++ /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */ + GPollFD poll; +-#else /* G_OS_WIN32 */ +- gboolean child_exited; /* (atomic) */ ++#ifndef G_OS_WIN32 ++ gboolean child_exited; /* (atomic); not used iff @using_pidfd is set */ ++ gboolean using_pidfd; + #endif /* G_OS_WIN32 */ + }; + +@@ -5325,7 +5332,8 @@ dispatch_unix_signals_unlocked (void) + { + GChildWatchSource *source = node->data; + +- if (!g_atomic_int_get (&source->child_exited)) ++ if (!source->using_pidfd && ++ !g_atomic_int_get (&source->child_exited)) + { + pid_t pid; + do +@@ -5384,6 +5392,38 @@ g_child_watch_prepare (GSource *source, + return g_atomic_int_get (&child_watch_source->child_exited); + } + ++#ifdef HAVE_PIDFD ++static int ++siginfo_t_to_wait_status (const siginfo_t *info) ++{ ++ /* Each of these returns is essentially the inverse of WIFEXITED(), ++ * WIFSIGNALED(), etc. */ ++ switch (info->si_code) ++ { ++ case CLD_EXITED: ++ return W_EXITCODE (info->si_status, 0); ++ case CLD_KILLED: ++ return W_EXITCODE (0, info->si_status); ++ case CLD_DUMPED: ++#ifdef WCOREFLAG ++ return W_EXITCODE (0, info->si_status | WCOREFLAG); ++#else ++ g_assert_not_reached (); ++#endif ++ case CLD_CONTINUED: ++#ifdef __W_CONTINUED ++ return __W_CONTINUED; ++#else ++ g_assert_not_reached (); ++#endif ++ case CLD_STOPPED: ++ case CLD_TRAPPED: ++ default: ++ return W_STOPCODE (info->si_status); ++ } ++} ++#endif /* HAVE_PIDFD */ ++ + static gboolean + g_child_watch_check (GSource *source) + { +@@ -5391,6 +5431,34 @@ g_child_watch_check (GSource *source) + + child_watch_source = (GChildWatchSource *) source; + ++#ifdef HAVE_PIDFD ++ if (child_watch_source->using_pidfd) ++ { ++ gboolean child_exited = child_watch_source->poll.revents & G_IO_IN; ++ ++ if (child_exited) ++ { ++ siginfo_t child_info = { 0, }; ++ ++ /* Get the exit status */ ++ if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && ++ child_info.si_pid != 0) ++ { ++ /* waitid() helpfully provides the wait status in a decomposed ++ * form which is quite useful. Unfortunately we have to report it ++ * to the #GChildWatchFunc as a waitpid()-style platform-specific ++ * wait status, so that the user code in #GChildWatchFunc can then ++ * call WIFEXITED() (etc.) on it. That means re-composing the ++ * status information. */ ++ child_watch_source->child_status = siginfo_t_to_wait_status (&child_info); ++ child_watch_source->child_exited = TRUE; ++ } ++ } ++ ++ return child_exited; ++ } ++#endif /* HAVE_PIDFD */ ++ + return g_atomic_int_get (&child_watch_source->child_exited); + } + +@@ -5575,6 +5643,11 @@ g_unix_signal_watch_finalize (GSource *source) + static void + g_child_watch_finalize (GSource *source) + { ++ GChildWatchSource *child_watch_source = (GChildWatchSource *) source; ++ ++ if (child_watch_source->using_pidfd) ++ return; ++ + G_LOCK (unix_signal_lock); + unix_child_watches = g_slist_remove (unix_child_watches, source); + unref_unix_signal_handler_unlocked (SIGCHLD); +@@ -5676,6 +5749,9 @@ g_child_watch_source_new (GPid pid) + { + GSource *source; + GChildWatchSource *child_watch_source; ++#ifdef HAVE_PIDFD ++ int errsv; ++#endif + + #ifndef G_OS_WIN32 + g_return_val_if_fail (pid > 0, NULL); +@@ -5694,14 +5770,43 @@ g_child_watch_source_new (GPid pid) + child_watch_source->poll.events = G_IO_IN; + + g_source_add_poll (source, &child_watch_source->poll); +-#else /* G_OS_WIN32 */ ++#else /* !G_OS_WIN32 */ ++ ++#ifdef HAVE_PIDFD ++ /* Use a pidfd, if possible, to avoid having to install a global SIGCHLD ++ * handler and potentially competing with any other library/code which wants ++ * to install one. ++ * ++ * Unfortunately this use of pidfd isn’t race-free (the PID could be recycled ++ * between the caller calling g_child_watch_source_new() and here), but it’s ++ * better than SIGCHLD. ++ */ ++ child_watch_source->poll.fd = (int) syscall (SYS_pidfd_open, pid, 0); ++ errsv = errno; ++ ++ if (child_watch_source->poll.fd >= 0) ++ { ++ child_watch_source->using_pidfd = TRUE; ++ child_watch_source->poll.events = G_IO_IN; ++ g_source_add_poll (source, &child_watch_source->poll); ++ ++ return source; ++ } ++ else ++ { ++ g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s", ++ pid, g_strerror (errsv)); ++ /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */ ++ } ++#endif /* HAVE_PIDFD */ ++ + G_LOCK (unix_signal_lock); + ref_unix_signal_handler_unlocked (SIGCHLD); + unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source); + if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0) + child_watch_source->child_exited = TRUE; + G_UNLOCK (unix_signal_lock); +-#endif /* G_OS_WIN32 */ ++#endif /* !G_OS_WIN32 */ + + return source; + } +diff --git a/meson.build b/meson.build +index a0223ce5b..1e1bd602c 100644 +--- a/meson.build ++++ b/meson.build +@@ -810,6 +810,20 @@ if cc.links('''#include + glib_conf.set('HAVE_EVENTFD', 1) + endif + ++# Check for pidfd_open(2) ++if cc.links('''#include ++ #include ++ #include ++ #include ++ int main (int argc, char ** argv) { ++ siginfo_t child_info = { 0, }; ++ syscall (SYS_pidfd_open, 0, 0); ++ waitid (P_PIDFD, 0, &child_info, WEXITED | WNOHANG); ++ return 0; ++ }''', name : 'pidfd_open(2) system call') ++ glib_conf.set('HAVE_PIDFD', 1) ++endif ++ + # Check for __uint128_t (gcc) by checking for 128-bit division + uint128_t_src = '''int main() { + static __uint128_t v1 = 100; +-- +2.41.0 + + +From 13c62bc181c6da9f287b737f7a3238e0269b40b3 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Tue, 2 Aug 2022 12:35:40 -0700 +Subject: [PATCH 2/4] gmain: close pidfd when finalizing GChildWatchSource + +A file-descriptor was created with the introduction of pidfd_getfd() but +nothing is closing it when the source finalizes. The GChildWatchSource is +the creator and consumer of this FD and therefore responsible for closing +it on finalization. + +The pidfd leak was introduced in !2408. + +This fixes issues with Builder where anon_inode:[pidfd] exhaust the +available FD limit for the process. + +Fixes #2708 +--- + glib/gmain.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index e9965f7f6..3ceec61ee 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -5646,7 +5646,11 @@ g_child_watch_finalize (GSource *source) + GChildWatchSource *child_watch_source = (GChildWatchSource *) source; + + if (child_watch_source->using_pidfd) +- return; ++ { ++ if (child_watch_source->poll.fd >= 0) ++ close (child_watch_source->poll.fd); ++ return; ++ } + + G_LOCK (unix_signal_lock); + unix_child_watches = g_slist_remove (unix_child_watches, source); +-- +2.41.0 + + +From 378c72cbe12767b8f6aedc19c7ca46c07aa1ca73 Mon Sep 17 00:00:00 2001 +From: Owen Rafferty +Date: Tue, 12 Jul 2022 20:03:56 -0500 +Subject: [PATCH 3/4] gmain: define non-posix symbols + +--- + glib/gmain.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/glib/gmain.c b/glib/gmain.c +index 3ceec61ee..a2d7bb3ba 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -71,6 +71,12 @@ + #include + #include + #include /* P_PIDFD */ ++#ifndef W_EXITCODE ++#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig)) ++#endif ++#ifndef W_STOPCODE ++#define W_STOPCODE(sig) ((sig) << 8 | 0x7f) ++#endif + #endif /* HAVE_PIDFD */ + + #ifdef G_OS_WIN32 +-- +2.41.0 + + +From aac37188ce26366bd86626700d49cee0cb121472 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Wed, 21 Dec 2022 12:11:46 +0000 +Subject: [PATCH 4/4] gmain: Define fallback values for siginfo_t constants for + musl +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +musl doesn’t define them itself, presumably because they’re not defined +in POSIX. glibc does define them. Thankfully, the values used in glibc +match the values used internally in other musl macros. + +Define the values as a fallback. As a result of this, we can get rid of +the `g_assert_if_reached()` checks in `siginfo_t_to_wait_status()`. + +This should fix catching signals from a subprocess when built against +musl. + +Signed-off-by: Philip Withnall + +Fixes: #2852 +--- + glib/gmain.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index a2d7bb3ba..f0cf700c0 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -77,6 +77,16 @@ + #ifndef W_STOPCODE + #define W_STOPCODE(sig) ((sig) << 8 | 0x7f) + #endif ++#ifndef WCOREFLAG ++/* musl doesn’t define WCOREFLAG while glibc does. Unfortunately, there’s no way ++ * to detect we’re building against musl, so just define it and hope. ++ * See https://git.musl-libc.org/cgit/musl/tree/include/sys/wait.h#n51 */ ++#define WCOREFLAG 0x80 ++#endif ++#ifndef __W_CONTINUED ++/* Same as above, for musl */ ++#define __W_CONTINUED 0xffff ++#endif + #endif /* HAVE_PIDFD */ + + #ifdef G_OS_WIN32 +@@ -5411,17 +5421,9 @@ siginfo_t_to_wait_status (const siginfo_t *info) + case CLD_KILLED: + return W_EXITCODE (0, info->si_status); + case CLD_DUMPED: +-#ifdef WCOREFLAG + return W_EXITCODE (0, info->si_status | WCOREFLAG); +-#else +- g_assert_not_reached (); +-#endif + case CLD_CONTINUED: +-#ifdef __W_CONTINUED + return __W_CONTINUED; +-#else +- g_assert_not_reached (); +-#endif + case CLD_STOPPED: + case CLD_TRAPPED: + default: +-- +2.41.0 + diff --git a/base/rx/rx-glib2/2435.patch b/base/rx/rx-glib2/2435.patch new file mode 100644 index 0000000..862503b --- /dev/null +++ b/base/rx/rx-glib2/2435.patch @@ -0,0 +1,132 @@ +From a879d08e912a4421786b44af479f94f7b4503f5a Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 17 Jan 2022 15:27:24 +0000 +Subject: [PATCH] gspawn: Report errors with closing file descriptors between + fork/exec +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If a seccomp policy is set up incorrectly so that it returns `EPERM` for +`close_range()` rather than `ENOSYS` due to it not being recognised, no +error would previously be reported from GLib, but some file descriptors +wouldn’t be closed, and that would cause a hung zombie process. The +zombie process would be waiting for one half of a socket to be closed. + +Fix that by correctly propagating errors from `close_range()` back to the +parent process so they can be reported correctly. + +Distributions which aren’t yet carrying the Docker fix to correctly +return `ENOSYS` from unrecognised syscalls may want to temporarily carry +an additional patch to fall back to `safe_fdwalk()` if `close_range()` +fails with `EPERM`. This change will not be accepted upstream as `EPERM` +is not the right error for `close_range()` to be returning. + +Signed-off-by: Philip Withnall + +Fixes: #2580 +--- + glib/gspawn.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/glib/gspawn.c b/glib/gspawn.c +index c2fe306dc..9c2f7ba7b 100644 +--- a/glib/gspawn.c ++++ b/glib/gspawn.c +@@ -1457,8 +1457,10 @@ safe_fdwalk (int (*cb)(void *data, int fd), void *data) + } + + /* This function is called between fork() and exec() and hence must be +- * async-signal-safe (see signal-safety(7)). */ +-static void ++ * async-signal-safe (see signal-safety(7)). ++ * ++ * On failure, `-1` will be returned and errno will be set. */ ++static int + safe_closefrom (int lowfd) + { + #if defined(__FreeBSD__) || defined(__OpenBSD__) +@@ -1472,6 +1474,7 @@ safe_closefrom (int lowfd) + * should be safe to use. + */ + (void) closefrom (lowfd); ++ return 0; + #elif defined(__DragonFly__) + /* It is unclear whether closefrom function included in DragonFlyBSD libc_r + * is safe to use because it calls a lot of library functions. It is also +@@ -1479,12 +1482,13 @@ safe_closefrom (int lowfd) + * direct system call here ourselves to avoid possible issues. + */ + (void) syscall (SYS_closefrom, lowfd); ++ return 0; + #elif defined(F_CLOSEM) + /* NetBSD and AIX have a special fcntl command which does the same thing as + * closefrom. NetBSD also includes closefrom function, which seems to be a + * simple wrapper of the fcntl command. + */ +- (void) fcntl (lowfd, F_CLOSEM); ++ return fcntl (lowfd, F_CLOSEM); + #else + + #if defined(HAVE_CLOSE_RANGE) +@@ -1494,9 +1498,11 @@ safe_closefrom (int lowfd) + * + * Handle ENOSYS in case it’s supported in libc but not the kernel; if so, + * fall back to safe_fdwalk(). */ +- if (close_range (lowfd, G_MAXUINT, 0) != 0 && errno == ENOSYS) ++ int ret = close_range (lowfd, G_MAXUINT, 0); ++ if (ret == 0 || errno != ENOSYS) ++ return ret; + #endif /* HAVE_CLOSE_RANGE */ +- (void) safe_fdwalk (close_func, GINT_TO_POINTER (lowfd)); ++ return safe_fdwalk (close_func, GINT_TO_POINTER (lowfd)); + #endif + } + +@@ -1534,7 +1540,8 @@ enum + CHILD_EXEC_FAILED, + CHILD_OPEN_FAILED, + CHILD_DUP2_FAILED, +- CHILD_FORK_FAILED ++ CHILD_FORK_FAILED, ++ CHILD_CLOSE_FAILED, + }; + + /* This function is called between fork() and exec() and hence must be +@@ -1650,12 +1657,14 @@ do_exec (gint child_err_report_fd, + if (safe_dup2 (child_err_report_fd, 3) < 0) + write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); + set_cloexec (GINT_TO_POINTER (0), 3); +- safe_closefrom (4); ++ if (safe_closefrom (4) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED); + child_err_report_fd = 3; + } + else + { +- safe_fdwalk (set_cloexec, GINT_TO_POINTER (3)); ++ if (safe_fdwalk (set_cloexec, GINT_TO_POINTER (3)) < 0) ++ write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED); + } + } + else +@@ -2446,7 +2455,15 @@ fork_exec (gboolean intermediate_child, + _("Failed to fork child process (%s)"), + g_strerror (buf[1])); + break; +- ++ ++ case CHILD_CLOSE_FAILED: ++ g_set_error (error, ++ G_SPAWN_ERROR, ++ G_SPAWN_ERROR_FAILED, ++ _("Failed to close file descriptor for child process (%s)"), ++ g_strerror (buf[1])); ++ break; ++ + default: + g_set_error (error, + G_SPAWN_ERROR, +-- +2.34.1 + diff --git a/base/rx/rx-glib2/2826.patch b/base/rx/rx-glib2/2826.patch new file mode 100644 index 0000000..83d527a --- /dev/null +++ b/base/rx/rx-glib2/2826.patch @@ -0,0 +1,278 @@ +From 764f071909df70622e79ee71323973c18c055c8c Mon Sep 17 00:00:00 2001 +From: Giuseppe Scrivano +Date: Mon, 14 Sep 2020 16:28:10 +0200 +Subject: [PATCH 1/5] gdbusauth: empty DATA does not need a trailing space + +This is an interoperability fix. If the line is exactly "DATA\r\n", +the reference implementation of D-Bus treats this as equivalent to +"DATA \r\n", meaning the data block consists of zero hex-encoded bytes. +In practice, D-Bus clients send empty data blocks as "DATA\r\n", and +in fact sd-bus only accepts that, rejecting "DATA \r\n". + +[Originally part of a larger commit; commit message added by smcv] + +Signed-off-by: Giuseppe Scrivano +Co-authored-by: Simon McVittie +Signed-off-by: Simon McVittie +--- + gio/gdbusauth.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c +index ede21c8514..d2ca41a201 100644 +--- a/gio/gdbusauth.c ++++ b/gio/gdbusauth.c +@@ -783,13 +783,13 @@ _g_dbus_auth_run_client (GDBusAuth *auth, + if (line == NULL) + goto out; + debug_print ("CLIENT: WaitingForData, read='%s'", line); +- if (g_str_has_prefix (line, "DATA ")) ++ if (g_str_equal (line, "DATA") || g_str_has_prefix (line, "DATA ")) + { + gchar *encoded; + gchar *decoded_data; + gsize decoded_data_len = 0; + +- encoded = g_strdup (line + 5); ++ encoded = g_strdup (line + 4); + g_free (line); + g_strstrip (encoded); + decoded_data = hexdecode (encoded, &decoded_data_len, error); +@@ -1255,13 +1255,13 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + debug_print ("SERVER: WaitingForData, read '%s'", line); + if (line == NULL) + goto out; +- if (g_str_has_prefix (line, "DATA ")) ++ if (g_str_equal (line, "DATA") || g_str_has_prefix (line, "DATA ")) + { + gchar *encoded; + gchar *decoded_data; + gsize decoded_data_len = 0; + +- encoded = g_strdup (line + 5); ++ encoded = g_strdup (line + 4); + g_free (line); + g_strstrip (encoded); + decoded_data = hexdecode (encoded, &decoded_data_len, error); +-- +GitLab + + +From a7d2e727eefcf883bb463ad559f5632e8e448757 Mon Sep 17 00:00:00 2001 +From: Giuseppe Scrivano +Date: Mon, 14 Sep 2020 16:28:10 +0200 +Subject: [PATCH 2/5] GDBusServer: If no initial response for EXTERNAL, send a + challenge + +Sending an "initial response" along with the AUTH command is meant +to be an optional optimization, and clients are allowed to omit it. +We must reply with our initial challenge, which in the case of EXTERNAL +is an empty string: the client responds to that with the authorization +identity. + +If we do not reply to the AUTH command, then the client will wait +forever for our reply, while we wait forever for the reply that we +expect the client to send, resulting in deadlock. + +D-Bus does not have a way to distinguish between an empty initial +response and the absence of an initial response, so clients that want +to use an empty authorization identity, such as systed's sd-bus, +cannot use the initial-response optimization and will fail to connect +to a GDBusServer that does not have this change. + +[Originally part of a larger commit; commit message added by smcv.] + +Signed-off-by: Simon McVittie +--- + gio/gdbusauthmechanismexternal.c | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c +index 617fe1d0e5..ddd06cbd5e 100644 +--- a/gio/gdbusauthmechanismexternal.c ++++ b/gio/gdbusauthmechanismexternal.c +@@ -40,6 +40,7 @@ struct _GDBusAuthMechanismExternalPrivate + gboolean is_client; + gboolean is_server; + GDBusAuthMechanismState state; ++ gboolean empty_data_sent; + }; + + static gint mechanism_get_priority (void); +@@ -253,7 +254,9 @@ mechanism_server_initiate (GDBusAuthMechanism *mechanism, + } + else + { +- m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA; ++ /* The initial-response optimization was not used, so we need to ++ * send an empty challenge to prompt the client to respond. */ ++ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND; + } + } + +@@ -288,12 +291,22 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism, + + g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL); + g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL); +- g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL); + +- /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */ +- g_assert_not_reached (); ++ if (out_data_len) ++ *out_data_len = 0; + +- return NULL; ++ if (m->priv->empty_data_sent) ++ { ++ /* We have already sent an empty data response. ++ Reject the connection. */ ++ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED; ++ return NULL; ++ } ++ ++ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA; ++ m->priv->empty_data_sent = TRUE; ++ ++ return g_strdup (""); + } + + static gchar * +-- +GitLab + + +From b51e3ab09e39c590c65a7be6228ecfa48a6189f6 Mon Sep 17 00:00:00 2001 +From: Giuseppe Scrivano +Date: Mon, 14 Sep 2020 16:28:10 +0200 +Subject: [PATCH 3/5] GDBusServer: Accept empty authorization identity for + EXTERNAL mechanism + +RFC 4422 appendix A defines the empty authorization identity to mean +the identity that the server associated with its authentication +credentials. In this case, this means whatever uid is in the +GCredentials object. + +In particular, this means that clients in a different Linux user +namespace can authenticate against our server and will be authorized +as the version of their uid that is visible in the server's namespace, +even if the corresponding numeric uid returned by geteuid() in the +client's namespace was different. systemd's sd-bus has relied on this +since commit +https://github.com/systemd/systemd/commit/1ed4723d38cd0d1423c8fe650f90fa86007ddf55. + +[Originally part of a larger commit; commit message added by smcv] + +Signed-off-by: Simon McVittie +--- + gio/gdbusauthmechanismexternal.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c +index ddd06cbd5e..a465862d12 100644 +--- a/gio/gdbusauthmechanismexternal.c ++++ b/gio/gdbusauthmechanismexternal.c +@@ -201,14 +201,24 @@ data_matches_credentials (const gchar *data, + if (credentials == NULL) + goto out; + +- if (data == NULL || data_len == 0) +- goto out; +- + #if defined(G_OS_UNIX) + { + gint64 alleged_uid; + gchar *endp; + ++ /* If we were unable to find out the uid, then nothing ++ * can possibly match it. */ ++ if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1) ++ goto out; ++ ++ /* An empty authorization identity means we want to be ++ * whatever identity the out-of-band credentials say we have ++ * (RFC 4422 appendix A.1). This effectively matches any uid. */ ++ if (data == NULL || data_len == 0) ++ { ++ match = TRUE; ++ goto out; ++ } + /* on UNIX, this is the uid as a string in base 10 */ + alleged_uid = g_ascii_strtoll (data, &endp, 10); + if (*endp == '\0') +-- +GitLab + + +From 3f532af65c98e4ba8426c53f26c9ee15d3692f9c Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Mon, 18 Jul 2022 17:14:44 +0100 +Subject: [PATCH 4/5] gdbusauth: Represent empty data block as DATA\r\n, with + no space + +This is an interoperability fix. The reference implementation of D-Bus +treats "DATA\r\n" as equivalent to "DATA \r\n", but sd-bus does not, +and only accepts the former. + +Signed-off-by: Simon McVittie +--- + gio/gdbusauth.c | 34 ++++++++++++++++++++++++++-------- + 1 file changed, 26 insertions(+), 8 deletions(-) + +diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c +index d2ca41a201..89cbbf67c6 100644 +--- a/gio/gdbusauth.c ++++ b/gio/gdbusauth.c +@@ -807,11 +807,21 @@ _g_dbus_auth_run_client (GDBusAuth *auth, + { + gchar *data; + gsize data_len; +- gchar *encoded_data; ++ + data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len); +- encoded_data = _g_dbus_hexencode (data, data_len); +- s = g_strdup_printf ("DATA %s\r\n", encoded_data); +- g_free (encoded_data); ++ ++ if (data_len == 0) ++ { ++ s = g_strdup ("DATA\r\n"); ++ } ++ else ++ { ++ gchar *encoded_data = _g_dbus_hexencode (data, data_len); ++ ++ s = g_strdup_printf ("DATA %s\r\n", encoded_data); ++ g_free (encoded_data); ++ } ++ + g_free (data); + debug_print ("CLIENT: writing '%s'", s); + if (!g_data_output_stream_put_string (dos, s, cancellable, error)) +@@ -1209,13 +1219,21 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + gsize data_len; + + data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len); ++ + if (data != NULL) + { +- gchar *encoded_data; ++ if (data_len == 0) ++ { ++ s = g_strdup ("DATA\r\n"); ++ } ++ else ++ { ++ gchar *encoded_data = _g_dbus_hexencode (data, data_len); ++ ++ s = g_strdup_printf ("DATA %s\r\n", encoded_data); ++ g_free (encoded_data); ++ } + +- encoded_data = _g_dbus_hexencode (data, data_len); +- s = g_strdup_printf ("DATA %s\r\n", encoded_data); +- g_free (encoded_data); + g_free (data); + + debug_print ("SERVER: writing '%s'", s); +-- +GitLab diff --git a/base/rx/rx-glib2/3126.patch b/base/rx/rx-glib2/3126.patch new file mode 100644 index 0000000..4bd6a17 --- /dev/null +++ b/base/rx/rx-glib2/3126.patch @@ -0,0 +1,3021 @@ +From b4ae1179cbcb91992adc0d5d4fa13341bbcd6c28 Mon Sep 17 00:00:00 2001 +From: William Manley +Date: Tue, 23 Jun 2020 22:59:58 +0100 +Subject: [PATCH 01/19] gvariant-core: Consolidate construction of + `GVariantSerialised` + +So I only need to change it in one place. + +This introduces no functional changes. + +Helps: #2121 +--- + glib/gvariant-core.c | 49 ++++++++++++++++++++++---------------------- + 1 file changed, 25 insertions(+), 24 deletions(-) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index b34ba8d8e..cd6a88768 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -349,6 +349,27 @@ g_variant_ensure_size (GVariant *value) + } + } + ++/* < private > ++ * g_variant_to_serialised: ++ * @value: a #GVariant ++ * ++ * Gets a GVariantSerialised for a GVariant in state STATE_SERIALISED. ++ */ ++inline static GVariantSerialised ++g_variant_to_serialised (GVariant *value) ++{ ++ g_assert (value->state & STATE_SERIALISED); ++ { ++ GVariantSerialised serialised = { ++ value->type_info, ++ (gpointer) value->contents.serialised.data, ++ value->size, ++ value->depth, ++ }; ++ return serialised; ++ } ++} ++ + /* < private > + * g_variant_serialise: + * @value: a #GVariant +@@ -1007,16 +1028,8 @@ g_variant_n_children (GVariant *value) + g_variant_lock (value); + + if (value->state & STATE_SERIALISED) +- { +- GVariantSerialised serialised = { +- value->type_info, +- (gpointer) value->contents.serialised.data, +- value->size, +- value->depth, +- }; +- +- n_children = g_variant_serialised_n_children (serialised); +- } ++ n_children = g_variant_serialised_n_children ( ++ g_variant_to_serialised (value)); + else + n_children = value->contents.tree.n_children; + +@@ -1083,12 +1096,7 @@ g_variant_get_child_value (GVariant *value, + } + + { +- GVariantSerialised serialised = { +- value->type_info, +- (gpointer) value->contents.serialised.data, +- value->size, +- value->depth, +- }; ++ GVariantSerialised serialised = g_variant_to_serialised (value); + GVariantSerialised s_child; + GVariant *child; + +@@ -1201,14 +1209,7 @@ g_variant_is_normal_form (GVariant *value) + + if (value->state & STATE_SERIALISED) + { +- GVariantSerialised serialised = { +- value->type_info, +- (gpointer) value->contents.serialised.data, +- value->size, +- value->depth +- }; +- +- if (g_variant_serialised_is_normal (serialised)) ++ if (g_variant_serialised_is_normal (g_variant_to_serialised (value))) + value->state |= STATE_TRUSTED; + } + else +-- +2.40.0 + +From d51c16a7c8c3a57f1bad4305363ad85914ecab2e Mon Sep 17 00:00:00 2001 +From: William Manley +Date: Thu, 25 Jun 2020 17:08:21 +0100 +Subject: [PATCH 02/19] gvariant-serialiser: Factor out functions for dealing + with framing offsets + +This introduces no functional changes. + +Helps: #2121 +--- + glib/gvariant-serialiser.c | 108 +++++++++++++++++++------------------ + 1 file changed, 57 insertions(+), 51 deletions(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 06f419fe4..ab6a56eb6 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -633,30 +633,62 @@ gvs_calculate_total_size (gsize body_size, + return body_size + 8 * offsets; + } + ++struct Offsets ++{ ++ gsize data_size; ++ ++ guchar *array; ++ gsize length; ++ guint offset_size; ++ ++ gboolean is_normal; ++}; ++ + static gsize +-gvs_variable_sized_array_n_children (GVariantSerialised value) ++gvs_offsets_get_offset_n (struct Offsets *offsets, ++ gsize n) ++{ ++ return gvs_read_unaligned_le ( ++ offsets->array + (offsets->offset_size * n), offsets->offset_size); ++} ++ ++static struct Offsets ++gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value) + { ++ struct Offsets out = { 0, }; + gsize offsets_array_size; +- gsize offset_size; + gsize last_end; + + if (value.size == 0) +- return 0; +- +- offset_size = gvs_get_offset_size (value.size); ++ { ++ out.is_normal = TRUE; ++ return out; ++ } + +- last_end = gvs_read_unaligned_le (value.data + value.size - +- offset_size, offset_size); ++ out.offset_size = gvs_get_offset_size (value.size); ++ last_end = gvs_read_unaligned_le (value.data + value.size - out.offset_size, ++ out.offset_size); + + if (last_end > value.size) +- return 0; ++ return out; /* offsets not normal */ + + offsets_array_size = value.size - last_end; + +- if (offsets_array_size % offset_size) +- return 0; ++ if (offsets_array_size % out.offset_size) ++ return out; /* offsets not normal */ ++ ++ out.data_size = last_end; ++ out.array = value.data + last_end; ++ out.length = offsets_array_size / out.offset_size; ++ out.is_normal = TRUE; + +- return offsets_array_size / offset_size; ++ return out; ++} ++ ++static gsize ++gvs_variable_sized_array_n_children (GVariantSerialised value) ++{ ++ return gvs_variable_sized_array_get_frame_offsets (value).length; + } + + static GVariantSerialised +@@ -664,8 +696,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, + gsize index_) + { + GVariantSerialised child = { 0, }; +- gsize offset_size; +- gsize last_end; ++ ++ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value); ++ + gsize start; + gsize end; + +@@ -673,18 +706,11 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, + g_variant_type_info_ref (child.type_info); + child.depth = value.depth + 1; + +- offset_size = gvs_get_offset_size (value.size); +- +- last_end = gvs_read_unaligned_le (value.data + value.size - +- offset_size, offset_size); +- + if (index_ > 0) + { + guint alignment; + +- start = gvs_read_unaligned_le (value.data + last_end + +- (offset_size * (index_ - 1)), +- offset_size); ++ start = gvs_offsets_get_offset_n (&offsets, index_ - 1); + + g_variant_type_info_query (child.type_info, &alignment, NULL); + start += (-start) & alignment; +@@ -692,11 +718,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, + else + start = 0; + +- end = gvs_read_unaligned_le (value.data + last_end + +- (offset_size * index_), +- offset_size); ++ end = gvs_offsets_get_offset_n (&offsets, index_); + +- if (start < end && end <= value.size && end <= last_end) ++ if (start < end && end <= value.size && end <= offsets.data_size) + { + child.data = value.data + start; + child.size = end - start; +@@ -768,34 +792,16 @@ static gboolean + gvs_variable_sized_array_is_normal (GVariantSerialised value) + { + GVariantSerialised child = { 0, }; +- gsize offsets_array_size; +- guchar *offsets_array; +- guint offset_size; + guint alignment; +- gsize last_end; +- gsize length; + gsize offset; + gsize i; + +- if (value.size == 0) +- return TRUE; +- +- offset_size = gvs_get_offset_size (value.size); +- last_end = gvs_read_unaligned_le (value.data + value.size - +- offset_size, offset_size); ++ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value); + +- if (last_end > value.size) ++ if (!offsets.is_normal) + return FALSE; + +- offsets_array_size = value.size - last_end; +- +- if (offsets_array_size % offset_size) +- return FALSE; +- +- offsets_array = value.data + value.size - offsets_array_size; +- length = offsets_array_size / offset_size; +- +- if (length == 0) ++ if (value.size != 0 && offsets.length == 0) + return FALSE; + + child.type_info = g_variant_type_info_element (value.type_info); +@@ -803,14 +809,14 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + child.depth = value.depth + 1; + offset = 0; + +- for (i = 0; i < length; i++) ++ for (i = 0; i < offsets.length; i++) + { + gsize this_end; + +- this_end = gvs_read_unaligned_le (offsets_array + offset_size * i, +- offset_size); ++ this_end = gvs_read_unaligned_le (offsets.array + offsets.offset_size * i, ++ offsets.offset_size); + +- if (this_end < offset || this_end > last_end) ++ if (this_end < offset || this_end > offsets.data_size) + return FALSE; + + while (offset & alignment) +@@ -832,7 +838,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + offset = this_end; + } + +- g_assert (offset == last_end); ++ g_assert (offset == offsets.data_size); + + return TRUE; + } +-- +2.40.0 + +From fd215233ae37ce20cd84eb128c86b90c4dd610fa Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 25 Oct 2022 18:05:52 +0100 +Subject: [PATCH 03/19] gvariant: Zero-initialise various GVariantSerialised + objects + +The following few commits will add a couple of new fields to +`GVariantSerialised`, and they should be zero-filled by default. + +Try and pre-empt that a bit by zero-filling `GVariantSerialised` by +default in a few places. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/gvariant.c | 2 +- + glib/tests/gvariant.c | 12 ++++++------ + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index e48dec1ad..8ebbaa662 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5944,7 +5944,7 @@ g_variant_byteswap (GVariant *value) + if (alignment) + /* (potentially) contains multi-byte numeric data */ + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + GVariant *trusted; + GBytes *bytes; + +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 35434cae9..ebd617639 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -1438,7 +1438,7 @@ test_maybe (void) + + for (flavour = 0; flavour < 8; flavour += alignment) + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + GVariantSerialised child; + + serialised.type_info = type_info; +@@ -1562,7 +1562,7 @@ test_array (void) + + for (flavour = 0; flavour < 8; flavour += alignment) + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + + serialised.type_info = array_info; + serialised.data = flavoured_malloc (needed_size, flavour); +@@ -1726,7 +1726,7 @@ test_tuple (void) + + for (flavour = 0; flavour < 8; flavour += alignment) + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + + serialised.type_info = type_info; + serialised.data = flavoured_malloc (needed_size, flavour); +@@ -1821,7 +1821,7 @@ test_variant (void) + + for (flavour = 0; flavour < 8; flavour += alignment) + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + GVariantSerialised child; + + serialised.type_info = type_info; +@@ -2268,7 +2268,7 @@ serialise_tree (TreeInstance *tree, + static void + test_byteswap (void) + { +- GVariantSerialised one, two; ++ GVariantSerialised one = { 0, }, two = { 0, }; + TreeInstance *tree; + + tree = tree_instance_new (NULL, 3); +@@ -2342,7 +2342,7 @@ test_serialiser_children (void) + static void + test_fuzz (gdouble *fuzziness) + { +- GVariantSerialised serialised; ++ GVariantSerialised serialised = { 0, }; + TreeInstance *tree; + + /* make an instance */ +-- +2.40.0 + +From 96e27afc7a1da0944d995ff0715679870ce8d503 Mon Sep 17 00:00:00 2001 +From: William Manley +Date: Mon, 29 Jun 2020 16:59:44 +0100 +Subject: [PATCH 04/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?= + =?UTF-8?q?=20elements=20to=20overlap=20with=20each=20other?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If different elements of a variable sized array can overlap with each +other then we can cause a `GVariant` to normalise to a much larger type. + +This commit changes the behaviour of `GVariant` with non-normal form data. If +an invalid frame offset is found all subsequent elements are given their +default value. + +When retrieving an element at index `n` we scan the frame offsets up to index +`n` and if they are not in order we return an element with the default value +for that type. This guarantees that elements don't overlap with each +other. We remember the offset we've scanned up to so we don't need to +repeat this work on subsequent accesses. We skip these checks for trusted +data. + +Unfortunately this makes random access of untrusted data O(n) — at least +on first access. It doesn't affect the algorithmic complexity of accessing +elements in order, such as when using the `GVariantIter` interface. Also: +the cost of validation will be amortised as the `GVariant` instance is +continued to be used. + +I've implemented this with 4 different functions, 1 for each element size, +rather than looping calling `gvs_read_unaligned_le` in the hope that the +compiler will find it easy to optimise and should produce fairly tight +code. + +Fixes: #2121 +--- + glib/gvariant-core.c | 35 ++++++++++++++++ + glib/gvariant-serialiser.c | 86 ++++++++++++++++++++++++++++++++++++-- + glib/gvariant-serialiser.h | 9 ++++ + glib/tests/gvariant.c | 45 ++++++++++++++++++++ + 4 files changed, 172 insertions(+), 3 deletions(-) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index cd6a88768..4bd7d0537 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -65,6 +65,7 @@ struct _GVariant + { + GBytes *bytes; + gconstpointer data; ++ gsize ordered_offsets_up_to; + } serialised; + + struct +@@ -162,6 +163,24 @@ struct _GVariant + * if .data pointed to the appropriate number of nul + * bytes. + * ++ * .ordered_offsets_up_to: If ordered_offsets_up_to == n this means that all ++ * the frame offsets up to and including the frame ++ * offset determining the end of element n are in ++ * order. This guarantees that the bytes of element ++ * n don't overlap with any previous element. ++ * ++ * For trusted data this is set to G_MAXSIZE and we ++ * don't check that the frame offsets are in order. ++ * ++ * Note: This doesn't imply the offsets are good in ++ * any way apart from their ordering. In particular ++ * offsets may be out of bounds for this value or ++ * may imply that the data overlaps the frame ++ * offsets themselves. ++ * ++ * This field is only relevant for arrays of non ++ * fixed width types. ++ * + * .tree: Only valid when the instance is in tree form. + * + * Note that accesses from other threads could result in +@@ -365,6 +384,7 @@ g_variant_to_serialised (GVariant *value) + (gpointer) value->contents.serialised.data, + value->size, + value->depth, ++ value->contents.serialised.ordered_offsets_up_to, + }; + return serialised; + } +@@ -396,6 +416,7 @@ g_variant_serialise (GVariant *value, + serialised.size = value->size; + serialised.data = data; + serialised.depth = value->depth; ++ serialised.ordered_offsets_up_to = 0; + + children = (gpointer *) value->contents.tree.children; + n_children = value->contents.tree.n_children; +@@ -439,6 +460,15 @@ g_variant_fill_gvs (GVariantSerialised *serialised, + g_assert (serialised->size == value->size); + serialised->depth = value->depth; + ++ if (value->state & STATE_SERIALISED) ++ { ++ serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to; ++ } ++ else ++ { ++ serialised->ordered_offsets_up_to = 0; ++ } ++ + if (serialised->data) + /* g_variant_store() is a public API, so it + * it will reacquire the lock if it needs to. +@@ -481,6 +511,7 @@ g_variant_ensure_serialised (GVariant *value) + bytes = g_bytes_new_take (data, value->size); + value->contents.serialised.data = g_bytes_get_data (bytes, NULL); + value->contents.serialised.bytes = bytes; ++ value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE; + value->state |= STATE_SERIALISED; + } + } +@@ -561,6 +592,7 @@ g_variant_new_from_bytes (const GVariantType *type, + serialised.type_info = value->type_info; + serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size); + serialised.depth = 0; ++ serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0; + + if (!g_variant_serialised_check (serialised)) + { +@@ -611,6 +643,8 @@ g_variant_new_from_bytes (const GVariantType *type, + value->contents.serialised.data = g_bytes_get_data (bytes, &value->size); + } + ++ value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0; ++ + g_clear_pointer (&owned_bytes, g_bytes_unref); + + return value; +@@ -1130,6 +1164,7 @@ g_variant_get_child_value (GVariant *value, + child->contents.serialised.bytes = + g_bytes_ref (value->contents.serialised.bytes); + child->contents.serialised.data = s_child.data; ++ child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to; + + return child; + } +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index ab6a56eb6..8e9364e85 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -1,6 +1,7 @@ + /* + * Copyright © 2007, 2008 Ryan Lortie + * Copyright © 2010 Codethink Limited ++ * Copyright © 2020 William Manley + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -264,6 +265,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value, + value.type_info = g_variant_type_info_element (value.type_info); + g_variant_type_info_ref (value.type_info); + value.depth++; ++ value.ordered_offsets_up_to = 0; + + return value; + } +@@ -295,7 +297,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value, + { + if (n_children) + { +- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 }; ++ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 }; + + gvs_filler (&child, children[0]); + } +@@ -317,6 +319,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value) + /* proper element size: "Just". recurse to the child. */ + value.type_info = g_variant_type_info_element (value.type_info); + value.depth++; ++ value.ordered_offsets_up_to = 0; + + return g_variant_serialised_is_normal (value); + } +@@ -358,6 +361,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value, + value.data = NULL; + + value.depth++; ++ value.ordered_offsets_up_to = 0; + + return value; + } +@@ -388,7 +392,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value, + { + if (n_children) + { +- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 }; ++ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 }; + + /* write the data for the child. */ + gvs_filler (&child, children[0]); +@@ -408,6 +412,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value) + value.type_info = g_variant_type_info_element (value.type_info); + value.size--; + value.depth++; ++ value.ordered_offsets_up_to = 0; + + return g_variant_serialised_is_normal (value); + } +@@ -691,6 +696,32 @@ gvs_variable_sized_array_n_children (GVariantSerialised value) + return gvs_variable_sized_array_get_frame_offsets (value).length; + } + ++/* Find the index of the first out-of-order element in @data, assuming that ++ * @data is an array of elements of given @type, starting at index @start and ++ * containing a further @len-@start elements. */ ++#define DEFINE_FIND_UNORDERED(type) \ ++ static gsize \ ++ find_unordered_##type (const guint8 *data, gsize start, gsize len) \ ++ { \ ++ gsize off; \ ++ type current, previous; \ ++ \ ++ memcpy (&previous, data + start * sizeof (current), sizeof (current)); \ ++ for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \ ++ { \ ++ memcpy (¤t, data + off, sizeof (current)); \ ++ if (current < previous) \ ++ break; \ ++ previous = current; \ ++ } \ ++ return off / sizeof (current) - 1; \ ++ } ++ ++DEFINE_FIND_UNORDERED (guint8); ++DEFINE_FIND_UNORDERED (guint16); ++DEFINE_FIND_UNORDERED (guint32); ++DEFINE_FIND_UNORDERED (guint64); ++ + static GVariantSerialised + gvs_variable_sized_array_get_child (GVariantSerialised value, + gsize index_) +@@ -706,6 +737,49 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, + g_variant_type_info_ref (child.type_info); + child.depth = value.depth + 1; + ++ /* If the requested @index_ is beyond the set of indices whose framing offsets ++ * have been checked, check the remaining offsets to see whether they’re ++ * normal (in order, no overlapping array elements). */ ++ if (index_ > value.ordered_offsets_up_to) ++ { ++ switch (offsets.offset_size) ++ { ++ case 1: ++ { ++ value.ordered_offsets_up_to = find_unordered_guint8 ( ++ offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ break; ++ } ++ case 2: ++ { ++ value.ordered_offsets_up_to = find_unordered_guint16 ( ++ offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ break; ++ } ++ case 4: ++ { ++ value.ordered_offsets_up_to = find_unordered_guint32 ( ++ offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ break; ++ } ++ case 8: ++ { ++ value.ordered_offsets_up_to = find_unordered_guint64 ( ++ offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ break; ++ } ++ default: ++ /* gvs_get_offset_size() only returns maximum 8 */ ++ g_assert_not_reached (); ++ } ++ } ++ ++ if (index_ > value.ordered_offsets_up_to) ++ { ++ /* Offsets are invalid somewhere, so return an empty child. */ ++ return child; ++ } ++ + if (index_ > 0) + { + guint alignment; +@@ -840,6 +914,9 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + + g_assert (offset == offsets.data_size); + ++ /* All offsets have now been checked. */ ++ value.ordered_offsets_up_to = G_MAXSIZE; ++ + return TRUE; + } + +@@ -1072,7 +1149,7 @@ gvs_tuple_is_normal (GVariantSerialised value) + for (i = 0; i < length; i++) + { + const GVariantMemberInfo *member_info; +- GVariantSerialised child; ++ GVariantSerialised child = { 0, }; + gsize fixed_size; + guint alignment; + gsize end; +@@ -1132,6 +1209,9 @@ gvs_tuple_is_normal (GVariantSerialised value) + offset = end; + } + ++ /* All element bounds have been checked above. */ ++ value.ordered_offsets_up_to = G_MAXSIZE; ++ + { + gsize fixed_size; + guint alignment; +diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h +index 81343e9ca..bc636000c 100644 +--- a/glib/gvariant-serialiser.h ++++ b/glib/gvariant-serialiser.h +@@ -29,6 +29,15 @@ typedef struct + guchar *data; + gsize size; + gsize depth; /* same semantics as GVariant.depth */ ++ ++ /* If ordered_offsets_up_to == n this means that all the frame offsets up to and ++ * including the frame offset determining the end of element n are in order. ++ * This guarantees that the bytes of element n don't overlap with any previous ++ * element. ++ * ++ * This is both read and set by g_variant_serialised_get_child for arrays of ++ * non-fixed-width types */ ++ gsize ordered_offsets_up_to; + } GVariantSerialised; + + /* deserialisation */ +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index ebd617639..7e5664e65 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -1,5 +1,6 @@ + /* + * Copyright © 2010 Codethink Limited ++ * Copyright © 2020 William Manley + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -1279,6 +1280,7 @@ random_instance_filler (GVariantSerialised *serialised, + serialised->size = instance->size; + + serialised->depth = 0; ++ serialised->ordered_offsets_up_to = 0; + + g_assert_true (serialised->type_info == instance->type_info); + g_assert_cmpuint (serialised->size, ==, instance->size); +@@ -5023,6 +5025,47 @@ test_normal_checking_array_offsets (void) + g_variant_unref (variant); + } + ++/* This is a regression test that we can't have non-normal values that take up ++ * significantly more space than the normal equivalent, by specifying the ++ * offset table entries so that array elements overlap. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_832242 */ ++static void ++test_normal_checking_array_offsets2 (void) ++{ ++ const guint8 data[] = { ++ 'h', 'i', '\0', ++ 0x03, 0x00, 0x03, ++ 0x06, 0x00, 0x06, ++ 0x09, 0x00, 0x09, ++ 0x0c, 0x00, 0x0c, ++ 0x0f, 0x00, 0x0f, ++ 0x12, 0x00, 0x12, ++ 0x15, 0x00, 0x15, ++ }; ++ gsize size = sizeof (data); ++ const GVariantType *aaaaaaas = G_VARIANT_TYPE ("aaaaaaas"); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ variant = g_variant_new_from_data (aaaaaaas, data, size, FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 2); ++ ++ expected = g_variant_new_parsed ( ++ "[[[[[[['hi', '', ''], [], []], [], []], [], []], [], []], [], []], [], []]"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ + /* Test that a tuple with invalidly large values in its offset table is + * normalised successfully without looping infinitely. */ + static void +@@ -5190,6 +5233,8 @@ main (int argc, char **argv) + test_normal_checking_tuples); + g_test_add_func ("/gvariant/normal-checking/array-offsets", + test_normal_checking_array_offsets); ++ g_test_add_func ("/gvariant/normal-checking/array-offsets2", ++ test_normal_checking_array_offsets2); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets", + test_normal_checking_tuple_offsets); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", +-- +2.40.0 + +From 21fba6a534b35cc4fc245c6a8822b53c84b7fb48 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 7 Jan 2022 15:03:52 +0000 +Subject: [PATCH 05/19] gvariant-serialiser: Factor out code to get bounds of a + tuple member + +This introduces no functional changes. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/gvariant-serialiser.c | 73 ++++++++++++++++++++++++-------------- + 1 file changed, 46 insertions(+), 27 deletions(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 8e9364e85..cf4d2a2a5 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -942,6 +942,51 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + * for the tuple. See the notes in gvarianttypeinfo.h. + */ + ++static void ++gvs_tuple_get_member_bounds (GVariantSerialised value, ++ gsize index_, ++ gsize offset_size, ++ gsize *out_member_start, ++ gsize *out_member_end) ++{ ++ const GVariantMemberInfo *member_info; ++ gsize member_start, member_end; ++ ++ member_info = g_variant_type_info_member_info (value.type_info, index_); ++ ++ if (member_info->i + 1) ++ member_start = gvs_read_unaligned_le (value.data + value.size - ++ offset_size * (member_info->i + 1), ++ offset_size); ++ else ++ member_start = 0; ++ ++ member_start += member_info->a; ++ member_start &= member_info->b; ++ member_start |= member_info->c; ++ ++ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST) ++ member_end = value.size - offset_size * (member_info->i + 1); ++ ++ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) ++ { ++ gsize fixed_size; ++ ++ g_variant_type_info_query (member_info->type_info, NULL, &fixed_size); ++ member_end = member_start + fixed_size; ++ } ++ ++ else /* G_VARIANT_MEMBER_ENDING_OFFSET */ ++ member_end = gvs_read_unaligned_le (value.data + value.size - ++ offset_size * (member_info->i + 2), ++ offset_size); ++ ++ if (out_member_start != NULL) ++ *out_member_start = member_start; ++ if (out_member_end != NULL) ++ *out_member_end = member_end; ++} ++ + static gsize + gvs_tuple_n_children (GVariantSerialised value) + { +@@ -997,33 +1042,7 @@ gvs_tuple_get_child (GVariantSerialised value, + } + } + +- if (member_info->i + 1) +- start = gvs_read_unaligned_le (value.data + value.size - +- offset_size * (member_info->i + 1), +- offset_size); +- else +- start = 0; +- +- start += member_info->a; +- start &= member_info->b; +- start |= member_info->c; +- +- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST) +- end = value.size - offset_size * (member_info->i + 1); +- +- else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) +- { +- gsize fixed_size; +- +- g_variant_type_info_query (child.type_info, NULL, &fixed_size); +- end = start + fixed_size; +- child.size = fixed_size; +- } +- +- else /* G_VARIANT_MEMBER_ENDING_OFFSET */ +- end = gvs_read_unaligned_le (value.data + value.size - +- offset_size * (member_info->i + 2), +- offset_size); ++ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end); + + /* The child should not extend into the offset table. */ + if (index_ != g_variant_type_info_n_members (value.type_info) - 1) +-- +2.40.0 + +From 44ae51d04615867fe3bcb2842b93c059e764f8f7 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 7 Jan 2022 16:37:29 +0000 +Subject: [PATCH 06/19] gvariant-serialiser: Rework child size calculation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reduces a few duplicate calls to `g_variant_type_info_query()` and +explains why they’re needed. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/gvariant-serialiser.c | 31 +++++++++---------------------- + 1 file changed, 9 insertions(+), 22 deletions(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index cf4d2a2a5..7ba7aed1a 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -1007,14 +1007,18 @@ gvs_tuple_get_child (GVariantSerialised value, + child.depth = value.depth + 1; + offset_size = gvs_get_offset_size (value.size); + ++ /* Ensure the size is set for fixed-sized children, or ++ * g_variant_serialised_check() will fail, even if we return ++ * (child.data == NULL) to indicate an error. */ ++ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) ++ g_variant_type_info_query (child.type_info, NULL, &child.size); ++ + /* tuples are the only (potentially) fixed-sized containers, so the + * only ones that have to deal with the possibility of having %NULL + * data with a non-zero %size if errors occurred elsewhere. + */ + if G_UNLIKELY (value.data == NULL && value.size != 0) + { +- g_variant_type_info_query (child.type_info, NULL, &child.size); +- + /* this can only happen in fixed-sized tuples, + * so the child must also be fixed sized. + */ +@@ -1032,29 +1036,12 @@ gvs_tuple_get_child (GVariantSerialised value, + else + { + if (offset_size * (member_info->i + 1) > value.size) +- { +- /* if the child is fixed size, return its size. +- * if child is not fixed-sized, return size = 0. +- */ +- g_variant_type_info_query (child.type_info, NULL, &child.size); +- +- return child; +- } ++ return child; + } + +- gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end); +- + /* The child should not extend into the offset table. */ +- if (index_ != g_variant_type_info_n_members (value.type_info) - 1) +- { +- GVariantSerialised last_child; +- last_child = gvs_tuple_get_child (value, +- g_variant_type_info_n_members (value.type_info) - 1); +- last_end = last_child.data + last_child.size - value.data; +- g_variant_type_info_unref (last_child.type_info); +- } +- else +- last_end = end; ++ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end); ++ gvs_tuple_get_member_bounds (value, g_variant_type_info_n_members (value.type_info) - 1, offset_size, NULL, &last_end); + + if (start < end && end <= value.size && end <= last_end) + { +-- +2.40.0 + +From a4ddf8ea87a43c2ceffb5d806061d9c6297b9096 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 7 Jan 2022 16:42:14 +0000 +Subject: [PATCH 07/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?= + =?UTF-8?q?=20elements=20of=20a=20tuple=20to=20overlap=20each=20other?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is similar to the earlier commit which prevents child elements of a +variable-sized array from overlapping each other, but this time for +tuples. It is based heavily on ideas by William Manley. + +Tuples are slightly different from variable-sized arrays in that they +contain a mixture of fixed and variable sized elements. All but one of +the variable sized elements have an entry in the frame offsets table. +This means that if we were to just check the ordering of the frame +offsets table, the variable sized elements could still overlap +interleaving fixed sized elements, which would be bad. + +Therefore we have to check the elements rather than the frame offsets. + +The logic of checking the elements up to the index currently being +requested, and caching the result in `ordered_offsets_up_to`, means that +the algorithmic cost implications are the same for this commit as for +variable-sized arrays: an O(N) cost for these checks is amortised out +over N accesses to O(1) per access. + +Signed-off-by: Philip Withnall + +Fixes: #2121 +--- + glib/gvariant-core.c | 6 +- + glib/gvariant-serialiser.c | 40 ++++++++ + glib/gvariant-serialiser.h | 7 +- + glib/gvariant.c | 1 + + glib/tests/gvariant.c | 181 +++++++++++++++++++++++++++++++++++++ + 5 files changed, 232 insertions(+), 3 deletions(-) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index 4bd7d0537..9b268d977 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -1,6 +1,7 @@ + /* + * Copyright © 2007, 2008 Ryan Lortie + * Copyright © 2010 Codethink Limited ++ * Copyright © 2022 Endless OS Foundation, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -179,7 +180,7 @@ struct _GVariant + * offsets themselves. + * + * This field is only relevant for arrays of non +- * fixed width types. ++ * fixed width types and for tuples. + * + * .tree: Only valid when the instance is in tree form. + * +@@ -1139,6 +1140,9 @@ g_variant_get_child_value (GVariant *value, + */ + s_child = g_variant_serialised_get_child (serialised, index_); + ++ /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */ ++ value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to); ++ + /* Check whether this would cause nesting too deep. If so, return a fake + * child. The only situation we expect this to happen in is with a variant, + * as all other deeply-nested types have a static type, and hence should +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 7ba7aed1a..470e21919 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -942,6 +942,10 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + * for the tuple. See the notes in gvarianttypeinfo.h. + */ + ++/* Note: This doesn’t guarantee that @out_member_end >= @out_member_start; that ++ * condition may not hold true for invalid serialised variants. The caller is ++ * responsible for checking the returned values and handling invalid ones ++ * appropriately. */ + static void + gvs_tuple_get_member_bounds (GVariantSerialised value, + gsize index_, +@@ -1028,6 +1032,42 @@ gvs_tuple_get_child (GVariantSerialised value, + return child; + } + ++ /* If the requested @index_ is beyond the set of indices whose framing offsets ++ * have been checked, check the remaining offsets to see whether they’re ++ * normal (in order, no overlapping tuple elements). ++ * ++ * Unlike the checks in gvs_variable_sized_array_get_child(), we have to check ++ * all the tuple *elements* here, not just all the framing offsets, since ++ * tuples contain a mix of elements which use framing offsets and ones which ++ * don’t. None of them are allowed to overlap. */ ++ if (index_ > value.ordered_offsets_up_to) ++ { ++ gsize i, prev_i_end = 0; ++ ++ if (value.ordered_offsets_up_to > 0) ++ gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end); ++ ++ for (i = value.ordered_offsets_up_to; i <= index_; i++) ++ { ++ gsize i_start, i_end; ++ ++ gvs_tuple_get_member_bounds (value, i, offset_size, &i_start, &i_end); ++ ++ if (i_start > i_end || i_start < prev_i_end || i_end > value.size) ++ break; ++ ++ prev_i_end = i_end; ++ } ++ ++ value.ordered_offsets_up_to = i - 1; ++ } ++ ++ if (index_ > value.ordered_offsets_up_to) ++ { ++ /* Offsets are invalid somewhere, so return an empty child. */ ++ return child; ++ } ++ + if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET) + { + if (offset_size * (member_info->i + 2) > value.size) +diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h +index bc636000c..765bd43b9 100644 +--- a/glib/gvariant-serialiser.h ++++ b/glib/gvariant-serialiser.h +@@ -35,8 +35,11 @@ typedef struct + * This guarantees that the bytes of element n don't overlap with any previous + * element. + * +- * This is both read and set by g_variant_serialised_get_child for arrays of +- * non-fixed-width types */ ++ * This is both read and set by g_variant_serialised_get_child() for arrays of ++ * non-fixed-width types, and for tuples. ++ * ++ * Even when dealing with tuples, @ordered_offsets_up_to is an element index, ++ * rather than an index into the frame offsets. */ + gsize ordered_offsets_up_to; + } GVariantSerialised; + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 8ebbaa662..5f6eb63ee 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5953,6 +5953,7 @@ g_variant_byteswap (GVariant *value) + serialised.size = g_variant_get_size (trusted); + serialised.data = g_malloc (serialised.size); + serialised.depth = g_variant_get_depth (trusted); ++ serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */ + g_variant_store (trusted, serialised.data); + g_variant_unref (trusted); + +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 7e5664e65..9ed56d66d 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -1,6 +1,7 @@ + /* + * Copyright © 2010 Codethink Limited + * Copyright © 2020 William Manley ++ * Copyright © 2022 Endless OS Foundation, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -1447,6 +1448,7 @@ test_maybe (void) + serialised.data = flavoured_malloc (needed_size, flavour); + serialised.size = needed_size; + serialised.depth = 0; ++ serialised.ordered_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, + random_instance_filler, +@@ -1570,6 +1572,7 @@ test_array (void) + serialised.data = flavoured_malloc (needed_size, flavour); + serialised.size = needed_size; + serialised.depth = 0; ++ serialised.ordered_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) instances, n_children); +@@ -1734,6 +1737,7 @@ test_tuple (void) + serialised.data = flavoured_malloc (needed_size, flavour); + serialised.size = needed_size; + serialised.depth = 0; ++ serialised.ordered_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) instances, n_children); +@@ -1830,6 +1834,7 @@ test_variant (void) + serialised.data = flavoured_malloc (needed_size, flavour); + serialised.size = needed_size; + serialised.depth = 0; ++ serialised.ordered_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) &instance, 1); +@@ -5090,6 +5095,176 @@ test_normal_checking_tuple_offsets (void) + g_variant_unref (variant); + } + ++/* This is a regression test that we can't have non-normal values that take up ++ * significantly more space than the normal equivalent, by specifying the ++ * offset table entries so that tuple elements overlap. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838503 and ++ * https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838513 */ ++static void ++test_normal_checking_tuple_offsets2 (void) ++{ ++ const GVariantType *data_type = G_VARIANT_TYPE ("(yyaiyyaiyy)"); ++ const guint8 data[] = { ++ 0x12, 0x34, 0x56, 0x78, 0x01, ++ /* ++ ^───────────────────┘ ++ ++ ^^^^^^^^^^ 1st yy ++ ^^^^^^^^^^ 2nd yy ++ ^^^^^^^^^^ 3rd yy ++ ^^^^ Framing offsets ++ */ ++ ++ /* If this variant was encoded normally, it would be something like this: ++ * 0x12, 0x34, pad, pad, [array bytes], 0x56, 0x78, pad, pad, [array bytes], 0x9A, 0xBC, 0xXX ++ * ^─────────────────────────────────────────────────────┘ ++ * ++ * ^^^^^^^^^^ 1st yy ++ * ^^^^^^^^^^ 2nd yy ++ * ^^^^^^^^^^ 3rd yy ++ * ^^^^ Framing offsets ++ */ ++ }; ++ gsize size = sizeof (data); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3); ++ ++ expected = g_variant_new_parsed ( ++ "@(yyaiyyaiyy) (0x12, 0x34, [], 0x00, 0x00, [], 0x00, 0x00)"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ ++/* This is a regression test that overlapping entries in the offset table are ++ * decoded consistently, even though they’re non-normal. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */ ++static void ++test_normal_checking_tuple_offsets3 (void) ++{ ++ /* The expected decoding of this non-normal byte stream is complex. See ++ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant ++ * specification. ++ * ++ * The rule “Child Values Overlapping Framing Offsets” from the specification ++ * says that the first `ay` must be decoded as `[0x01]` even though it ++ * overlaps the first byte of the offset table. However, since commit ++ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow ++ * this as it’s exploitable. So the first `ay` must be given a default value. ++ * ++ * The second and third `ay`s must be given default values because of rule ++ * “End Boundary Precedes Start Boundary”. ++ * ++ * The `i` must be given a default value because of rule “Start or End ++ * Boundary of a Child Falls Outside the Container”. ++ */ ++ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayiay)"); ++ const guint8 data[] = { ++ 0x01, 0x00, 0x02, ++ /* ++ ^──┘ ++ ++ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above) ++ 2nd ay, bytes 2-0 ++ i, bytes 0-4 ++ 3rd ay, bytes 4-1 ++ ^^^^^^^^^^ Framing offsets ++ */ ++ }; ++ gsize size = sizeof (data); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ g_assert_false (g_variant_is_normal_form (variant)); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3); ++ ++ expected = g_variant_new_parsed ("@(ayayiay) ([], [], 0, [])"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ ++/* This is a regression test that overlapping entries in the offset table are ++ * decoded consistently, even though they’re non-normal. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */ ++static void ++test_normal_checking_tuple_offsets4 (void) ++{ ++ /* The expected decoding of this non-normal byte stream is complex. See ++ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant ++ * specification. ++ * ++ * The rule “Child Values Overlapping Framing Offsets” from the specification ++ * says that the first `ay` must be decoded as `[0x01]` even though it ++ * overlaps the first byte of the offset table. However, since commit ++ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow ++ * this as it’s exploitable. So the first `ay` must be given a default value. ++ * ++ * The second `ay` must be given a default value because of rule “End Boundary ++ * Precedes Start Boundary”. ++ * ++ * The third `ay` must be given a default value because its framing offsets ++ * overlap that of the first `ay`. ++ */ ++ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayay)"); ++ const guint8 data[] = { ++ 0x01, 0x00, 0x02, ++ /* ++ ^──┘ ++ ++ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above) ++ 2nd ay, bytes 2-0 ++ 3rd ay, bytes 0-1 ++ ^^^^^^^^^^ Framing offsets ++ */ ++ }; ++ gsize size = sizeof (data); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ g_assert_false (g_variant_is_normal_form (variant)); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3); ++ ++ expected = g_variant_new_parsed ("@(ayayay) ([], [], [])"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ + /* Test that an empty object path is normalised successfully to the base object + * path, ‘/’. */ + static void +@@ -5237,6 +5412,12 @@ main (int argc, char **argv) + test_normal_checking_array_offsets2); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets", + test_normal_checking_tuple_offsets); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets2", ++ test_normal_checking_tuple_offsets2); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets3", ++ test_normal_checking_tuple_offsets3); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", ++ test_normal_checking_tuple_offsets4); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", + test_normal_checking_empty_object_path); + +-- +2.40.0 + +From 98d5b84c2cdff1431084909b47d1a7f6d0dd60b5 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 7 Jan 2022 16:42:14 +0000 +Subject: [PATCH 08/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?= + =?UTF-8?q?=20elements=20of=20a=20tuple=20to=20overlap=20each=20other?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is similar to the earlier commit which prevents child elements of a +variable-sized array from overlapping each other, but this time for +tuples. It is based heavily on ideas by William Manley. + +Tuples are slightly different from variable-sized arrays in that they +contain a mixture of fixed and variable sized elements. All but one of +the variable sized elements have an entry in the frame offsets table. +This means that if we were to just check the ordering of the frame +offsets table, the variable sized elements could still overlap +interleaving fixed sized elements, which would be bad. + +Therefore we have to check the elements rather than the frame offsets. + +The logic of checking the elements up to the index currently being +requested, and caching the result in `ordered_offsets_up_to`, means that +the algorithmic cost implications are the same for this commit as for +variable-sized arrays: an O(N) cost for these checks is amortised out +over N accesses to O(1) per access. + +Signed-off-by: Philip Withnall + +Fixes: #2121 +--- + glib/gvariant-core.c | 2 ++ + glib/tests/gvariant.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index 9b268d977..c6c50a6ff 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -3,6 +3,8 @@ + * Copyright © 2010 Codethink Limited + * Copyright © 2022 Endless OS Foundation, LLC + * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * + * 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 +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 9ed56d66d..a565ea69a 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -3,6 +3,8 @@ + * Copyright © 2020 William Manley + * Copyright © 2022 Endless OS Foundation, LLC + * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * + * 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 +-- +2.40.0 + +From b53b26c5596a57d06bcc3947b9c8316526d9e70b Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 25 Oct 2022 15:14:14 +0100 +Subject: [PATCH 09/19] gvariant: Track checked and ordered offsets + independently + +The past few commits introduced the concept of known-good offsets in the +offset table (which is used for variable-width arrays and tuples). +Good offsets are ones which are non-overlapping with all the previous +offsets in the table. + +If a bad offset is encountered when indexing into the array or tuple, +the cached known-good offset index will not be increased. In this way, +all child variants at and beyond the first bad offset can be returned as +default values rather than dereferencing potentially invalid data. + +In this case, there was no information about the fact that the indexes +between the highest known-good index and the requested one had been +checked already. That could lead to a pathological case where an offset +table with an invalid first offset is repeatedly checked in full when +trying to access higher-indexed children. + +Avoid that by storing the index of the highest checked offset in the +table, as well as the index of the highest good/ordered offset. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/gvariant-core.c | 28 ++++++++++++++++++++++++ + glib/gvariant-serialiser.c | 44 +++++++++++++++++++++++++++----------- + glib/gvariant-serialiser.h | 9 ++++++++ + glib/gvariant.c | 1 + + glib/tests/gvariant.c | 5 +++++ + 5 files changed, 75 insertions(+), 12 deletions(-) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index c6c50a6ff..1efcf200f 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -69,6 +69,7 @@ struct _GVariant + GBytes *bytes; + gconstpointer data; + gsize ordered_offsets_up_to; ++ gsize checked_offsets_up_to; + } serialised; + + struct +@@ -184,6 +185,24 @@ struct _GVariant + * This field is only relevant for arrays of non + * fixed width types and for tuples. + * ++ * .checked_offsets_up_to: Similarly to .ordered_offsets_up_to, this stores ++ * the index of the highest element, n, whose frame ++ * offsets (and all the preceding frame offsets) ++ * have been checked for validity. ++ * ++ * It is always the case that ++ * .checked_offsets_up_to ≥ .ordered_offsets_up_to. ++ * ++ * If .checked_offsets_up_to == .ordered_offsets_up_to, ++ * then a bad offset has not been found so far. ++ * ++ * If .checked_offsets_up_to > .ordered_offsets_up_to, ++ * then a bad offset has been found at ++ * (.ordered_offsets_up_to + 1). ++ * ++ * This field is only relevant for arrays of non ++ * fixed width types and for tuples. ++ * + * .tree: Only valid when the instance is in tree form. + * + * Note that accesses from other threads could result in +@@ -388,6 +407,7 @@ g_variant_to_serialised (GVariant *value) + value->size, + value->depth, + value->contents.serialised.ordered_offsets_up_to, ++ value->contents.serialised.checked_offsets_up_to, + }; + return serialised; + } +@@ -420,6 +440,7 @@ g_variant_serialise (GVariant *value, + serialised.data = data; + serialised.depth = value->depth; + serialised.ordered_offsets_up_to = 0; ++ serialised.checked_offsets_up_to = 0; + + children = (gpointer *) value->contents.tree.children; + n_children = value->contents.tree.n_children; +@@ -466,10 +487,12 @@ g_variant_fill_gvs (GVariantSerialised *serialised, + if (value->state & STATE_SERIALISED) + { + serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to; ++ serialised->checked_offsets_up_to = value->contents.serialised.checked_offsets_up_to; + } + else + { + serialised->ordered_offsets_up_to = 0; ++ serialised->checked_offsets_up_to = 0; + } + + if (serialised->data) +@@ -515,6 +538,7 @@ g_variant_ensure_serialised (GVariant *value) + value->contents.serialised.data = g_bytes_get_data (bytes, NULL); + value->contents.serialised.bytes = bytes; + value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE; ++ value->contents.serialised.checked_offsets_up_to = G_MAXSIZE; + value->state |= STATE_SERIALISED; + } + } +@@ -596,6 +620,7 @@ g_variant_new_from_bytes (const GVariantType *type, + serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size); + serialised.depth = 0; + serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0; ++ serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0; + + if (!g_variant_serialised_check (serialised)) + { +@@ -647,6 +672,7 @@ g_variant_new_from_bytes (const GVariantType *type, + } + + value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0; ++ value->contents.serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0; + + g_clear_pointer (&owned_bytes, g_bytes_unref); + +@@ -1144,6 +1170,7 @@ g_variant_get_child_value (GVariant *value, + + /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */ + value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to); ++ value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to); + + /* Check whether this would cause nesting too deep. If so, return a fake + * child. The only situation we expect this to happen in is with a variant, +@@ -1171,6 +1198,7 @@ g_variant_get_child_value (GVariant *value, + g_bytes_ref (value->contents.serialised.bytes); + child->contents.serialised.data = s_child.data; + child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to; ++ child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to; + + return child; + } +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 470e21919..8e01ab53e 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -120,6 +120,8 @@ + * + * @depth has no restrictions; the depth of a top-level serialised #GVariant is + * zero, and it increases for each level of nested child. ++ * ++ * @checked_offsets_up_to is always ≥ @ordered_offsets_up_to + */ + + /* < private > +@@ -147,6 +149,9 @@ g_variant_serialised_check (GVariantSerialised serialised) + !(serialised.size == 0 || serialised.data != NULL)) + return FALSE; + ++ if (serialised.ordered_offsets_up_to > serialised.checked_offsets_up_to) ++ return FALSE; ++ + /* Depending on the native alignment requirements of the machine, the + * compiler will insert either 3 or 7 padding bytes after the char. + * This will result in the sizeof() the struct being 12 or 16. +@@ -266,6 +271,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value, + g_variant_type_info_ref (value.type_info); + value.depth++; + value.ordered_offsets_up_to = 0; ++ value.checked_offsets_up_to = 0; + + return value; + } +@@ -297,7 +303,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value, + { + if (n_children) + { +- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 }; ++ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0, 0 }; + + gvs_filler (&child, children[0]); + } +@@ -320,6 +326,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value) + value.type_info = g_variant_type_info_element (value.type_info); + value.depth++; + value.ordered_offsets_up_to = 0; ++ value.checked_offsets_up_to = 0; + + return g_variant_serialised_is_normal (value); + } +@@ -362,6 +369,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value, + + value.depth++; + value.ordered_offsets_up_to = 0; ++ value.checked_offsets_up_to = 0; + + return value; + } +@@ -392,7 +400,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value, + { + if (n_children) + { +- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 }; ++ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0, 0 }; + + /* write the data for the child. */ + gvs_filler (&child, children[0]); +@@ -413,6 +421,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value) + value.size--; + value.depth++; + value.ordered_offsets_up_to = 0; ++ value.checked_offsets_up_to = 0; + + return g_variant_serialised_is_normal (value); + } +@@ -739,39 +748,46 @@ gvs_variable_sized_array_get_child (GVariantSerialised value, + + /* If the requested @index_ is beyond the set of indices whose framing offsets + * have been checked, check the remaining offsets to see whether they’re +- * normal (in order, no overlapping array elements). */ +- if (index_ > value.ordered_offsets_up_to) ++ * normal (in order, no overlapping array elements). ++ * ++ * Don’t bother checking if the highest known-good offset is lower than the ++ * highest checked offset, as that means there’s an invalid element at that ++ * index, so there’s no need to check further. */ ++ if (index_ > value.checked_offsets_up_to && ++ value.ordered_offsets_up_to == value.checked_offsets_up_to) + { + switch (offsets.offset_size) + { + case 1: + { + value.ordered_offsets_up_to = find_unordered_guint8 ( +- offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ offsets.array, value.checked_offsets_up_to, index_ + 1); + break; + } + case 2: + { + value.ordered_offsets_up_to = find_unordered_guint16 ( +- offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ offsets.array, value.checked_offsets_up_to, index_ + 1); + break; + } + case 4: + { + value.ordered_offsets_up_to = find_unordered_guint32 ( +- offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ offsets.array, value.checked_offsets_up_to, index_ + 1); + break; + } + case 8: + { + value.ordered_offsets_up_to = find_unordered_guint64 ( +- offsets.array, value.ordered_offsets_up_to, index_ + 1); ++ offsets.array, value.checked_offsets_up_to, index_ + 1); + break; + } + default: + /* gvs_get_offset_size() only returns maximum 8 */ + g_assert_not_reached (); + } ++ ++ value.checked_offsets_up_to = index_; + } + + if (index_ > value.ordered_offsets_up_to) +@@ -916,6 +932,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value) + + /* All offsets have now been checked. */ + value.ordered_offsets_up_to = G_MAXSIZE; ++ value.checked_offsets_up_to = G_MAXSIZE; + + return TRUE; + } +@@ -1040,14 +1057,15 @@ gvs_tuple_get_child (GVariantSerialised value, + * all the tuple *elements* here, not just all the framing offsets, since + * tuples contain a mix of elements which use framing offsets and ones which + * don’t. None of them are allowed to overlap. */ +- if (index_ > value.ordered_offsets_up_to) ++ if (index_ > value.checked_offsets_up_to && ++ value.ordered_offsets_up_to == value.checked_offsets_up_to) + { + gsize i, prev_i_end = 0; + +- if (value.ordered_offsets_up_to > 0) +- gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end); ++ if (value.checked_offsets_up_to > 0) ++ gvs_tuple_get_member_bounds (value, value.checked_offsets_up_to - 1, offset_size, NULL, &prev_i_end); + +- for (i = value.ordered_offsets_up_to; i <= index_; i++) ++ for (i = value.checked_offsets_up_to; i <= index_; i++) + { + gsize i_start, i_end; + +@@ -1060,6 +1078,7 @@ gvs_tuple_get_child (GVariantSerialised value, + } + + value.ordered_offsets_up_to = i - 1; ++ value.checked_offsets_up_to = index_; + } + + if (index_ > value.ordered_offsets_up_to) +@@ -1257,6 +1276,7 @@ gvs_tuple_is_normal (GVariantSerialised value) + + /* All element bounds have been checked above. */ + value.ordered_offsets_up_to = G_MAXSIZE; ++ value.checked_offsets_up_to = G_MAXSIZE; + + { + gsize fixed_size; +diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h +index 765bd43b9..113df2ccc 100644 +--- a/glib/gvariant-serialiser.h ++++ b/glib/gvariant-serialiser.h +@@ -41,6 +41,15 @@ typedef struct + * Even when dealing with tuples, @ordered_offsets_up_to is an element index, + * rather than an index into the frame offsets. */ + gsize ordered_offsets_up_to; ++ ++ /* Similar to @ordered_offsets_up_to. This gives the index of the child element ++ * whose frame offset is the highest in the offset table which has been ++ * checked so far. ++ * ++ * This is always ≥ @ordered_offsets_up_to. It is always an element index. ++ * ++ * See documentation in gvariant-core.c for `struct GVariant` for details. */ ++ gsize checked_offsets_up_to; + } GVariantSerialised; + + /* deserialisation */ +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 5f6eb63ee..3e1adffaa 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5954,6 +5954,7 @@ g_variant_byteswap (GVariant *value) + serialised.data = g_malloc (serialised.size); + serialised.depth = g_variant_get_depth (trusted); + serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */ ++ serialised.checked_offsets_up_to = G_MAXSIZE; + g_variant_store (trusted, serialised.data); + g_variant_unref (trusted); + +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index a565ea69a..a13655eef 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -1284,6 +1284,7 @@ random_instance_filler (GVariantSerialised *serialised, + + serialised->depth = 0; + serialised->ordered_offsets_up_to = 0; ++ serialised->checked_offsets_up_to = 0; + + g_assert_true (serialised->type_info == instance->type_info); + g_assert_cmpuint (serialised->size, ==, instance->size); +@@ -1451,6 +1452,7 @@ test_maybe (void) + serialised.size = needed_size; + serialised.depth = 0; + serialised.ordered_offsets_up_to = 0; ++ serialised.checked_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, + random_instance_filler, +@@ -1575,6 +1577,7 @@ test_array (void) + serialised.size = needed_size; + serialised.depth = 0; + serialised.ordered_offsets_up_to = 0; ++ serialised.checked_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) instances, n_children); +@@ -1740,6 +1743,7 @@ test_tuple (void) + serialised.size = needed_size; + serialised.depth = 0; + serialised.ordered_offsets_up_to = 0; ++ serialised.checked_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) instances, n_children); +@@ -1837,6 +1841,7 @@ test_variant (void) + serialised.size = needed_size; + serialised.depth = 0; + serialised.ordered_offsets_up_to = 0; ++ serialised.checked_offsets_up_to = 0; + + g_variant_serialiser_serialise (serialised, random_instance_filler, + (gpointer *) &instance, 1); +-- +2.40.0 + +From 319f859c4ad021f636df67d61fa674afdd10d3c2 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 12 Jun 2020 18:01:13 +0100 +Subject: [PATCH 10/19] tests: Add another test for overlapping offsets in + GVariant + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/tests/gvariant.c | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index a13655eef..965aa98af 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -5013,6 +5013,38 @@ test_recursion_limits_array_in_variant (void) + g_variant_unref (wrapper_variant); + } + ++/* Test that a nested array with invalid values in its offset table (which point ++ * from the inner to the outer array) is normalised successfully without ++ * looping infinitely. */ ++static void ++test_normal_checking_array_offsets_overlapped (void) ++{ ++ const guint8 data[] = { ++ 0x01, 0x00, ++ }; ++ gsize size = sizeof (data); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected_variant = NULL; ++ ++ variant = g_variant_new_from_data (G_VARIANT_TYPE ("aay"), data, size, ++ FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ ++ expected_variant = g_variant_new_parsed ("[@ay [], []]"); ++ g_assert_cmpvariant (normal_variant, expected_variant); ++ ++ g_assert_cmpmem (g_variant_get_data (normal_variant), g_variant_get_size (normal_variant), ++ g_variant_get_data (expected_variant), g_variant_get_size (expected_variant)); ++ ++ g_variant_unref (expected_variant); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ + /* Test that an array with invalidly large values in its offset table is + * normalised successfully without looping infinitely. */ + static void +@@ -5413,6 +5445,8 @@ main (int argc, char **argv) + + g_test_add_func ("/gvariant/normal-checking/tuples", + test_normal_checking_tuples); ++ g_test_add_func ("/gvariant/normal-checking/array-offsets/overlapped", ++ test_normal_checking_array_offsets_overlapped); + g_test_add_func ("/gvariant/normal-checking/array-offsets", + test_normal_checking_array_offsets); + g_test_add_func ("/gvariant/normal-checking/array-offsets2", +-- +2.40.0 + +From 24ac75fab0b869fbf7e2cb970974ba59bc1a8a10 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 24 Oct 2022 16:43:23 +0100 +Subject: [PATCH 11/19] tests: Disable some random instance tests of GVariants +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Building a `GVariant` using entirely random data may result in a +non-normally-formed `GVariant`. It’s always possible to read these +`GVariant`s, but the API might return default values for some or all of +their components. + +In particular, this can easily happen when randomly generating the +offset tables for non-fixed-width container types. + +If it does happen, bytewise comparison of the parsed `GVariant` with the +original bytes will not always match. So skip those checks. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/tests/gvariant.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 965aa98af..f3b975652 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -1231,6 +1231,7 @@ random_instance_assert (RandomInstance *instance, + GRand *rand; + gsize i; + ++ g_assert_true (size == 0 || buffer != NULL); + g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0); + g_assert_cmpint (size, ==, instance->size); + +@@ -1457,10 +1458,13 @@ test_maybe (void) + g_variant_serialiser_serialise (serialised, + random_instance_filler, + (gpointer *) &instance, 1); ++ + child = g_variant_serialised_get_child (serialised, 0); + g_assert_true (child.type_info == instance->type_info); +- random_instance_assert (instance, child.data, child.size); ++ if (child.data != NULL) /* could be NULL if element is non-normal */ ++ random_instance_assert (instance, child.data, child.size); + g_variant_type_info_unref (child.type_info); ++ + flavoured_free (serialised.data, flavour); + } + } +@@ -1593,7 +1597,8 @@ test_array (void) + + child = g_variant_serialised_get_child (serialised, i); + g_assert_true (child.type_info == instances[i]->type_info); +- random_instance_assert (instances[i], child.data, child.size); ++ if (child.data != NULL) /* could be NULL if element is non-normal */ ++ random_instance_assert (instances[i], child.data, child.size); + g_variant_type_info_unref (child.type_info); + } + +@@ -1759,7 +1764,8 @@ test_tuple (void) + + child = g_variant_serialised_get_child (serialised, i); + g_assert_true (child.type_info == instances[i]->type_info); +- random_instance_assert (instances[i], child.data, child.size); ++ if (child.data != NULL) /* could be NULL if element is non-normal */ ++ random_instance_assert (instances[i], child.data, child.size); + g_variant_type_info_unref (child.type_info); + } + +-- +2.40.0 + +From 88bbf60b61e4aada5b8ebd7deb91fff1f25e60c2 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 24 Oct 2022 18:14:57 +0100 +Subject: [PATCH 12/19] gvariant: Clarify the docs for + g_variant_get_normal_form() + +Document how non-normal parts of the `GVariant` are handled. + +Signed-off-by: Philip Withnall +--- + glib/gvariant.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 3e1adffaa..2a6aaa1f4 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5879,7 +5879,9 @@ g_variant_deep_copy (GVariant *value) + * marked as trusted and a new reference to it is returned. + * + * If @value is found not to be in normal form then a new trusted +- * #GVariant is created with the same value as @value. ++ * #GVariant is created with the same value as @value. The non-normal parts of ++ * @value will be replaced with default values which are guaranteed to be in ++ * normal form. + * + * It makes sense to call this function if you've received #GVariant + * data from untrusted sources and you want to ensure your serialised +-- +2.40.0 + +From 2f3b16d3439e4843718428c21aaedd1ecba868d7 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 24 Oct 2022 18:43:55 +0100 +Subject: [PATCH 13/19] gvariant: Port g_variant_deep_copy() to count its + iterations directly + +This is equivalent to what `GVariantIter` does, but it means that +`g_variant_deep_copy()` is making its own `g_variant_get_child_value()` +calls. + +This will be useful in an upcoming commit, where those child values will +be inspected a little more deeply. + +Signed-off-by: Philip Withnall + +Helps: #2121 +--- + glib/gvariant.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 2a6aaa1f4..40d3639bc 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5806,14 +5806,13 @@ g_variant_deep_copy (GVariant *value) + case G_VARIANT_CLASS_VARIANT: + { + GVariantBuilder builder; +- GVariantIter iter; +- GVariant *child; ++ gsize i, n_children; + + g_variant_builder_init (&builder, g_variant_get_type (value)); +- g_variant_iter_init (&iter, value); + +- while ((child = g_variant_iter_next_value (&iter))) ++ for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) + { ++ GVariant *child = g_variant_get_child_value (value, i); + g_variant_builder_add_value (&builder, g_variant_deep_copy (child)); + g_variant_unref (child); + } +-- +2.40.0 + +From 81567d2bd81f303061b93be34da96ef2e2b11693 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 25 Oct 2022 13:03:22 +0100 +Subject: [PATCH 14/19] gvariant: Add internal + g_variant_maybe_get_child_value() + +This will be used in a following commit. + +Signed-off-by: Philip Withnall + +Helps: #2540 +--- + glib/gvariant-core.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ + glib/gvariant-core.h | 3 ++ + 2 files changed, 71 insertions(+) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index 1efcf200f..dbcf60f69 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -1204,6 +1204,74 @@ g_variant_get_child_value (GVariant *value, + } + } + ++/** ++ * g_variant_maybe_get_child_value: ++ * @value: a container #GVariant ++ * @index_: the index of the child to fetch ++ * ++ * Reads a child item out of a container #GVariant instance, if it is in normal ++ * form. If it is not in normal form, return %NULL. ++ * ++ * This function behaves the same as g_variant_get_child_value(), except that it ++ * returns %NULL if the child is not in normal form. g_variant_get_child_value() ++ * would instead return a new default value of the correct type. ++ * ++ * This is intended to be used internally to avoid unnecessary #GVariant ++ * allocations. ++ * ++ * The returned value is never floating. You should free it with ++ * g_variant_unref() when you're done with it. ++ * ++ * This function is O(1). ++ * ++ * Returns: (transfer full): the child at the specified index ++ * ++ * Since: 2.74 ++ */ ++GVariant * ++g_variant_maybe_get_child_value (GVariant *value, ++ gsize index_) ++{ ++ g_return_val_if_fail (index_ < g_variant_n_children (value), NULL); ++ g_return_val_if_fail (value->depth < G_MAXSIZE, NULL); ++ ++ if (~g_atomic_int_get (&value->state) & STATE_SERIALISED) ++ { ++ g_variant_lock (value); ++ ++ if (~value->state & STATE_SERIALISED) ++ { ++ GVariant *child; ++ ++ child = g_variant_ref (value->contents.tree.children[index_]); ++ g_variant_unlock (value); ++ ++ return child; ++ } ++ ++ g_variant_unlock (value); ++ } ++ ++ { ++ GVariantSerialised serialised = g_variant_to_serialised (value); ++ GVariantSerialised s_child; ++ ++ /* get the serializer to extract the serialized data for the child ++ * from the serialized data for the container ++ */ ++ s_child = g_variant_serialised_get_child (serialised, index_); ++ ++ if (!(value->state & STATE_TRUSTED) && s_child.data == NULL) ++ { ++ g_variant_type_info_unref (s_child.type_info); ++ return NULL; ++ } ++ ++ g_variant_type_info_unref (s_child.type_info); ++ return g_variant_get_child_value (value, index_); ++ } ++} ++ + /** + * g_variant_store: + * @value: the #GVariant to store +diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h +index fc04711ac..947a98ca0 100644 +--- a/glib/gvariant-core.h ++++ b/glib/gvariant-core.h +@@ -36,4 +36,7 @@ GVariantTypeInfo * g_variant_get_type_info (GVarian + + gsize g_variant_get_depth (GVariant *value); + ++GVariant * g_variant_maybe_get_child_value (GVariant *value, ++ gsize index_); ++ + #endif /* __G_VARIANT_CORE_H__ */ +-- +2.40.0 + +From 2db1a61cabfc4919277f004c411c753bcfa71235 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 25 Oct 2022 13:03:45 +0100 +Subject: [PATCH 15/19] gvariant: Cut allocs of default values for children of + non-normal arrays +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This improves a slow case in `g_variant_get_normal_form()` where +allocating many identical default values for the children of a +variable-sized array which has a malformed offset table would take a lot +of time. + +The fix is to make all child values after the first invalid one be +references to the default value emitted for the first invalid one, +rather than identical new `GVariant`s. + +In particular, this fixes a case where an attacker could create an array +of length L of very large tuples of size T each, corrupt the offset table +so they don’t have to specify the array content, and then induce +`g_variant_get_normal_form()` into allocating L×T default values from an +input which is significantly smaller than L×T in length. + +A pre-existing workaround for this issue is for code to call +`g_variant_is_normal_form()` before calling +`g_variant_get_normal_form()`, and to skip the latter call if the former +returns false. This commit improves the behaviour in the case that +`g_variant_get_normal_form()` is called anyway. + +This fix changes the time to run the `fuzz_variant_binary` test on the +testcase from oss-fuzz#19777 from >60s (before being terminated) with +2.3GB of memory usage and 580k page faults; to 32s, 8.3MB of memory +usage and 1500 page faults (as measured by `time -v`). + +Signed-off-by: Philip Withnall + +Fixes: #2540 +oss-fuzz#19777 +--- + glib/gvariant.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 65 insertions(+), 1 deletion(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 40d3639bc..63c134839 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5800,7 +5800,6 @@ g_variant_deep_copy (GVariant *value) + switch (g_variant_classify (value)) + { + case G_VARIANT_CLASS_MAYBE: +- case G_VARIANT_CLASS_ARRAY: + case G_VARIANT_CLASS_TUPLE: + case G_VARIANT_CLASS_DICT_ENTRY: + case G_VARIANT_CLASS_VARIANT: +@@ -5820,6 +5819,71 @@ g_variant_deep_copy (GVariant *value) + return g_variant_builder_end (&builder); + } + ++ case G_VARIANT_CLASS_ARRAY: ++ { ++ GVariantBuilder builder; ++ gsize i, n_children; ++ GVariant *first_invalid_child_deep_copy = NULL; ++ ++ /* Arrays are in theory treated the same as maybes, tuples, dict entries ++ * and variants, and could be another case in the above block of code. ++ * ++ * However, they have the property that when dealing with non-normal ++ * data (which is the only time g_variant_deep_copy() is currently ++ * called) in a variable-sized array, the code above can easily end up ++ * creating many default child values in order to return an array which ++ * is of the right length and type, but without containing non-normal ++ * data. This can happen if the offset table for the array is malformed. ++ * ++ * In this case, the code above would end up allocating the same default ++ * value for each one of the child indexes beyond the first malformed ++ * entry in the offset table. This can end up being a lot of identical ++ * allocations of default values, particularly if the non-normal array ++ * is crafted maliciously. ++ * ++ * Avoid that problem by returning a new reference to the same default ++ * value for every child after the first invalid one. This results in ++ * returning an equivalent array, in normal form and trusted — but with ++ * significantly fewer memory allocations. ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2540 */ ++ ++ g_variant_builder_init (&builder, g_variant_get_type (value)); ++ ++ for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) ++ { ++ /* Try maybe_get_child_value() first; if it returns NULL, this child ++ * is non-normal. get_child_value() would have constructed and ++ * returned a default value in that case. */ ++ GVariant *child = g_variant_maybe_get_child_value (value, i); ++ ++ if (child != NULL) ++ { ++ /* Non-normal children may not always be contiguous, as they may ++ * be non-normal for reasons other than invalid offset table ++ * entries. As they are all the same type, they will all have ++ * the same default value though, so keep that around. */ ++ g_variant_builder_add_value (&builder, g_variant_deep_copy (child)); ++ } ++ else if (child == NULL && first_invalid_child_deep_copy != NULL) ++ { ++ g_variant_builder_add_value (&builder, first_invalid_child_deep_copy); ++ } ++ else if (child == NULL) ++ { ++ child = g_variant_get_child_value (value, i); ++ first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child)); ++ g_variant_builder_add_value (&builder, first_invalid_child_deep_copy); ++ } ++ ++ g_clear_pointer (&child, g_variant_unref); ++ } ++ ++ g_clear_pointer (&first_invalid_child_deep_copy, g_variant_unref); ++ ++ return g_variant_builder_end (&builder); ++ } ++ + case G_VARIANT_CLASS_BOOLEAN: + return g_variant_new_boolean (g_variant_get_boolean (value)); + +-- +2.40.0 + +From b4cfb50f42950b0f2f382865f23e9ff02ba05aa4 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 25 Oct 2022 18:03:56 +0100 +Subject: [PATCH 16/19] gvariant: Fix a leak of a GVariantTypeInfo on an error + handling path + +Signed-off-by: Philip Withnall +--- + glib/gvariant-core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index dbcf60f69..2d2508244 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -1183,6 +1183,7 @@ g_variant_get_child_value (GVariant *value, + G_VARIANT_MAX_RECURSION_DEPTH - value->depth) + { + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)); ++ g_variant_type_info_unref (s_child.type_info); + return g_variant_new_tuple (NULL, 0); + } + +-- +2.40.0 + +From 6dd5afa8a0daf574210ea21f1536f69960c0d007 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 27 Oct 2022 12:00:04 +0100 +Subject: [PATCH 17/19] gvariant-serialiser: Check offset table entry size is + minimal + +The entries in an offset table (which is used for variable sized arrays +and tuples containing variable sized members) are sized so that they can +address every byte in the overall variant. + +The specification requires that for a variant to be in normal form, its +offset table entries must be the minimum width such that they can +address every byte in the variant. + +That minimality requirement was not checked in +`g_variant_is_normal_form()`, leading to two different byte arrays being +interpreted as the normal form of a given variant tree. That kind of +confusion could potentially be exploited, and is certainly a bug. + +Fix it by adding the necessary checks on offset table entry width, and +unit tests. + +Spotted by William Manley. + +Signed-off-by: Philip Withnall + +Fixes: #2794 +--- + glib/gvariant-serialiser.c | 19 +++- + glib/tests/gvariant.c | 176 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 194 insertions(+), 1 deletion(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index 8e01ab53e..a6141f7d0 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value) + out.data_size = last_end; + out.array = value.data + last_end; + out.length = offsets_array_size / out.offset_size; ++ ++ if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size) ++ return out; /* offset size not minimal */ ++ + out.is_normal = TRUE; + + return out; +@@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value) + gsize length; + gsize offset; + gsize i; ++ gsize offset_table_size; + + /* as per the comment in gvs_tuple_get_child() */ + if G_UNLIKELY (value.data == NULL && value.size != 0) +@@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value) + } + } + +- return offset_ptr == offset; ++ /* @offset_ptr has been counting backwards from the end of the variant, to ++ * find the beginning of the offset table. @offset has been counting forwards ++ * from the beginning of the variant to find the end of the data. They should ++ * have met in the middle. */ ++ if (offset_ptr != offset) ++ return FALSE; ++ ++ offset_table_size = value.size - offset_ptr; ++ if (value.size > 0 && ++ gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size) ++ return FALSE; /* offset size not minimal */ ++ ++ return TRUE; + } + + /* Variants {{{2 +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index f3b975652..028543833 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -5116,6 +5116,86 @@ test_normal_checking_array_offsets2 (void) + g_variant_unref (variant); + } + ++/* Test that an otherwise-valid serialised GVariant is considered non-normal if ++ * its offset table entries are too wide. ++ * ++ * See §2.3.6 (Framing Offsets) of the GVariant specification. */ ++static void ++test_normal_checking_array_offsets_minimal_sized (void) ++{ ++ GVariantBuilder builder; ++ gsize i; ++ GVariant *aay_constructed = NULL; ++ const guint8 *data = NULL; ++ guint8 *data_owned = NULL; ++ GVariant *aay_deserialised = NULL; ++ GVariant *aay_normalised = NULL; ++ ++ /* Construct an array of type aay, consisting of 128 elements which are each ++ * an empty array, i.e. `[[] * 128]`. This is chosen because the inner ++ * elements are variable sized (making the outer array variable sized, so it ++ * must have an offset table), but they are also zero-sized when serialised. ++ * So the serialised representation of @aay_constructed consists entirely of ++ * its offset table, which is entirely zeroes. ++ * ++ * The array is chosen to be 128 elements long because that means offset ++ * table entries which are 1 byte long. If the elements in the array were ++ * non-zero-sized (to the extent that the overall array is ≥256 bytes long), ++ * the offset table entries would end up being 2 bytes long. */ ++ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay")); ++ ++ for (i = 0; i < 128; i++) ++ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); ++ ++ aay_constructed = g_variant_builder_end (&builder); ++ ++ /* Verify that the constructed array is in normal form, and its serialised ++ * form is `b'\0' * 128`. */ ++ g_assert_true (g_variant_is_normal_form (aay_constructed)); ++ g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128); ++ g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128); ++ ++ data = g_variant_get_data (aay_constructed); ++ for (i = 0; i < g_variant_get_size (aay_constructed); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to ++ * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table ++ * entries, because each offset table entry has to be able to reference all of ++ * the byte boundaries in the container. All the entries in the offset table ++ * are zero, so all the elements of the array are zero-sized. */ ++ data = data_owned = g_malloc0 (256); ++ aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"), ++ data, ++ 256, ++ FALSE, ++ g_free, ++ g_steal_pointer (&data_owned)); ++ ++ g_assert_false (g_variant_is_normal_form (aay_deserialised)); ++ g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128); ++ g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256); ++ ++ data = g_variant_get_data (aay_deserialised); ++ for (i = 0; i < g_variant_get_size (aay_deserialised); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ /* Get its normal form. That should change the serialised size. */ ++ aay_normalised = g_variant_get_normal_form (aay_deserialised); ++ ++ g_assert_true (g_variant_is_normal_form (aay_normalised)); ++ g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128); ++ g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128); ++ ++ data = g_variant_get_data (aay_normalised); ++ for (i = 0; i < g_variant_get_size (aay_normalised); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ g_variant_unref (aay_normalised); ++ g_variant_unref (aay_deserialised); ++ g_variant_unref (aay_constructed); ++} ++ + /* Test that a tuple with invalidly large values in its offset table is + * normalised successfully without looping infinitely. */ + static void +@@ -5310,6 +5390,98 @@ test_normal_checking_tuple_offsets4 (void) + g_variant_unref (variant); + } + ++/* Test that an otherwise-valid serialised GVariant is considered non-normal if ++ * its offset table entries are too wide. ++ * ++ * See §2.3.6 (Framing Offsets) of the GVariant specification. */ ++static void ++test_normal_checking_tuple_offsets_minimal_sized (void) ++{ ++ GString *type_string = NULL; ++ GVariantBuilder builder; ++ gsize i; ++ GVariant *ray_constructed = NULL; ++ const guint8 *data = NULL; ++ guint8 *data_owned = NULL; ++ GVariant *ray_deserialised = NULL; ++ GVariant *ray_normalised = NULL; ++ ++ /* Construct a tuple of type (ay…ay), consisting of 129 members which are each ++ * an empty array, i.e. `([] * 129)`. This is chosen because the inner ++ * members are variable sized, so the outer tuple must have an offset table, ++ * but they are also zero-sized when serialised. So the serialised ++ * representation of @ray_constructed consists entirely of its offset table, ++ * which is entirely zeroes. ++ * ++ * The tuple is chosen to be 129 members long because that means it has 128 ++ * offset table entries which are 1 byte long each. If the members in the ++ * tuple were non-zero-sized (to the extent that the overall tuple is ≥256 ++ * bytes long), the offset table entries would end up being 2 bytes long. ++ * ++ * 129 members are used unlike 128 array elements in ++ * test_normal_checking_array_offsets_minimal_sized(), because the last member ++ * in a tuple never needs an offset table entry. */ ++ type_string = g_string_new (""); ++ g_string_append_c (type_string, '('); ++ for (i = 0; i < 129; i++) ++ g_string_append (type_string, "ay"); ++ g_string_append_c (type_string, ')'); ++ ++ g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str)); ++ ++ for (i = 0; i < 129; i++) ++ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0)); ++ ++ ray_constructed = g_variant_builder_end (&builder); ++ ++ /* Verify that the constructed tuple is in normal form, and its serialised ++ * form is `b'\0' * 128`. */ ++ g_assert_true (g_variant_is_normal_form (ray_constructed)); ++ g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129); ++ g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128); ++ ++ data = g_variant_get_data (ray_constructed); ++ for (i = 0; i < g_variant_get_size (ray_constructed); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has ++ * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table ++ * entries, because each offset table entry has to be able to reference all of ++ * the byte boundaries in the container. All the entries in the offset table ++ * are zero, so all the members of the tuple are zero-sized. */ ++ data = data_owned = g_malloc0 (256); ++ ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str), ++ data, ++ 256, ++ FALSE, ++ g_free, ++ g_steal_pointer (&data_owned)); ++ ++ g_assert_false (g_variant_is_normal_form (ray_deserialised)); ++ g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129); ++ g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256); ++ ++ data = g_variant_get_data (ray_deserialised); ++ for (i = 0; i < g_variant_get_size (ray_deserialised); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ /* Get its normal form. That should change the serialised size. */ ++ ray_normalised = g_variant_get_normal_form (ray_deserialised); ++ ++ g_assert_true (g_variant_is_normal_form (ray_normalised)); ++ g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129); ++ g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128); ++ ++ data = g_variant_get_data (ray_normalised); ++ for (i = 0; i < g_variant_get_size (ray_normalised); i++) ++ g_assert_cmpuint (data[i], ==, 0); ++ ++ g_variant_unref (ray_normalised); ++ g_variant_unref (ray_deserialised); ++ g_variant_unref (ray_constructed); ++ g_string_free (type_string, TRUE); ++} ++ + /* Test that an empty object path is normalised successfully to the base object + * path, ‘/’. */ + static void +@@ -5457,6 +5629,8 @@ main (int argc, char **argv) + test_normal_checking_array_offsets); + g_test_add_func ("/gvariant/normal-checking/array-offsets2", + test_normal_checking_array_offsets2); ++ g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized", ++ test_normal_checking_array_offsets_minimal_sized); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets", + test_normal_checking_tuple_offsets); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets2", +@@ -5465,6 +5639,8 @@ main (int argc, char **argv) + test_normal_checking_tuple_offsets3); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", + test_normal_checking_tuple_offsets4); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", ++ test_normal_checking_tuple_offsets_minimal_sized); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", + test_normal_checking_empty_object_path); + +-- +2.40.0 + +From 5ebfcae9b5e409cc2ad0c1e73175af2bdab455ac Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 27 Oct 2022 16:13:54 +0100 +Subject: [PATCH 18/19] gvariant: Fix g_variant_byteswap() returning non-normal + data sometimes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If `g_variant_byteswap()` was called on a non-normal variant of a type +which doesn’t need byteswapping, it would return a non-normal output. + +That contradicts the documentation, which says that the return value is +always in normal form. + +Fix the code so it matches the documentation. + +Includes a unit test. + +Signed-off-by: Philip Withnall + +Helps: #2797 +--- + glib/gvariant.c | 8 +++++--- + glib/tests/gvariant.c | 24 ++++++++++++++++++++++++ + 2 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 63c134839..9388c529d 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -6026,14 +6026,16 @@ g_variant_byteswap (GVariant *value) + g_variant_serialised_byteswap (serialised); + + bytes = g_bytes_new_take (serialised.data, serialised.size); +- new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE); ++ new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE)); + g_bytes_unref (bytes); + } + else + /* contains no multi-byte data */ +- new = value; ++ new = g_variant_get_normal_form (value); + +- return g_variant_ref_sink (new); ++ g_assert (g_variant_is_trusted (new)); ++ ++ return g_steal_pointer (&new); + } + + /** +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 028543833..871a6dab0 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -3826,6 +3826,29 @@ test_gv_byteswap (void) + g_free (string); + } + ++static void ++test_gv_byteswap_non_normal_non_aligned (void) ++{ ++ const guint8 data[] = { 0x02 }; ++ GVariant *v = NULL; ++ GVariant *v_byteswapped = NULL; ++ ++ g_test_summary ("Test that calling g_variant_byteswap() on a variant which " ++ "is in non-normal form and doesn’t need byteswapping returns " ++ "the same variant in normal form."); ++ ++ v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL); ++ g_assert_false (g_variant_is_normal_form (v)); ++ ++ v_byteswapped = g_variant_byteswap (v); ++ g_assert_true (g_variant_is_normal_form (v_byteswapped)); ++ ++ g_assert_cmpvariant (v, v_byteswapped); ++ ++ g_variant_unref (v); ++ g_variant_unref (v_byteswapped); ++} ++ + static void + test_parser (void) + { +@@ -5594,6 +5617,7 @@ main (int argc, char **argv) + g_test_add_func ("/gvariant/builder-memory", test_builder_memory); + g_test_add_func ("/gvariant/hashing", test_hashing); + g_test_add_func ("/gvariant/byteswap", test_gv_byteswap); ++ g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned); + g_test_add_func ("/gvariant/parser", test_parses); + g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds); + g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion); +-- +2.40.0 + +From c6d0ae6c04a4257e9e06edd186d19e8ac4f37024 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 27 Oct 2022 22:53:13 +0100 +Subject: [PATCH 19/19] gvariant: Allow g_variant_byteswap() to operate on + tree-form variants +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This avoids needing to always serialise a variant before byteswapping it. +With variants in non-normal forms, serialisation can result in a large +increase in size of the variant, and a lot of allocations for leaf +`GVariant`s. This can lead to a denial of service attack. + +Avoid that by changing byteswapping so that it happens on the tree form +of the variant if the input is in non-normal form. If the input is in +normal form (either serialised or in tree form), continue using the +existing code as byteswapping an already-serialised normal variant is +about 3× faster than byteswapping on the equivalent tree form. + +The existing unit tests cover byteswapping well, but need some +adaptation so that they operate on tree form variants too. + +I considered dropping the serialised byteswapping code and doing all +byteswapping on tree-form variants, as that would make maintenance +simpler (avoiding having two parallel implementations of byteswapping). +However, most inputs to `g_variant_byteswap()` are likely to be +serialised variants (coming from a byte array of input from some foreign +source) and most of them are going to be in normal form (as corruption +and malicious action are rare). So getting rid of the serialised +byteswapping code would impose quite a performance penalty on the common +case. + +Signed-off-by: Philip Withnall + +Fixes: #2797 +--- + glib/gvariant.c | 87 ++++++++++++++++++++++++++++++++----------- + glib/tests/gvariant.c | 57 ++++++++++++++++++++++++---- + 2 files changed, 115 insertions(+), 29 deletions(-) + +diff --git a/glib/gvariant.c b/glib/gvariant.c +index 9388c529d..1f0eccb73 100644 +--- a/glib/gvariant.c ++++ b/glib/gvariant.c +@@ -5795,7 +5795,8 @@ g_variant_iter_loop (GVariantIter *iter, + + /* Serialised data {{{1 */ + static GVariant * +-g_variant_deep_copy (GVariant *value) ++g_variant_deep_copy (GVariant *value, ++ gboolean byteswap) + { + switch (g_variant_classify (value)) + { +@@ -5812,7 +5813,7 @@ g_variant_deep_copy (GVariant *value) + for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) + { + GVariant *child = g_variant_get_child_value (value, i); +- g_variant_builder_add_value (&builder, g_variant_deep_copy (child)); ++ g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap)); + g_variant_unref (child); + } + +@@ -5863,7 +5864,7 @@ g_variant_deep_copy (GVariant *value) + * be non-normal for reasons other than invalid offset table + * entries. As they are all the same type, they will all have + * the same default value though, so keep that around. */ +- g_variant_builder_add_value (&builder, g_variant_deep_copy (child)); ++ g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap)); + } + else if (child == NULL && first_invalid_child_deep_copy != NULL) + { +@@ -5872,7 +5873,7 @@ g_variant_deep_copy (GVariant *value) + else if (child == NULL) + { + child = g_variant_get_child_value (value, i); +- first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child)); ++ first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child, byteswap)); + g_variant_builder_add_value (&builder, first_invalid_child_deep_copy); + } + +@@ -5891,28 +5892,63 @@ g_variant_deep_copy (GVariant *value) + return g_variant_new_byte (g_variant_get_byte (value)); + + case G_VARIANT_CLASS_INT16: +- return g_variant_new_int16 (g_variant_get_int16 (value)); ++ if (byteswap) ++ return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value))); ++ else ++ return g_variant_new_int16 (g_variant_get_int16 (value)); + + case G_VARIANT_CLASS_UINT16: +- return g_variant_new_uint16 (g_variant_get_uint16 (value)); ++ if (byteswap) ++ return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value))); ++ else ++ return g_variant_new_uint16 (g_variant_get_uint16 (value)); + + case G_VARIANT_CLASS_INT32: +- return g_variant_new_int32 (g_variant_get_int32 (value)); ++ if (byteswap) ++ return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value))); ++ else ++ return g_variant_new_int32 (g_variant_get_int32 (value)); + + case G_VARIANT_CLASS_UINT32: +- return g_variant_new_uint32 (g_variant_get_uint32 (value)); ++ if (byteswap) ++ return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value))); ++ else ++ return g_variant_new_uint32 (g_variant_get_uint32 (value)); + + case G_VARIANT_CLASS_INT64: +- return g_variant_new_int64 (g_variant_get_int64 (value)); ++ if (byteswap) ++ return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value))); ++ else ++ return g_variant_new_int64 (g_variant_get_int64 (value)); + + case G_VARIANT_CLASS_UINT64: +- return g_variant_new_uint64 (g_variant_get_uint64 (value)); ++ if (byteswap) ++ return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value))); ++ else ++ return g_variant_new_uint64 (g_variant_get_uint64 (value)); + + case G_VARIANT_CLASS_HANDLE: +- return g_variant_new_handle (g_variant_get_handle (value)); ++ if (byteswap) ++ return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value))); ++ else ++ return g_variant_new_handle (g_variant_get_handle (value)); + + case G_VARIANT_CLASS_DOUBLE: +- return g_variant_new_double (g_variant_get_double (value)); ++ if (byteswap) ++ { ++ /* We have to convert the double to a uint64 here using a union, ++ * because a cast will round it numerically. */ ++ union ++ { ++ guint64 u64; ++ gdouble dbl; ++ } u1, u2; ++ u1.dbl = g_variant_get_double (value); ++ u2.u64 = GUINT64_SWAP_LE_BE (u1.u64); ++ return g_variant_new_double (u2.dbl); ++ } ++ else ++ return g_variant_new_double (g_variant_get_double (value)); + + case G_VARIANT_CLASS_STRING: + return g_variant_new_string (g_variant_get_string (value, NULL)); +@@ -5969,7 +6005,7 @@ g_variant_get_normal_form (GVariant *value) + if (g_variant_is_normal_form (value)) + return g_variant_ref (value); + +- trusted = g_variant_deep_copy (value); ++ trusted = g_variant_deep_copy (value, FALSE); + g_assert (g_variant_is_trusted (trusted)); + + return g_variant_ref_sink (trusted); +@@ -5989,6 +6025,11 @@ g_variant_get_normal_form (GVariant *value) + * contain multi-byte numeric data. That include strings, booleans, + * bytes and containers containing only these things (recursively). + * ++ * While this function can safely handle untrusted, non-normal data, it is ++ * recommended to check whether the input is in normal form beforehand, using ++ * g_variant_is_normal_form(), and to reject non-normal inputs if your ++ * application can be strict about what inputs it rejects. ++ * + * The returned value is always in normal form and is marked as trusted. + * + * Returns: (transfer full): the byteswapped form of @value +@@ -6006,22 +6047,21 @@ g_variant_byteswap (GVariant *value) + + g_variant_type_info_query (type_info, &alignment, NULL); + +- if (alignment) +- /* (potentially) contains multi-byte numeric data */ ++ if (alignment && g_variant_is_normal_form (value)) + { ++ /* (potentially) contains multi-byte numeric data, but is also already in ++ * normal form so we can use a faster byteswapping codepath on the ++ * serialised data */ + GVariantSerialised serialised = { 0, }; +- GVariant *trusted; + GBytes *bytes; + +- trusted = g_variant_get_normal_form (value); +- serialised.type_info = g_variant_get_type_info (trusted); +- serialised.size = g_variant_get_size (trusted); ++ serialised.type_info = g_variant_get_type_info (value); ++ serialised.size = g_variant_get_size (value); + serialised.data = g_malloc (serialised.size); +- serialised.depth = g_variant_get_depth (trusted); ++ serialised.depth = g_variant_get_depth (value); + serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */ + serialised.checked_offsets_up_to = G_MAXSIZE; +- g_variant_store (trusted, serialised.data); +- g_variant_unref (trusted); ++ g_variant_store (value, serialised.data); + + g_variant_serialised_byteswap (serialised); + +@@ -6029,6 +6069,9 @@ g_variant_byteswap (GVariant *value) + new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE)); + g_bytes_unref (bytes); + } ++ else if (alignment) ++ /* (potentially) contains multi-byte numeric data */ ++ new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE)); + else + /* contains no multi-byte data */ + new = g_variant_get_normal_form (value); +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index 871a6dab0..143057de7 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -2288,24 +2288,67 @@ serialise_tree (TreeInstance *tree, + static void + test_byteswap (void) + { +- GVariantSerialised one = { 0, }, two = { 0, }; ++ GVariantSerialised one = { 0, }, two = { 0, }, three = { 0, }; + TreeInstance *tree; +- ++ GVariant *one_variant = NULL; ++ GVariant *two_variant = NULL; ++ GVariant *two_byteswapped = NULL; ++ GVariant *three_variant = NULL; ++ GVariant *three_byteswapped = NULL; ++ guint8 *three_data_copy = NULL; ++ gsize three_size_copy = 0; ++ ++ /* Write a tree out twice, once normally and once byteswapped. */ + tree = tree_instance_new (NULL, 3); + serialise_tree (tree, &one); + ++ one_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (one.type_info)), ++ one.data, one.size, FALSE, NULL, NULL); ++ + i_am_writing_byteswapped = TRUE; + serialise_tree (tree, &two); ++ serialise_tree (tree, &three); + i_am_writing_byteswapped = FALSE; + +- g_variant_serialised_byteswap (two); +- +- g_assert_cmpmem (one.data, one.size, two.data, two.size); +- g_assert_cmpuint (one.depth, ==, two.depth); +- ++ /* Swap the first byteswapped one back using the function we want to test. */ ++ two_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (two.type_info)), ++ two.data, two.size, FALSE, NULL, NULL); ++ two_byteswapped = g_variant_byteswap (two_variant); ++ ++ /* Make the second byteswapped one non-normal (hopefully), and then byteswap ++ * it back using the function we want to test in its non-normal mode. ++ * This might not work because it’s not necessarily possible to make an ++ * arbitrary random variant non-normal. Adding a single zero byte to the end ++ * often makes something non-normal but still readable. */ ++ three_size_copy = three.size + 1; ++ three_data_copy = g_malloc (three_size_copy); ++ memcpy (three_data_copy, three.data, three.size); ++ three_data_copy[three.size] = '\0'; ++ ++ three_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (three.type_info)), ++ three_data_copy, three_size_copy, FALSE, NULL, NULL); ++ three_byteswapped = g_variant_byteswap (three_variant); ++ ++ /* Check they’re the same. We can always compare @one_variant and ++ * @two_byteswapped. We can only compare @two_byteswapped and ++ * @three_byteswapped if @two_variant and @three_variant are equal: in that ++ * case, the corruption to @three_variant was enough to make it non-normal but ++ * not enough to change its value. */ ++ g_assert_cmpvariant (one_variant, two_byteswapped); ++ ++ if (g_variant_equal (two_variant, three_variant)) ++ g_assert_cmpvariant (two_byteswapped, three_byteswapped); ++ ++ g_variant_unref (three_byteswapped); ++ g_variant_unref (three_variant); ++ g_variant_unref (two_byteswapped); ++ g_variant_unref (two_variant); ++ g_variant_unref (one_variant); + tree_instance_free (tree); + g_free (one.data); + g_free (two.data); ++ g_free (three.data); ++ g_free (three_data_copy); + } + + static void +-- +2.40.0 diff --git a/base/rx/rx-glib2/3136.patch b/base/rx/rx-glib2/3136.patch new file mode 100644 index 0000000..57e1b95 --- /dev/null +++ b/base/rx/rx-glib2/3136.patch @@ -0,0 +1,65 @@ +From ba2137b0d9ea3744155be81a5ba770c6535b46f3 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 15 Dec 2022 12:51:37 +0000 +Subject: [PATCH] gvariant-serialiser: Convert endianness of offsets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The array of offsets is little-endian, even on big-endian architectures +like s390x. + +Fixes: ade71fb5 "gvariant: Don’t allow child elements to overlap with each other" +Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/2839 +Signed-off-by: Simon McVittie +--- + glib/gvariant-serialiser.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index fadefab659..f443c2eb85 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -714,17 +714,19 @@ gvs_variable_sized_array_n_children (GVariantSerialised value) + /* Find the index of the first out-of-order element in @data, assuming that + * @data is an array of elements of given @type, starting at index @start and + * containing a further @len-@start elements. */ +-#define DEFINE_FIND_UNORDERED(type) \ ++#define DEFINE_FIND_UNORDERED(type, le_to_native) \ + static gsize \ + find_unordered_##type (const guint8 *data, gsize start, gsize len) \ + { \ + gsize off; \ +- type current, previous; \ ++ type current_le, previous_le, current, previous; \ + \ +- memcpy (&previous, data + start * sizeof (current), sizeof (current)); \ ++ memcpy (&previous_le, data + start * sizeof (current), sizeof (current)); \ ++ previous = le_to_native (previous_le); \ + for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \ + { \ +- memcpy (¤t, data + off, sizeof (current)); \ ++ memcpy (¤t_le, data + off, sizeof (current)); \ ++ current = le_to_native (current_le); \ + if (current < previous) \ + break; \ + previous = current; \ +@@ -732,10 +734,11 @@ gvs_variable_sized_array_n_children (GVariantSerialised value) + return off / sizeof (current) - 1; \ + } + +-DEFINE_FIND_UNORDERED (guint8); +-DEFINE_FIND_UNORDERED (guint16); +-DEFINE_FIND_UNORDERED (guint32); +-DEFINE_FIND_UNORDERED (guint64); ++#define NO_CONVERSION(x) (x) ++DEFINE_FIND_UNORDERED (guint8, NO_CONVERSION); ++DEFINE_FIND_UNORDERED (guint16, GUINT16_FROM_LE); ++DEFINE_FIND_UNORDERED (guint32, GUINT32_FROM_LE); ++DEFINE_FIND_UNORDERED (guint64, GUINT64_FROM_LE); + + static GVariantSerialised + gvs_variable_sized_array_get_child (GVariantSerialised value, +-- +GitLab + diff --git a/base/rx/rx-glib2/3163.patch b/base/rx/rx-glib2/3163.patch new file mode 100644 index 0000000..13fe47c --- /dev/null +++ b/base/rx/rx-glib2/3163.patch @@ -0,0 +1,199 @@ +From 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 15 Dec 2022 13:00:39 +0000 +Subject: [PATCH 1/2] =?UTF-8?q?gvariant:=20Check=20offset=20table=20doesn?= + =?UTF-8?q?=E2=80=99t=20fall=20outside=20variant=20bounds?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When dereferencing the first entry in the offset table for a tuple, +check that it doesn’t fall outside the bounds of the variant first. + +This prevents an out-of-bounds read from some non-normal tuples. + +This bug was introduced in commit 73d0aa81c2575a5c9ae77d. + +Includes a unit test, although the test will likely only catch the +original bug if run with asan enabled. + +Signed-off-by: Philip Withnall + +Fixes: #2840 +oss-fuzz#54302 +--- + glib/gvariant-serialiser.c | 12 ++++++-- + glib/tests/gvariant.c | 63 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 72 insertions(+), 3 deletions(-) + +diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c +index f443c2eb85..4e4a73ad17 100644 +--- a/glib/gvariant-serialiser.c ++++ b/glib/gvariant-serialiser.c +@@ -984,7 +984,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, + + member_info = g_variant_type_info_member_info (value.type_info, index_); + +- if (member_info->i + 1) ++ if (member_info->i + 1 && ++ offset_size * (member_info->i + 1) <= value.size) + member_start = gvs_read_unaligned_le (value.data + value.size - + offset_size * (member_info->i + 1), + offset_size); +@@ -995,7 +996,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, + member_start &= member_info->b; + member_start |= member_info->c; + +- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST) ++ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST && ++ offset_size * (member_info->i + 1) <= value.size) + member_end = value.size - offset_size * (member_info->i + 1); + + else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) +@@ -1006,11 +1008,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised value, + member_end = member_start + fixed_size; + } + +- else /* G_VARIANT_MEMBER_ENDING_OFFSET */ ++ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET && ++ offset_size * (member_info->i + 2) <= value.size) + member_end = gvs_read_unaligned_le (value.data + value.size - + offset_size * (member_info->i + 2), + offset_size); + ++ else /* invalid */ ++ member_end = G_MAXSIZE; ++ + if (out_member_start != NULL) + *out_member_start = member_start; + if (out_member_end != NULL) +diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c +index b360888e4d..98c51a1d75 100644 +--- a/glib/tests/gvariant.c ++++ b/glib/tests/gvariant.c +@@ -5576,6 +5576,67 @@ test_normal_checking_tuple_offsets4 (void) + g_variant_unref (variant); + } + ++/* This is a regression test that dereferencing the first element in the offset ++ * table doesn’t dereference memory before the start of the GVariant. The first ++ * element in the offset table gives the offset of the final member in the ++ * tuple (the offset table is stored in reverse), and the position of this final ++ * member is needed to check that none of the tuple members overlap with the ++ * offset table ++ * ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */ ++static void ++test_normal_checking_tuple_offsets5 (void) ++{ ++ /* A tuple of type (sss) in normal form would have an offset table with two ++ * entries: ++ * - The first entry (lowest index in the table) gives the offset of the ++ * third `s` in the tuple, as the offset table is reversed compared to the ++ * tuple members. ++ * - The second entry (highest index in the table) gives the offset of the ++ * second `s` in the tuple. ++ * - The offset of the first `s` in the tuple is always 0. ++ * ++ * See §2.5.4 (Structures) of the GVariant specification for details, noting ++ * that the table is only layed out this way because all three members of the ++ * tuple have non-fixed sizes. ++ * ++ * It’s not clear whether the 0xaa data of this variant is part of the strings ++ * in the tuple, or part of the offset table. It doesn’t really matter. This ++ * is a regression test to check that the code to validate the offset table ++ * doesn’t unconditionally try to access the first entry in the offset table ++ * by subtracting the table size from the end of the GVariant data. ++ * ++ * In this non-normal case, that would result in an address off the start of ++ * the GVariant data, and an out-of-bounds read, because the GVariant is one ++ * byte long, but the offset table is calculated as two bytes long (with 1B ++ * sized entries) from the tuple’s type. ++ */ ++ const GVariantType *data_type = G_VARIANT_TYPE ("(sss)"); ++ const guint8 data[] = { 0xaa }; ++ gsize size = sizeof (data); ++ GVariant *variant = NULL; ++ GVariant *normal_variant = NULL; ++ GVariant *expected = NULL; ++ ++ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840"); ++ ++ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL); ++ g_assert_nonnull (variant); ++ ++ g_assert_false (g_variant_is_normal_form (variant)); ++ ++ normal_variant = g_variant_get_normal_form (variant); ++ g_assert_nonnull (normal_variant); ++ ++ expected = g_variant_new_parsed ("('', '', '')"); ++ g_assert_cmpvariant (expected, variant); ++ g_assert_cmpvariant (expected, normal_variant); ++ ++ g_variant_unref (expected); ++ g_variant_unref (normal_variant); ++ g_variant_unref (variant); ++} ++ + /* Test that an otherwise-valid serialised GVariant is considered non-normal if + * its offset table entries are too wide. + * +@@ -5827,6 +5888,8 @@ main (int argc, char **argv) + test_normal_checking_tuple_offsets3); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets4", + test_normal_checking_tuple_offsets4); ++ g_test_add_func ("/gvariant/normal-checking/tuple-offsets5", ++ test_normal_checking_tuple_offsets5); + g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized", + test_normal_checking_tuple_offsets_minimal_sized); + g_test_add_func ("/gvariant/normal-checking/empty-object-path", +-- +GitLab + + +From 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Thu, 15 Dec 2022 16:49:28 +0000 +Subject: [PATCH 2/2] gvariant: Propagate trust when getting a child of a + serialised variant + +If a variant is trusted, that means all its children are trusted, so +ensure that their checked offsets are set as such. + +This allows a lot of the offset table checks to be avoided when getting +children from trusted serialised tuples, which speeds things up. + +No unit test is included because this is just a performance fix. If +there are other slownesses, or regressions, in serialised `GVariant` +performance, the fuzzing setup will catch them like it did this one. + +This change does reduce the time to run the oss-fuzz reproducer from 80s +to about 0.7s on my machine. + +Signed-off-by: Philip Withnall + +Fixes: #2841 +oss-fuzz#54314 +--- + glib/gvariant-core.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c +index f441c4757e..4778022829 100644 +--- a/glib/gvariant-core.c ++++ b/glib/gvariant-core.c +@@ -1198,8 +1198,8 @@ g_variant_get_child_value (GVariant *value, + child->contents.serialised.bytes = + g_bytes_ref (value->contents.serialised.bytes); + child->contents.serialised.data = s_child.data; +- child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to; +- child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to; ++ child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to; ++ child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to; + + return child; + } +-- +GitLab + diff --git a/base/rx/rx-glib2/3272.patch b/base/rx/rx-glib2/3272.patch new file mode 100644 index 0000000..c97e428 --- /dev/null +++ b/base/rx/rx-glib2/3272.patch @@ -0,0 +1,141 @@ +From 059f4f3999f1de506417611318c6f27db57fb689 Mon Sep 17 00:00:00 2001 +From: Marius Vollmer +Date: Mon, 13 Feb 2023 14:12:52 +0200 +Subject: [PATCH] gdbus: Never buffer reads during server authentication + +Otherwise, the content of the buffer is thrown away when switching +from reading via a GDataInputStream to unbuffered reads when waiting +for the "BEGIN" line. + +(The code already tried to protect against over-reading like this by +using unbuffered reads for the last few lines of the auth protocol, +but it might already be too late at that point. The buffer of the +GDataInputStream might already contain the "BEGIN" line for example.) + +This matters when connecting a sd-bus client directly to a GDBus +client. A sd-bus client optimistically sends the whole auth +conversation in one go without waiting for intermediate replies. This +is done to improve performance for the many short-lived connections +that are typically made. +--- + gio/gdbusauth.c | 50 ++++++++++++++++++++++++++++++------------------- + 1 file changed, 31 insertions(+), 19 deletions(-) + +diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c +index c430f0cf0..17c7d47b7 100644 +--- a/gio/gdbusauth.c ++++ b/gio/gdbusauth.c +@@ -933,7 +933,6 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + { + gboolean ret; + ServerState state; +- GDataInputStream *dis; + GDataOutputStream *dos; + GError *local_error; + gchar *line; +@@ -949,7 +948,6 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + _g_dbus_auth_add_mechs (auth, observer); + + ret = FALSE; +- dis = NULL; + dos = NULL; + mech = NULL; + negotiated_capabilities = 0; +@@ -965,13 +963,18 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + goto out; + } + +- dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream))); ++ /* We use an extremely slow (but reliable) line reader for input ++ * instead of something buffered - this basically does a recvfrom() ++ * system call per character ++ * ++ * (the problem with using GDataInputStream's read_line is that ++ * because of buffering it might start reading into the first D-Bus ++ * message that appears after "BEGIN\r\n"....) ++ */ ++ + dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream))); +- g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE); + g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE); + +- g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); +- + /* read the NUL-byte, possibly with credentials attached */ + #ifdef G_OS_UNIX + #ifndef G_CREDENTIALS_PREFER_MESSAGE_PASSING +@@ -1010,11 +1013,22 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + } + else + { ++ gchar c; ++ gssize num_read; ++ + local_error = NULL; +- (void)g_data_input_stream_read_byte (dis, cancellable, &local_error); +- if (local_error != NULL) ++ num_read = g_input_stream_read (g_io_stream_get_input_stream (auth->priv->stream), ++ &c, 1, ++ cancellable, &local_error); ++ if (num_read != 1 || local_error != NULL) + { +- g_propagate_error (error, local_error); ++ if (local_error == NULL) ++ g_set_error_literal (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ _ ("Unexpected lack of content trying to read a byte")); ++ else ++ g_propagate_error (error, local_error); + goto out; + } + } +@@ -1050,7 +1064,10 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + { + case SERVER_STATE_WAITING_FOR_AUTH: + debug_print ("SERVER: WaitingForAuth"); +- line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); ++ line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), ++ &line_length, ++ cancellable, ++ error); + debug_print ("SERVER: WaitingForAuth, read '%s'", line); + if (line == NULL) + goto out; +@@ -1260,7 +1277,10 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + + case SERVER_STATE_WAITING_FOR_DATA: + debug_print ("SERVER: WaitingForData"); +- line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); ++ line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), ++ &line_length, ++ cancellable, ++ error); + debug_print ("SERVER: WaitingForData, read '%s'", line); + if (line == NULL) + goto out; +@@ -1299,13 +1319,6 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + + case SERVER_STATE_WAITING_FOR_BEGIN: + debug_print ("SERVER: WaitingForBegin"); +- /* Use extremely slow (but reliable) line reader - this basically +- * does a recvfrom() system call per character +- * +- * (the problem with using GDataInputStream's read_line is that because of +- * buffering it might start reading into the first D-Bus message that +- * appears after "BEGIN\r\n"....) +- */ + line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), + &line_length, + cancellable, +@@ -1364,7 +1377,6 @@ _g_dbus_auth_run_server (GDBusAuth *auth, + + out: + g_clear_object (&mech); +- g_clear_object (&dis); + g_clear_object (&dos); + g_clear_object (&own_credentials); + +-- +2.41.0 + diff --git a/base/rx/rx-glib2/3353.patch b/base/rx/rx-glib2/3353.patch new file mode 100644 index 0000000..eb9020a --- /dev/null +++ b/base/rx/rx-glib2/3353.patch @@ -0,0 +1,1078 @@ +From 68f0cd7f3d351da1f011a65e080d8e3f26a31525 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 28 Mar 2023 14:38:08 +0200 +Subject: [PATCH 1/7] gmain: unify win/unix implementations for child watcher + +Let's move the difference between the win/unix implementations closer to +where the difference is. Thereby, we easier see the two implementations +side by side. Splitting it at a higher layer makes the code harder to +read. + +This is just a preparation for what comes next. +--- + glib/gmain.c | 192 ++++++++++++++++++++++++--------------------------- + 1 file changed, 92 insertions(+), 100 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index f0cf700c0..228737efa 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -446,6 +446,11 @@ static gboolean g_child_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); + static void g_child_watch_finalize (GSource *source); ++ ++#ifndef G_OS_WIN32 ++static void unref_unix_signal_handler_unlocked (int signum); ++#endif ++ + #ifdef G_OS_UNIX + static void g_unix_signal_handler (int signum); + static gboolean g_unix_signal_watch_prepare (GSource *source, +@@ -5214,17 +5219,49 @@ g_timeout_add_seconds (guint interval, + + /* Child watch functions */ + +-#ifdef G_OS_WIN32 ++#ifdef HAVE_PIDFD ++static int ++siginfo_t_to_wait_status (const siginfo_t *info) ++{ ++ /* Each of these returns is essentially the inverse of WIFEXITED(), ++ * WIFSIGNALED(), etc. */ ++ switch (info->si_code) ++ { ++ case CLD_EXITED: ++ return W_EXITCODE (info->si_status, 0); ++ case CLD_KILLED: ++ return W_EXITCODE (0, info->si_status); ++ case CLD_DUMPED: ++ return W_EXITCODE (0, info->si_status | WCOREFLAG); ++ case CLD_CONTINUED: ++ return __W_CONTINUED; ++ case CLD_STOPPED: ++ case CLD_TRAPPED: ++ default: ++ return W_STOPCODE (info->si_status); ++ } ++} ++#endif /* HAVE_PIDFD */ + + static gboolean + g_child_watch_prepare (GSource *source, + gint *timeout) + { ++#ifdef G_OS_WIN32 + *timeout = -1; + return FALSE; ++#else /* G_OS_WIN32 */ ++ { ++ GChildWatchSource *child_watch_source; ++ ++ child_watch_source = (GChildWatchSource *) source; ++ ++ return g_atomic_int_get (&child_watch_source->child_exited); ++ } ++#endif /* G_OS_WIN32 */ + } + +-static gboolean ++static gboolean + g_child_watch_check (GSource *source) + { + GChildWatchSource *child_watch_source; +@@ -5232,6 +5269,7 @@ g_child_watch_check (GSource *source) + + child_watch_source = (GChildWatchSource *) source; + ++#ifdef G_OS_WIN32 + child_exited = child_watch_source->poll.revents & G_IO_IN; + + if (child_exited) +@@ -5246,15 +5284,45 @@ g_child_watch_check (GSource *source) + */ + if (!GetExitCodeProcess (child_watch_source->pid, &child_status)) + { +- gchar *emsg = g_win32_error_message (GetLastError ()); +- g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg); +- g_free (emsg); ++ gchar *emsg = g_win32_error_message (GetLastError ()); ++ g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg); ++ g_free (emsg); + +- child_watch_source->child_status = -1; +- } ++ child_watch_source->child_status = -1; ++ } + else +- child_watch_source->child_status = child_status; ++ child_watch_source->child_status = child_status; + } ++#else /* G_OS_WIN32 */ ++#ifdef HAVE_PIDFD ++ if (child_watch_source->using_pidfd) ++ { ++ child_exited = child_watch_source->poll.revents & G_IO_IN; ++ ++ if (child_exited) ++ { ++ siginfo_t child_info = { 0, }; ++ ++ /* Get the exit status */ ++ if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && ++ child_info.si_pid != 0) ++ { ++ /* waitid() helpfully provides the wait status in a decomposed ++ * form which is quite useful. Unfortunately we have to report it ++ * to the #GChildWatchFunc as a waitpid()-style platform-specific ++ * wait status, so that the user code in #GChildWatchFunc can then ++ * call WIFEXITED() (etc.) on it. That means re-composing the ++ * status information. */ ++ child_watch_source->child_status = siginfo_t_to_wait_status (&child_info); ++ child_watch_source->child_exited = TRUE; ++ } ++ } ++ ++ return child_exited; ++ } ++#endif /* HAVE_PIDFD */ ++ child_exited = g_atomic_int_get (&child_watch_source->child_exited); ++#endif /* G_OS_WIN32 */ + + return child_exited; + } +@@ -5262,9 +5330,24 @@ g_child_watch_check (GSource *source) + static void + g_child_watch_finalize (GSource *source) + { ++#ifndef G_OS_WIN32 ++ GChildWatchSource *child_watch_source = (GChildWatchSource *) source; ++ ++ if (child_watch_source->using_pidfd) ++ { ++ if (child_watch_source->poll.fd >= 0) ++ close (child_watch_source->poll.fd); ++ return; ++ } ++ ++ G_LOCK (unix_signal_lock); ++ unix_child_watches = g_slist_remove (unix_child_watches, source); ++ unref_unix_signal_handler_unlocked (SIGCHLD); ++ G_UNLOCK (unix_signal_lock); ++#endif /* G_OS_WIN32 */ + } + +-#else /* G_OS_WIN32 */ ++#ifndef G_OS_WIN32 + + static void + wake_source (GSource *source) +@@ -5397,79 +5480,6 @@ dispatch_unix_signals (void) + G_UNLOCK(unix_signal_lock); + } + +-static gboolean +-g_child_watch_prepare (GSource *source, +- gint *timeout) +-{ +- GChildWatchSource *child_watch_source; +- +- child_watch_source = (GChildWatchSource *) source; +- +- return g_atomic_int_get (&child_watch_source->child_exited); +-} +- +-#ifdef HAVE_PIDFD +-static int +-siginfo_t_to_wait_status (const siginfo_t *info) +-{ +- /* Each of these returns is essentially the inverse of WIFEXITED(), +- * WIFSIGNALED(), etc. */ +- switch (info->si_code) +- { +- case CLD_EXITED: +- return W_EXITCODE (info->si_status, 0); +- case CLD_KILLED: +- return W_EXITCODE (0, info->si_status); +- case CLD_DUMPED: +- return W_EXITCODE (0, info->si_status | WCOREFLAG); +- case CLD_CONTINUED: +- return __W_CONTINUED; +- case CLD_STOPPED: +- case CLD_TRAPPED: +- default: +- return W_STOPCODE (info->si_status); +- } +-} +-#endif /* HAVE_PIDFD */ +- +-static gboolean +-g_child_watch_check (GSource *source) +-{ +- GChildWatchSource *child_watch_source; +- +- child_watch_source = (GChildWatchSource *) source; +- +-#ifdef HAVE_PIDFD +- if (child_watch_source->using_pidfd) +- { +- gboolean child_exited = child_watch_source->poll.revents & G_IO_IN; +- +- if (child_exited) +- { +- siginfo_t child_info = { 0, }; +- +- /* Get the exit status */ +- if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && +- child_info.si_pid != 0) +- { +- /* waitid() helpfully provides the wait status in a decomposed +- * form which is quite useful. Unfortunately we have to report it +- * to the #GChildWatchFunc as a waitpid()-style platform-specific +- * wait status, so that the user code in #GChildWatchFunc can then +- * call WIFEXITED() (etc.) on it. That means re-composing the +- * status information. */ +- child_watch_source->child_status = siginfo_t_to_wait_status (&child_info); +- child_watch_source->child_exited = TRUE; +- } +- } +- +- return child_exited; +- } +-#endif /* HAVE_PIDFD */ +- +- return g_atomic_int_get (&child_watch_source->child_exited); +-} +- + static gboolean + g_unix_signal_watch_prepare (GSource *source, + gint *timeout) +@@ -5648,24 +5658,6 @@ g_unix_signal_watch_finalize (GSource *source) + G_UNLOCK (unix_signal_lock); + } + +-static void +-g_child_watch_finalize (GSource *source) +-{ +- GChildWatchSource *child_watch_source = (GChildWatchSource *) source; +- +- if (child_watch_source->using_pidfd) +- { +- if (child_watch_source->poll.fd >= 0) +- close (child_watch_source->poll.fd); +- return; +- } +- +- G_LOCK (unix_signal_lock); +- unix_child_watches = g_slist_remove (unix_child_watches, source); +- unref_unix_signal_handler_unlocked (SIGCHLD); +- G_UNLOCK (unix_signal_lock); +-} +- + #endif /* G_OS_WIN32 */ + + static gboolean +-- +2.41.0 + + +From 984e04a77b29c60f66fd60b1f9690e6ff7880574 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 28 Mar 2023 15:44:30 +0200 +Subject: [PATCH 2/7] gmain: simplify handling child watchers in + dispatch_unix_signals_unlocked() + +- if a child watch source has "using_pidfd", it is never linked in the + unix_child_watches list. Drop that check. +- replace the deep nested if, with an early "continue" in the loop, + if we detect there is nothing to do. It makes the code easier to + read. +--- + glib/gmain.c | 39 +++++++++++++++++++-------------------- + 1 file changed, 19 insertions(+), 20 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index 228737efa..8a030a409 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -5430,31 +5430,30 @@ dispatch_unix_signals_unlocked (void) + for (node = unix_child_watches; node; node = node->next) + { + GChildWatchSource *source = node->data; ++ pid_t pid; + +- if (!source->using_pidfd && +- !g_atomic_int_get (&source->child_exited)) ++ if (g_atomic_int_get (&source->child_exited)) ++ continue; ++ ++ do + { +- pid_t pid; +- do +- { +- g_assert (source->pid > 0); ++ g_assert (source->pid > 0); + +- pid = waitpid (source->pid, &source->child_status, WNOHANG); +- if (pid > 0) +- { +- g_atomic_int_set (&source->child_exited, TRUE); +- wake_source ((GSource *) source); +- } +- else if (pid == -1 && errno == ECHILD) +- { +- g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes."); +- source->child_status = 0; +- g_atomic_int_set (&source->child_exited, TRUE); +- wake_source ((GSource *) source); +- } ++ pid = waitpid (source->pid, &source->child_status, WNOHANG); ++ if (pid > 0) ++ { ++ g_atomic_int_set (&source->child_exited, TRUE); ++ wake_source ((GSource *) source); ++ } ++ else if (pid == -1 && errno == ECHILD) ++ { ++ g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes."); ++ source->child_status = 0; ++ g_atomic_int_set (&source->child_exited, TRUE); ++ wake_source ((GSource *) source); + } +- while (pid == -1 && errno == EINTR); + } ++ while (pid == -1 && errno == EINTR); + } + } + +-- +2.41.0 + + +From aaac91a86288e103f7f413b9d075acc803178da9 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 17 May 2023 08:04:02 +0200 +Subject: [PATCH 3/7] gmain: remove unnecessary initialization of + source_timeout in g_main_context_prepare_unlocked() + +Note that the variable source_timeout is already initialized upon +definition, at the beginning of the block. + +It's easy to see, that no code changes the variable between the variable +definition, and the place where it was initialized. It was thus +unnecessary. + +It's not about dropping the unnecessary code (the compiler could do that +just fine too). It's that there is the other branch of the "if/else", where +the variable is also not initialized. But the other branch also requires +that the variable is in fact initialized to -1, because prepare() +callbacks are free not to explicitly set the output value. So both +branches require the variable to be initialized to -1, but only one of +them did. This poses unnecessary questions about whether anything is +wrong. Avoid that by dropping the redundant code. +--- + glib/gmain.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index 8a030a409..28fbcc015 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -3695,10 +3695,7 @@ g_main_context_prepare (GMainContext *context, + context->in_check_or_prepare--; + } + else +- { +- source_timeout = -1; +- result = FALSE; +- } ++ result = FALSE; + + if (result == FALSE && source->priv->ready_time != -1) + { +-- +2.41.0 + + +From b71ae65f14843cc09819e5b482667e5c941a27af Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 17 May 2023 08:15:16 +0200 +Subject: [PATCH 4/7] gmain: remove unnecessary initialization of *timeout in + prepare() callbacks + +Note that the prepare callback only has one caller, which pre-initializes +the timeout argument to -1. That may be an implementation detail and not +publicly promised, but it wouldn't make sense to do it any other way in +the caller. + +Also, note that g_unix_signal_watch_prepare() and the UNIX branch of +g_child_watch_prepare() already relied on that. +--- + gio/gsocket.c | 2 -- + glib/giounix.c | 2 -- + glib/giowin32.c | 2 -- + glib/gmain.c | 1 - + glib/tests/mainloop.c | 2 ++ + 5 files changed, 2 insertions(+), 7 deletions(-) + +diff --git a/gio/gsocket.c b/gio/gsocket.c +index f39a568b3..c624eb1ae 100644 +--- a/gio/gsocket.c ++++ b/gio/gsocket.c +@@ -3955,8 +3955,6 @@ socket_source_prepare (GSource *source, + { + GSocketSource *socket_source = (GSocketSource *)source; + +- *timeout = -1; +- + #ifdef G_OS_WIN32 + if ((socket_source->pollfd.revents & G_IO_NVAL) != 0) + return TRUE; +diff --git a/glib/giounix.c b/glib/giounix.c +index b86d79db7..94b33253f 100644 +--- a/glib/giounix.c ++++ b/glib/giounix.c +@@ -129,8 +129,6 @@ g_io_unix_prepare (GSource *source, + GIOUnixWatch *watch = (GIOUnixWatch *)source; + GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel); + +- *timeout = -1; +- + /* Only return TRUE here if _all_ bits in watch->condition will be set + */ + return ((watch->condition & buffer_condition) == watch->condition); +diff --git a/glib/giowin32.c b/glib/giowin32.c +index b0b6c3d85..e4b171b0d 100644 +--- a/glib/giowin32.c ++++ b/glib/giowin32.c +@@ -707,8 +707,6 @@ g_io_win32_prepare (GSource *source, + GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel; + int event_mask; + +- *timeout = -1; +- + if (channel->debug) + g_print ("g_io_win32_prepare: source=%p channel=%p", source, channel); + +diff --git a/glib/gmain.c b/glib/gmain.c +index 28fbcc015..aec04314c 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -5245,7 +5245,6 @@ g_child_watch_prepare (GSource *source, + gint *timeout) + { + #ifdef G_OS_WIN32 +- *timeout = -1; + return FALSE; + #else /* G_OS_WIN32 */ + { +diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c +index d43b2cf08..a7c5b33d1 100644 +--- a/glib/tests/mainloop.c ++++ b/glib/tests/mainloop.c +@@ -34,6 +34,8 @@ cb (gpointer data) + static gboolean + prepare (GSource *source, gint *time) + { ++ g_assert_nonnull (time); ++ g_assert_cmpint (*time, ==, -1); + return FALSE; + } + static gboolean +-- +2.41.0 + + +From ccbebd3bd2d9163da3e2ad7f213ded04cd92136d Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 28 Mar 2023 14:30:58 +0200 +Subject: [PATCH 5/7] gmain: fix race with waitpid() and child watcher sources + +GChildWatchSource uses waitpid(), among pidfd and GetExitCodeProcess(). +It thus only works for child processes which the user must ensure to +exist and not being reaped yet. Also, the user must not kill() the PID +after the child process is reaped and must not race kill() against +waitpid(). Also, the user must not call waitpid()/kill() after the child +process is reaped. + +Previously, GChildWatchSource would call waitpid() already when adding +the source (g_child_watch_source_new()) and from the worker thread +(dispatch_unix_signals_unlocked()). That is racy: + +- if a child watcher is attached and did not yet fire, you cannot call + kill() on the PID without racing against the PID being reaped on the + worker thread. That would then lead to ESRCH or even worse, killing + the wrong process. + +- if you g_source_destroy() the source that didn't fire yet, the user + doesn't know whether the PID was reaped in the background. Any + subsequent kill()/waitpid() may fail with ESRCH/ECHILD or even address + the wrong process. + +The race is most visible on Unix without pidfd support, because then the +process gets reaped on the worker thread or during g_child_watch_source_new(). +But it's also with Windows and pidfd, because we would have waited for +the process in g_child_watch_check(), where other callbacks could fire +between reaping the process status and emitting the source's callback. + +Fix all that by calling waitpid() right before dispatching the callback. +--- + glib/gmain.c | 209 +++++++++++++++++++++++++++------------------- + glib/tests/unix.c | 93 +++++++++++++++++++++ + 2 files changed, 218 insertions(+), 84 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index aec04314c..d1ab421fb 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -350,12 +350,11 @@ struct _GChildWatchSource + { + GSource source; + GPid pid; +- gint child_status; + /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */ + GPollFD poll; + #ifndef G_OS_WIN32 +- gboolean child_exited; /* (atomic); not used iff @using_pidfd is set */ +- gboolean using_pidfd; ++ gboolean child_maybe_exited; /* (atomic) */ ++ gboolean using_pidfd; + #endif /* G_OS_WIN32 */ + }; + +@@ -5238,7 +5237,7 @@ siginfo_t_to_wait_status (const siginfo_t *info) + return W_STOPCODE (info->si_status); + } + } +-#endif /* HAVE_PIDFD */ ++#endif /* HAVE_PIDFD */ + + static gboolean + g_child_watch_prepare (GSource *source, +@@ -5246,19 +5245,19 @@ g_child_watch_prepare (GSource *source, + { + #ifdef G_OS_WIN32 + return FALSE; +-#else /* G_OS_WIN32 */ ++#else /* G_OS_WIN32 */ + { + GChildWatchSource *child_watch_source; + + child_watch_source = (GChildWatchSource *) source; + +- return g_atomic_int_get (&child_watch_source->child_exited); ++ return !child_watch_source->using_pidfd && g_atomic_int_get (&child_watch_source->child_maybe_exited); + } + #endif /* G_OS_WIN32 */ + } + + static gboolean +-g_child_watch_check (GSource *source) ++g_child_watch_check (GSource *source) + { + GChildWatchSource *child_watch_source; + gboolean child_exited; +@@ -5267,57 +5266,15 @@ g_child_watch_check (GSource *source) + + #ifdef G_OS_WIN32 + child_exited = child_watch_source->poll.revents & G_IO_IN; +- +- if (child_exited) +- { +- DWORD child_status; +- +- /* +- * Note: We do _not_ check for the special value of STILL_ACTIVE +- * since we know that the process has exited and doing so runs into +- * problems if the child process "happens to return STILL_ACTIVE(259)" +- * as Microsoft's Platform SDK puts it. +- */ +- if (!GetExitCodeProcess (child_watch_source->pid, &child_status)) +- { +- gchar *emsg = g_win32_error_message (GetLastError ()); +- g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg); +- g_free (emsg); +- +- child_watch_source->child_status = -1; +- } +- else +- child_watch_source->child_status = child_status; +- } + #else /* G_OS_WIN32 */ + #ifdef HAVE_PIDFD + if (child_watch_source->using_pidfd) + { + child_exited = child_watch_source->poll.revents & G_IO_IN; +- +- if (child_exited) +- { +- siginfo_t child_info = { 0, }; +- +- /* Get the exit status */ +- if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && +- child_info.si_pid != 0) +- { +- /* waitid() helpfully provides the wait status in a decomposed +- * form which is quite useful. Unfortunately we have to report it +- * to the #GChildWatchFunc as a waitpid()-style platform-specific +- * wait status, so that the user code in #GChildWatchFunc can then +- * call WIFEXITED() (etc.) on it. That means re-composing the +- * status information. */ +- child_watch_source->child_status = siginfo_t_to_wait_status (&child_info); +- child_watch_source->child_exited = TRUE; +- } +- } +- + return child_exited; + } +-#endif /* HAVE_PIDFD */ +- child_exited = g_atomic_int_get (&child_watch_source->child_exited); ++#endif /* HAVE_PIDFD */ ++ child_exited = g_atomic_int_get (&child_watch_source->child_maybe_exited); + #endif /* G_OS_WIN32 */ + + return child_exited; +@@ -5426,30 +5383,9 @@ dispatch_unix_signals_unlocked (void) + for (node = unix_child_watches; node; node = node->next) + { + GChildWatchSource *source = node->data; +- pid_t pid; +- +- if (g_atomic_int_get (&source->child_exited)) +- continue; + +- do +- { +- g_assert (source->pid > 0); +- +- pid = waitpid (source->pid, &source->child_status, WNOHANG); +- if (pid > 0) +- { +- g_atomic_int_set (&source->child_exited, TRUE); +- wake_source ((GSource *) source); +- } +- else if (pid == -1 && errno == ECHILD) +- { +- g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes."); +- source->child_status = 0; +- g_atomic_int_set (&source->child_exited, TRUE); +- wake_source ((GSource *) source); +- } +- } +- while (pid == -1 && errno == EINTR); ++ if (g_atomic_int_compare_and_exchange (&source->child_maybe_exited, FALSE, TRUE)) ++ wake_source ((GSource *) source); + } + } + +@@ -5662,9 +5598,106 @@ g_child_watch_dispatch (GSource *source, + { + GChildWatchSource *child_watch_source; + GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback; ++ int wait_status; + + child_watch_source = (GChildWatchSource *) source; + ++ /* We only (try to) reap the child process right before dispatching the callback. ++ * That way, the caller can rely that the process is there until the callback ++ * is invoked; or, if the caller calls g_source_destroy() without the callback ++ * being dispatched, the process is still not reaped. */ ++ ++#ifdef G_OS_WIN32 ++ { ++ DWORD child_status; ++ ++ /* ++ * Note: We do _not_ check for the special value of STILL_ACTIVE ++ * since we know that the process has exited and doing so runs into ++ * problems if the child process "happens to return STILL_ACTIVE(259)" ++ * as Microsoft's Platform SDK puts it. ++ */ ++ if (!GetExitCodeProcess (child_watch_source->pid, &child_status)) ++ { ++ gchar *emsg = g_win32_error_message (GetLastError ()); ++ g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg); ++ g_free (emsg); ++ ++ /* Unknown error. We got signaled that the process might be exited, ++ * but now we failed to reap it? Assume the process is gone and proceed. */ ++ wait_status = -1; ++ } ++ else ++ wait_status = child_status; ++ } ++#else /* G_OS_WIN32 */ ++ { ++ gboolean child_exited = FALSE; ++ ++ wait_status = -1; ++ ++#ifdef HAVE_PIDFD ++ if (child_watch_source->using_pidfd) ++ { ++ siginfo_t child_info = { ++ 0, ++ }; ++ ++ /* Get the exit status */ ++ if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && ++ child_info.si_pid != 0) ++ { ++ /* waitid() helpfully provides the wait status in a decomposed ++ * form which is quite useful. Unfortunately we have to report it ++ * to the #GChildWatchFunc as a waitpid()-style platform-specific ++ * wait status, so that the user code in #GChildWatchFunc can then ++ * call WIFEXITED() (etc.) on it. That means re-composing the ++ * status information. */ ++ wait_status = siginfo_t_to_wait_status (&child_info); ++ } ++ else ++ { ++ /* Unknown error. We got signaled that the process might be exited, ++ * but now we failed to reap it? Assume the process is gone and proceed. */ ++ g_warning (G_STRLOC ": pidfd signaled ready but failed"); ++ } ++ child_exited = TRUE; ++ } ++#endif /* HAVE_PIDFD*/ ++ ++ if (!child_exited) ++ { ++ pid_t pid; ++ int wstatus; ++ ++ waitpid_again: ++ ++ /* We must reset the flag before waitpid(). Otherwise, there would be a ++ * race. */ ++ g_atomic_int_set (&child_watch_source->child_maybe_exited, FALSE); ++ ++ pid = waitpid (child_watch_source->pid, &wstatus, WNOHANG); ++ ++ if (pid == 0) ++ { ++ /* Not exited yet. Wait longer. */ ++ return TRUE; ++ } ++ ++ if (pid > 0) ++ wait_status = wstatus; ++ else if (errno == ECHILD) ++ g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes."); ++ else if (errno == EINTR) ++ goto waitpid_again; ++ else ++ { ++ /* Unexpected error. Whatever happened, we are done waiting for this child. */ ++ } ++ } ++ } ++#endif /* G_OS_WIN32 */ ++ + if (!callback) + { + g_warning ("Child watch source dispatched without callback. " +@@ -5672,7 +5705,7 @@ g_child_watch_dispatch (GSource *source, + return FALSE; + } + +- (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data); ++ (child_watch_callback) (child_watch_source->pid, wait_status, user_data); + + /* We never keep a child watch source around as the child is gone */ + return FALSE; +@@ -5731,6 +5764,14 @@ g_unix_signal_handler (int signum) + * mechanism, including `waitpid(pid, ...)` or a second child-watch + * source for the same @pid + * * the application must not ignore `SIGCHLD` ++ * * Before 2.78, the application could not send a signal (`kill()`) to the ++ * watched @pid in a race free manner. Since 2.78, you can do that while the ++ * associated #GMainContext is acquired. ++ * * Before 2.78, even after destroying the #GSource, you could not ++ * be sure that @pid wasn't already reaped. Hence, it was also not ++ * safe to `kill()` or `waitpid()` on the process ID after the child watch ++ * source was gone. Destroying the source before it fired made it ++ * impossible to reliably reap the process. + * + * If any of those conditions are not met, this and related APIs will + * not work correctly. This can often be diagnosed via a GLib warning +@@ -5791,19 +5832,19 @@ g_child_watch_source_new (GPid pid) + + return source; + } +- else +- { +- g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s", +- pid, g_strerror (errsv)); +- /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */ +- } +-#endif /* HAVE_PIDFD */ ++ ++ g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s", ++ pid, g_strerror (errsv)); ++ /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */ ++#endif /* HAVE_PIDFD */ ++ ++ /* We can do that without atomic, as the source is not yet added in ++ * unix_child_watches (which we do next under a lock). */ ++ child_watch_source->child_maybe_exited = TRUE; + + G_LOCK (unix_signal_lock); + ref_unix_signal_handler_unlocked (SIGCHLD); + unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source); +- if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0) +- child_watch_source->child_exited = TRUE; + G_UNLOCK (unix_signal_lock); + #endif /* !G_OS_WIN32 */ + +diff --git a/glib/tests/unix.c b/glib/tests/unix.c +index 7639d066a..6f40ff893 100644 +--- a/glib/tests/unix.c ++++ b/glib/tests/unix.c +@@ -330,6 +330,99 @@ test_get_passwd_entry_nonexistent (void) + g_clear_error (&local_error); + } + ++static void ++_child_wait_watch_cb (GPid pid, ++ gint wait_status, ++ gpointer user_data) ++{ ++ gboolean *p_got_callback = user_data; ++ ++ g_assert_nonnull (p_got_callback); ++ g_assert_false (*p_got_callback); ++ *p_got_callback = TRUE; ++} ++ ++static void ++test_child_wait (void) ++{ ++ gboolean r; ++ GPid pid; ++ guint id; ++ pid_t pid2; ++ int wstatus; ++ gboolean got_callback = FALSE; ++ gboolean iterate_maincontext = g_test_rand_bit (); ++ char **argv; ++ int errsv; ++ ++ /* - We spawn a trivial child process that exits after a short time. ++ * - We schedule a g_child_watch_add() ++ * - we may iterate the GMainContext a bit. Randomly we either get the ++ * child-watcher callback or not. ++ * - if we didn't get the callback, we g_source_remove() the child watcher. ++ * ++ * Afterwards, if the callback didn't fire, we check that we are able to waitpid() ++ * on the process ourselves. Of course, if the child watcher notified, the waitpid() ++ * will fail with ECHILD. ++ */ ++ ++ argv = g_test_rand_bit () ? ((char *[]){ "/bin/sleep", "0.05", NULL }) : ((char *[]){ "/bin/true", NULL }); ++ ++ r = g_spawn_async (NULL, ++ argv, ++ NULL, ++ G_SPAWN_DO_NOT_REAP_CHILD, ++ NULL, ++ NULL, ++ &pid, ++ NULL); ++ if (!r) ++ { ++ /* Some odd system without /bin/sleep? Skip the test. */ ++ g_test_skip ("failure to spawn test process in test_child_wait()"); ++ return; ++ } ++ ++ g_assert_cmpint (pid, >=, 1); ++ ++ if (g_test_rand_bit ()) ++ g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10))); ++ ++ id = g_child_watch_add (pid, _child_wait_watch_cb, &got_callback); ++ ++ if (g_test_rand_bit ()) ++ g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10))); ++ ++ if (iterate_maincontext) ++ { ++ gint64 start_usec = g_get_monotonic_time (); ++ gint64 end_usec = start_usec + g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)); ++ ++ while (!got_callback && g_get_monotonic_time () < end_usec) ++ g_main_context_iteration (NULL, FALSE); ++ } ++ ++ if (!got_callback) ++ g_source_remove (id); ++ ++ errno = 0; ++ pid2 = waitpid (pid, &wstatus, 0); ++ errsv = errno; ++ if (got_callback) ++ { ++ g_assert_true (iterate_maincontext); ++ g_assert_cmpint (errsv, ==, ECHILD); ++ g_assert_cmpint (pid2, <, 0); ++ } ++ else ++ { ++ g_assert_cmpint (errsv, ==, 0); ++ g_assert_cmpint (pid2, ==, pid); ++ g_assert_true (WIFEXITED (wstatus)); ++ g_assert_cmpint (WEXITSTATUS (wstatus), ==, 0); ++ } ++} ++ + int + main (int argc, + char *argv[]) +-- +2.41.0 + + +From ad18d2dad0b7105a379e1b2feae653bf30418397 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Tue, 28 Mar 2023 19:53:02 +0200 +Subject: [PATCH 6/7] gmain: drop redundant using_pidfd field from + GChildWatchSource + +It's redundant, which leads to impossible code like: + + if (child_watch_source->using_pidfd) + { + if (child_watch_source->poll.fd >= 0) + close (child_watch_source->poll.fd); +--- + glib/gmain.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index d1ab421fb..3813b032a 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -350,11 +350,11 @@ struct _GChildWatchSource + { + GSource source; + GPid pid; +- /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */ ++ /* @poll is always used on Windows. ++ * On Unix, poll.fd will be negative if PIDFD is unavailable. */ + GPollFD poll; + #ifndef G_OS_WIN32 + gboolean child_maybe_exited; /* (atomic) */ +- gboolean using_pidfd; + #endif /* G_OS_WIN32 */ + }; + +@@ -5251,7 +5251,10 @@ g_child_watch_prepare (GSource *source, + + child_watch_source = (GChildWatchSource *) source; + +- return !child_watch_source->using_pidfd && g_atomic_int_get (&child_watch_source->child_maybe_exited); ++ if (child_watch_source->poll.fd >= 0) ++ return FALSE; ++ ++ return g_atomic_int_get (&child_watch_source->child_maybe_exited); + } + #endif /* G_OS_WIN32 */ + } +@@ -5268,7 +5271,7 @@ g_child_watch_check (GSource *source) + child_exited = child_watch_source->poll.revents & G_IO_IN; + #else /* G_OS_WIN32 */ + #ifdef HAVE_PIDFD +- if (child_watch_source->using_pidfd) ++ if (child_watch_source->poll.fd >= 0) + { + child_exited = child_watch_source->poll.revents & G_IO_IN; + return child_exited; +@@ -5286,10 +5289,9 @@ g_child_watch_finalize (GSource *source) + #ifndef G_OS_WIN32 + GChildWatchSource *child_watch_source = (GChildWatchSource *) source; + +- if (child_watch_source->using_pidfd) ++ if (child_watch_source->poll.fd >= 0) + { +- if (child_watch_source->poll.fd >= 0) +- close (child_watch_source->poll.fd); ++ close (child_watch_source->poll.fd); + return; + } + +@@ -5637,7 +5639,7 @@ g_child_watch_dispatch (GSource *source, + wait_status = -1; + + #ifdef HAVE_PIDFD +- if (child_watch_source->using_pidfd) ++ if (child_watch_source->poll.fd >= 0) + { + siginfo_t child_info = { + 0, +@@ -5822,17 +5824,15 @@ g_child_watch_source_new (GPid pid) + * better than SIGCHLD. + */ + child_watch_source->poll.fd = (int) syscall (SYS_pidfd_open, pid, 0); +- errsv = errno; + + if (child_watch_source->poll.fd >= 0) + { +- child_watch_source->using_pidfd = TRUE; + child_watch_source->poll.events = G_IO_IN; + g_source_add_poll (source, &child_watch_source->poll); +- + return source; + } + ++ errsv = errno; + g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s", + pid, g_strerror (errsv)); + /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */ +@@ -5841,6 +5841,7 @@ g_child_watch_source_new (GPid pid) + /* We can do that without atomic, as the source is not yet added in + * unix_child_watches (which we do next under a lock). */ + child_watch_source->child_maybe_exited = TRUE; ++ child_watch_source->poll.fd = -1; + + G_LOCK (unix_signal_lock); + ref_unix_signal_handler_unlocked (SIGCHLD); +-- +2.41.0 + + +From bbdcc6e72ac97e577486aeb1baad0060ad8e1cd6 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Wed, 29 Mar 2023 07:51:26 +0200 +Subject: [PATCH 7/7] gmain: ensure boolean value in g_child_watch_check() is + strictly 0 or 1 + +No problem in practice, but it seems nice to ensure that a gboolean is +always either FALSE or TRUE. +--- + glib/gmain.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/glib/gmain.c b/glib/gmain.c +index 3813b032a..b6ffc6dd0 100644 +--- a/glib/gmain.c ++++ b/glib/gmain.c +@@ -5268,12 +5268,12 @@ g_child_watch_check (GSource *source) + child_watch_source = (GChildWatchSource *) source; + + #ifdef G_OS_WIN32 +- child_exited = child_watch_source->poll.revents & G_IO_IN; ++ child_exited = !!(child_watch_source->poll.revents & G_IO_IN); + #else /* G_OS_WIN32 */ + #ifdef HAVE_PIDFD + if (child_watch_source->poll.fd >= 0) + { +- child_exited = child_watch_source->poll.revents & G_IO_IN; ++ child_exited = !!(child_watch_source->poll.revents & G_IO_IN); + return child_exited; + } + #endif /* HAVE_PIDFD */ +-- +2.41.0 + diff --git a/base/rx/rx-glib2/3845.patch b/base/rx/rx-glib2/3845.patch new file mode 100644 index 0000000..8cd6b01 --- /dev/null +++ b/base/rx/rx-glib2/3845.patch @@ -0,0 +1,195 @@ +From 37e323f1d16720d662611866cde567b1d2a01d48 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Mon, 22 Jan 2024 15:29:37 +0100 +Subject: [PATCH 1/2] gunixmounts: Use libmnt_monitor API for monitoring + +The `GUnixMountMonitor` object implements monitoring on its own currently. +Only the `/proc/mounts` file changes are monitored. It is not aware of the +`/run/mount/utab` file changes. This file contains the userspace mount +options (e.g. `x-gvfs-notrash`, `x-gvfs-hide`) among others. There is a +problem when `/sbin/mount.` (e.g. `mount.nfs`) helper programs are +used. In that case, the `/run/mount/utab` file is updated later than the +`/proc/mounts` file and thus the `GUnixMountMonitor` clients (e.g. +`gvfs-udisks2-volume-monitor`, `gvfsd-trash`) don't see the userspace +options until the next `mount-changed` signal. Let's use the `libmnt_monitor` +API for monitoring instead and emit the `mount-changed` signal also when the +`/run/mount/utab` file is changed. + +Related: https://issues.redhat.com/browse/RHEL-14607 +Related: https://github.com/util-linux/util-linux/pull/2607 +--- + gio/gunixmounts.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 71 insertions(+), 1 deletion(-) + +diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c +index 32b936259..e11b34a7d 100644 +--- a/gio/gunixmounts.c ++++ b/gio/gunixmounts.c +@@ -202,6 +202,11 @@ static GSource *proc_mounts_watch_source; + #define endmntent(f) fclose(f) + #endif + ++#ifdef HAVE_LIBMOUNT ++/* Protected by proc_mounts_source lock */ ++static struct libmnt_monitor *proc_mounts_monitor = NULL; ++#endif ++ + static gboolean + is_in (const char *value, const char *set[]) + { +@@ -1859,7 +1864,36 @@ proc_mounts_changed (GIOChannel *channel, + GIOCondition cond, + gpointer user_data) + { ++ gboolean has_changed = FALSE; ++ ++#ifdef HAVE_LIBMOUNT ++ if (cond & G_IO_IN) ++ { ++ G_LOCK (proc_mounts_source); ++ if (proc_mounts_monitor != NULL) ++ { ++ int ret; ++ ++ /* The mnt_monitor_next_change function needs to be used to avoid false-positives. */ ++ ret = mnt_monitor_next_change (proc_mounts_monitor, NULL, NULL); ++ if (ret == 0) ++ { ++ has_changed = TRUE; ++ ret = mnt_monitor_event_cleanup (proc_mounts_monitor); ++ } ++ ++ if (ret < 0) ++ g_debug ("mnt_monitor_next_change failed: %s", g_strerror (-ret)); ++ } ++ G_UNLOCK (proc_mounts_source); ++ } ++ ++#else + if (cond & G_IO_ERR) ++ has_changed = TRUE; ++#endif ++ ++ if (has_changed) + { + G_LOCK (proc_mounts_source); + mount_poller_time = (guint64) g_get_monotonic_time (); +@@ -1924,6 +1958,10 @@ mount_monitor_stop (void) + g_source_destroy (proc_mounts_watch_source); + proc_mounts_watch_source = NULL; + } ++ ++#ifdef HAVE_LIBMOUNT ++ g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor); ++#endif + G_UNLOCK (proc_mounts_source); + + if (mtab_monitor) +@@ -1965,9 +2003,37 @@ mount_monitor_start (void) + */ + if (g_str_has_prefix (mtab_path, "/proc/")) + { +- GIOChannel *proc_mounts_channel; ++ GIOChannel *proc_mounts_channel = NULL; + GError *error = NULL; ++#ifdef HAVE_LIBMOUNT ++ int ret; ++ ++ G_LOCK (proc_mounts_source); ++ ++ proc_mounts_monitor = mnt_new_monitor (); ++ ret = mnt_monitor_enable_kernel (proc_mounts_monitor, TRUE); ++ if (ret < 0) ++ g_warning ("mnt_monitor_enable_kernel failed: %s", g_strerror (-ret)); ++ ++ ret = mnt_monitor_enable_userspace (proc_mounts_monitor, TRUE, NULL); ++ if (ret < 0) ++ g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret)); ++ ++ ret = mnt_monitor_get_fd (proc_mounts_monitor); ++ if (ret >= 0) ++ { ++ proc_mounts_channel = g_io_channel_unix_new (ret); ++ } ++ else ++ { ++ g_set_error_literal (&error, G_IO_ERROR, g_io_error_from_errno (-ret), ++ g_strerror (-ret)); ++ } ++ ++ G_UNLOCK (proc_mounts_source); ++#else + proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error); ++#endif + if (proc_mounts_channel == NULL) + { + g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path, +@@ -1978,7 +2044,11 @@ mount_monitor_start (void) + { + G_LOCK (proc_mounts_source); + ++#ifdef HAVE_LIBMOUNT ++ proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_IN); ++#else + proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR); ++#endif + mount_poller_time = (guint64) g_get_monotonic_time (); + g_source_set_callback (proc_mounts_watch_source, + (GSourceFunc) proc_mounts_changed, +-- +2.43.0 + + +From bb7d6b8fcef36af5452071c8758f89955888469a Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Wed, 31 Jan 2024 13:35:39 +0100 +Subject: [PATCH 2/2] gunixmounts: Use mnt_monitor_veil_kernel option + +The previous commit enabled the `/run/mount/utab` monitoring. The problem +is that the `mount-changed` signal can be emitted twice for one mount. One +for the `/proc/mounts` file change and another one for the `/run/media/utab` +file change. This is still not ideal because e.g. the `GMount` objects for +mounts with the `x-gvfs-hide` option are added and immediately removed. +Let's enable the `mnt_monitor_veil_kernel` option to avoid this. + +Related: https://github.com/util-linux/util-linux/pull/2725 +--- + gio/gunixmounts.c | 6 ++++++ + meson.build | 4 ++++ + 2 files changed, 10 insertions(+) + +diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c +index e11b34a7d..6abe87414 100644 +--- a/gio/gunixmounts.c ++++ b/gio/gunixmounts.c +@@ -2019,6 +2019,12 @@ mount_monitor_start (void) + if (ret < 0) + g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret)); + ++#ifdef HAVE_MNT_MONITOR_VEIL_KERNEL ++ ret = mnt_monitor_veil_kernel (proc_mounts_monitor, TRUE); ++ if (ret < 0) ++ g_warning ("mnt_monitor_veil_kernel failed: %s", g_strerror (-ret)); ++#endif ++ + ret = mnt_monitor_get_fd (proc_mounts_monitor); + if (ret >= 0) + { +diff --git a/meson.build b/meson.build +index a0502fe69..159703557 100644 +--- a/meson.build ++++ b/meson.build +@@ -2102,6 +2102,10 @@ libmount_dep = [] + if host_system == 'linux' + libmount_dep = dependency('mount', version : '>=2.23', required : get_option('libmount')) + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) ++ ++ if libmount_dep.found() and cc.has_function('mnt_monitor_veil_kernel', dependencies: libmount_dep) ++ glib_conf.set('HAVE_MNT_MONITOR_VEIL_KERNEL', 1) ++ endif + endif + + # gnutls is used optionally by GHmac +-- +2.43.0 + diff --git a/base/rx/rx-glib2/4038.patch b/base/rx/rx-glib2/4038.patch new file mode 100644 index 0000000..bf1ba0f --- /dev/null +++ b/base/rx/rx-glib2/4038.patch @@ -0,0 +1,3359 @@ +From 82f62517eb327aa3ea255294a90911091f39d7ae Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Tue, 28 Nov 2023 12:58:20 +0000 +Subject: [PATCH 01/19] gdbusmessage: Cache the arg0 value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Technically we can’t rely on it being kept alive by the `message->body` +pointer, unless we can guarantee that the `GVariant` is always +serialised. That’s not necessarily the case, so keep a separate ref on +the arg0 value at all times. + +This avoids a potential use-after-free. + +Spotted by Thomas Haller in +https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3720#note_1924707. + +[This is a prerequisite for having tests pass after fixing the +vulnerability described in glib#3268, because after fixing that +vulnerability, the use-after-free genuinely does happen during +regression testing. -smcv] + +Signed-off-by: Philip Withnall + +Helps: #3183, #3268 +(cherry picked from commit 10e9a917be7fb92b6b27837ef7a7f1d0be6095d5) +--- + gio/gdbusmessage.c | 35 ++++++++++++++++++++++------------- + 1 file changed, 22 insertions(+), 13 deletions(-) + +diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c +index bc9386ee7..40a77dd92 100644 +--- a/gio/gdbusmessage.c ++++ b/gio/gdbusmessage.c +@@ -471,6 +471,7 @@ struct _GDBusMessage + guint32 serial; + GHashTable *headers; + GVariant *body; ++ GVariant *arg0_cache; /* (nullable) (owned) */ + #ifdef G_OS_UNIX + GUnixFDList *fd_list; + #endif +@@ -493,6 +494,7 @@ g_dbus_message_finalize (GObject *object) + g_hash_table_unref (message->headers); + if (message->body != NULL) + g_variant_unref (message->body); ++ g_clear_pointer (&message->arg0_cache, g_variant_unref); + #ifdef G_OS_UNIX + if (message->fd_list != NULL) + g_object_unref (message->fd_list); +@@ -1128,6 +1130,7 @@ g_dbus_message_set_body (GDBusMessage *message, + if (body == NULL) + { + message->body = NULL; ++ message->arg0_cache = NULL; + g_dbus_message_set_signature (message, NULL); + } + else +@@ -1138,6 +1141,12 @@ g_dbus_message_set_body (GDBusMessage *message, + + message->body = g_variant_ref_sink (body); + ++ if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) && ++ g_variant_n_children (message->body) > 0) ++ message->arg0_cache = g_variant_get_child_value (message->body, 0); ++ else ++ message->arg0_cache = NULL; ++ + type_string = g_variant_get_type_string (body); + type_string_len = strlen (type_string); + g_assert (type_string_len >= 2); +@@ -2230,6 +2239,14 @@ g_dbus_message_new_from_blob (guchar *blob, + 2, + error); + g_variant_type_free (variant_type); ++ ++ if (message->body != NULL && ++ g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) && ++ g_variant_n_children (message->body) > 0) ++ message->arg0_cache = g_variant_get_child_value (message->body, 0); ++ else ++ message->arg0_cache = NULL; ++ + if (message->body == NULL) + goto out; + } +@@ -3265,22 +3282,13 @@ g_dbus_message_set_signature (GDBusMessage *message, + const gchar * + g_dbus_message_get_arg0 (GDBusMessage *message) + { +- const gchar *ret; +- + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + +- ret = NULL; ++ if (message->arg0_cache != NULL && ++ g_variant_is_of_type (message->arg0_cache, G_VARIANT_TYPE_STRING)) ++ return g_variant_get_string (message->arg0_cache, NULL); + +- if (message->body != NULL && g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE)) +- { +- GVariant *item; +- item = g_variant_get_child_value (message->body, 0); +- if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING)) +- ret = g_variant_get_string (item, NULL); +- g_variant_unref (item); +- } +- +- return ret; ++ return NULL; + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -3723,6 +3731,7 @@ g_dbus_message_copy (GDBusMessage *message, + * to just ref (as opposed to deep-copying) the GVariant instances + */ + ret->body = message->body != NULL ? g_variant_ref (message->body) : NULL; ++ ret->arg0_cache = message->arg0_cache != NULL ? g_variant_ref (message->arg0_cache) : NULL; + g_hash_table_iter_init (&iter, message->headers); + while (g_hash_table_iter_next (&iter, &header_key, (gpointer) &header_value)) + g_hash_table_insert (ret->headers, header_key, g_variant_ref (header_value)); +-- +2.45.0 + + +From 3649ce253433149dea16d158542e7285655eb182 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Wed, 1 May 2024 15:51:42 +0100 +Subject: [PATCH 02/19] gdbusconnection: Make a backport of g_set_str() + available + +A subsequent commit will need this. Copying all of g_set_str() into a +private header seems cleaner than replacing the call to it. + +Helps: GNOME/glib#3268 +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 1 + + glib/glib-private.h | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index a37611275..2808b1e46 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -95,6 +95,7 @@ + #include + #include + ++#include "glib-private.h" + #include "gdbusauth.h" + #include "gdbusutils.h" + #include "gdbusaddress.h" +diff --git a/glib/glib-private.h b/glib/glib-private.h +index 8de380d12..acdfa4911 100644 +--- a/glib/glib-private.h ++++ b/glib/glib-private.h +@@ -161,4 +161,22 @@ GLibPrivateVTable *glib__private__ (void); + # define GLIB_DEFAULT_LOCALE "" + #endif + ++/* Backported from GLib 2.78.x, where it is public API in gstrfuncs.h */ ++static inline gboolean ++g_set_str (char **str_pointer, ++ const char *new_str) ++{ ++ char *copy; ++ ++ if (*str_pointer == new_str || ++ (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0)) ++ return FALSE; ++ ++ copy = g_strdup (new_str); ++ g_free (*str_pointer); ++ *str_pointer = copy; ++ ++ return TRUE; ++} ++ + #endif /* __GLIB_PRIVATE_H__ */ +-- +2.45.0 + + +From cef2cd7a033d3df50a8b5b3db28df93feb1d14a9 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 14:19:46 +0000 +Subject: [PATCH 03/19] tests: Add a data-driven test for signal subscriptions + +This somewhat duplicates test_connection_signals(), but is easier to +extend to cover different scenarios. + +Each scenario is tested three times: once with lower-level +GDBusConnection APIs, once with the higher-level GDBusProxy (which +cannot implement all of the subscription scenarios, so some message +counts are lower), and once with both (to check that delivery of the +same message to multiple destinations is handled appropriately). + +[Backported to glib-2-74, resolving conflicts in gio/tests/meson.build] +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 938 ++++++++++++++++++++++++++++++++++++ + gio/tests/meson.build | 1 + + 2 files changed, 939 insertions(+) + create mode 100644 gio/tests/gdbus-subscribe.c + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +new file mode 100644 +index 000000000..3f53e1d7f +--- /dev/null ++++ b/gio/tests/gdbus-subscribe.c +@@ -0,0 +1,938 @@ ++/* ++ * Copyright 2024 Collabora Ltd. ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ */ ++ ++#include ++ ++#include "gdbus-tests.h" ++ ++#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" ++#define DBUS_PATH_DBUS "/org/freedesktop/DBus" ++#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS ++ ++/* A signal that each connection emits to indicate that it has finished ++ * emitting other signals */ ++#define FINISHED_PATH "/org/gtk/Test/Finished" ++#define FINISHED_INTERFACE "org.gtk.Test.Finished" ++#define FINISHED_SIGNAL "Finished" ++ ++/* A signal emitted during testing */ ++#define EXAMPLE_PATH "/org/gtk/GDBus/ExampleInterface" ++#define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface" ++#define FOO_SIGNAL "Foo" ++ ++/* Log @s in a debug message. */ ++static inline const char * ++nonnull (const char *s, ++ const char *if_null) ++{ ++ return (s == NULL) ? if_null : s; ++} ++ ++typedef enum ++{ ++ TEST_CONN_NONE, ++ TEST_CONN_FIRST, ++ /* A connection that subscribes to signals */ ++ TEST_CONN_SUBSCRIBER = TEST_CONN_FIRST, ++ /* A mockup of a legitimate service */ ++ TEST_CONN_SERVICE, ++ /* A mockup of a second legitimate service */ ++ TEST_CONN_SERVICE2, ++ /* A connection that tries to trick @subscriber into processing its signals ++ * as if they came from @service */ ++ TEST_CONN_ATTACKER, ++ NUM_TEST_CONNS ++} TestConn; ++ ++static const char * const test_conn_descriptions[NUM_TEST_CONNS] = ++{ ++ "(unused)", ++ "subscriber", ++ "service", ++ "service 2", ++ "attacker" ++}; ++ ++typedef enum ++{ ++ SUBSCRIPTION_MODE_CONN, ++ SUBSCRIPTION_MODE_PROXY, ++ SUBSCRIPTION_MODE_PARALLEL ++} SubscriptionMode; ++ ++typedef struct ++{ ++ GDBusProxy *received_by_proxy; ++ TestConn sender; ++ char *path; ++ char *iface; ++ char *member; ++ GVariant *parameters; ++ char *arg0; ++ guint32 step; ++} ReceivedMessage; ++ ++static void ++received_message_free (ReceivedMessage *self) ++{ ++ ++ g_clear_object (&self->received_by_proxy); ++ g_free (self->path); ++ g_free (self->iface); ++ g_free (self->member); ++ g_clear_pointer (&self->parameters, g_variant_unref); ++ g_free (self->arg0); ++ g_free (self); ++} ++ ++typedef struct ++{ ++ TestConn sender; ++ TestConn unicast_to; ++ const char *path; ++ const char *iface; ++ const char *member; ++ const char *arg0; ++ guint received_by_conn; ++ guint received_by_proxy; ++} TestEmitSignal; ++ ++typedef struct ++{ ++ TestConn sender; ++ const char *path; ++ const char *iface; ++ const char *member; ++ const char *arg0; ++ GDBusSignalFlags flags; ++} TestSubscribe; ++ ++typedef enum ++{ ++ TEST_ACTION_NONE = 0, ++ TEST_ACTION_SUBSCRIBE, ++ TEST_ACTION_EMIT_SIGNAL, ++} TestAction; ++ ++typedef struct ++{ ++ TestAction action; ++ union { ++ TestEmitSignal signal; ++ TestSubscribe subscribe; ++ } u; ++} TestStep; ++ ++/* Arbitrary, extend as necessary to accommodate the longest test */ ++#define MAX_TEST_STEPS 10 ++ ++typedef struct ++{ ++ const char *description; ++ TestStep steps[MAX_TEST_STEPS]; ++} TestPlan; ++ ++static const TestPlan plan_simple = ++{ ++ .description = "A broadcast is only received after subscribing to it", ++ .steps = { ++ { ++ /* We don't receive a signal if we haven't subscribed yet */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* Now it works */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ /* The proxy can't be used in this case, because it needs ++ * a bus name to subscribe to */ ++ .received_by_proxy = 0 ++ }, ++ }, ++ }, ++}; ++ ++static const TestPlan plan_broadcast_from_anyone = ++{ ++ .description = "A subscription with NULL sender accepts broadcast and unicast", ++ .steps = { ++ { ++ /* Subscriber wants to receive signals from anyone */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* First service sends a broadcast */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Second service also sends a broadcast */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE2, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* First service sends a unicast signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Second service also sends a unicast signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE2, ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 0 ++ }, ++ }, ++ }, ++}; ++ ++static const TestPlan plan_match_twice = ++{ ++ .description = "A message matching more than one subscription is received " ++ "once per subscription", ++ .steps = { ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .path = EXAMPLE_PATH, ++ }, ++ }, ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 4, ++ /* Only the first and last work with GDBusProxy */ ++ .received_by_proxy = 2 ++ }, ++ }, ++ }, ++}; ++ ++static const TestPlan plan_limit_by_unique_name = ++{ ++ .description = "A subscription via a unique name only accepts messages " ++ "sent by that same unique name", ++ .steps = { ++ { ++ /* Subscriber wants to receive signals from service */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* Attacker wants to trick subscriber into thinking that service ++ * sent a signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Attacker tries harder, by sending a signal unicast directly to ++ * the subscriber */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* When the real service sends a signal, it should still get through */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 1 ++ }, ++ }, ++ }, ++}; ++ ++typedef struct ++{ ++ const TestPlan *plan; ++ SubscriptionMode mode; ++ GError *error; ++ /* (element-type ReceivedMessage) */ ++ GPtrArray *received; ++ /* conns[TEST_CONN_NONE] is unused and remains NULL */ ++ GDBusConnection *conns[NUM_TEST_CONNS]; ++ /* Proxies on conns[TEST_CONN_SUBSCRIBER] */ ++ GPtrArray *proxies; ++ /* unique_names[TEST_CONN_NONE] is unused and remains NULL */ ++ const char *unique_names[NUM_TEST_CONNS]; ++ /* finished[TEST_CONN_NONE] is unused and remains FALSE */ ++ gboolean finished[NUM_TEST_CONNS]; ++ /* Remains 0 for any step that is not a subscription */ ++ guint subscriptions[MAX_TEST_STEPS]; ++ /* Number of times the signal from step n was received */ ++ guint received_by_conn[MAX_TEST_STEPS]; ++ /* Number of times the signal from step n was received */ ++ guint received_by_proxy[MAX_TEST_STEPS]; ++ guint finished_subscription; ++} Fixture; ++ ++/* Wait for asynchronous messages from @conn to have been processed ++ * by the message bus, as a sequence point so that we can make ++ * "happens before" and "happens after" assertions relative to this. ++ * The easiest way to achieve this is to call a message bus method that has ++ * no arguments and wait for it to return: because the message bus processes ++ * messages in-order, anything we sent before this must have been processed ++ * by the time this call arrives. */ ++static void ++connection_wait_for_bus (GDBusConnection *conn) ++{ ++ GError *error = NULL; ++ GVariant *call_result; ++ ++ call_result = g_dbus_connection_call_sync (conn, ++ DBUS_SERVICE_DBUS, ++ DBUS_PATH_DBUS, ++ DBUS_INTERFACE_DBUS, ++ "GetId", ++ NULL, /* arguments */ ++ NULL, /* result type */ ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (call_result); ++ g_variant_unref (call_result); ++} ++ ++/* ++ * Called when the subscriber receives a message from any connection ++ * announcing that it has emitted all the signals that it plans to emit. ++ */ ++static void ++subscriber_finished_cb (GDBusConnection *conn, ++ const char *sender_name, ++ const char *path, ++ const char *iface, ++ const char *member, ++ GVariant *parameters, ++ void *user_data) ++{ ++ Fixture *f = user_data; ++ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; ++ guint i; ++ ++ g_assert_true (conn == subscriber); ++ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ { ++ if (g_str_equal (sender_name, f->unique_names[i])) ++ { ++ g_assert_false (f->finished[i]); ++ f->finished[i] = TRUE; ++ ++ g_test_message ("Received Finished signal from %s %s", ++ test_conn_descriptions[i], sender_name); ++ return; ++ } ++ } ++ ++ g_error ("Received Finished signal from unknown sender %s", sender_name); ++} ++ ++/* ++ * Called when we receive a signal, either via the GDBusProxy (proxy != NULL) ++ * or via the GDBusConnection (proxy == NULL). ++ */ ++static void ++fixture_received_signal (Fixture *f, ++ GDBusProxy *proxy, ++ const char *sender_name, ++ const char *path, ++ const char *iface, ++ const char *member, ++ GVariant *parameters) ++{ ++ guint i; ++ ReceivedMessage *received; ++ ++ /* Ignore the Finished signal if it matches a wildcard subscription */ ++ if (g_str_equal (member, FINISHED_SIGNAL)) ++ return; ++ ++ received = g_new0 (ReceivedMessage, 1); ++ ++ if (proxy != NULL) ++ received->received_by_proxy = g_object_ref (proxy); ++ else ++ received->received_by_proxy = NULL; ++ ++ received->path = g_strdup (path); ++ received->iface = g_strdup (iface); ++ received->member = g_strdup (member); ++ received->parameters = g_variant_ref (parameters); ++ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ { ++ if (g_str_equal (sender_name, f->unique_names[i])) ++ { ++ received->sender = i; ++ g_assert_false (f->finished[i]); ++ break; ++ } ++ } ++ ++ g_assert_cmpint (received->sender, !=, TEST_CONN_NONE); ++ ++ g_test_message ("Signal received from %s %s via %s", ++ test_conn_descriptions[received->sender], ++ sender_name, ++ proxy != NULL ? "proxy" : "connection"); ++ g_test_message ("\tPath: %s", path); ++ g_test_message ("\tInterface: %s", iface); ++ g_test_message ("\tMember: %s", member); ++ ++ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(su)"))) ++ { ++ g_variant_get (parameters, "(su)", &received->arg0, &received->step); ++ g_test_message ("\tString argument 0: %s", received->arg0); ++ g_test_message ("\tSent in step: %u", received->step); ++ } ++ else ++ { ++ g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(uu)"); ++ g_variant_get (parameters, "(uu)", NULL, &received->step); ++ g_test_message ("\tArgument 0: (not a string)"); ++ g_test_message ("\tSent in step: %u", received->step); ++ } ++ ++ g_ptr_array_add (f->received, g_steal_pointer (&received)); ++} ++ ++static void ++proxy_signal_cb (GDBusProxy *proxy, ++ const char *sender_name, ++ const char *member, ++ GVariant *parameters, ++ void *user_data) ++{ ++ Fixture *f = user_data; ++ ++ fixture_received_signal (f, proxy, sender_name, ++ g_dbus_proxy_get_object_path (proxy), ++ g_dbus_proxy_get_interface_name (proxy), ++ member, parameters); ++} ++ ++static void ++subscribed_signal_cb (GDBusConnection *conn, ++ const char *sender_name, ++ const char *path, ++ const char *iface, ++ const char *member, ++ GVariant *parameters, ++ void *user_data) ++{ ++ Fixture *f = user_data; ++ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; ++ ++ g_assert_true (conn == subscriber); ++ ++ fixture_received_signal (f, NULL, sender_name, path, iface, member, parameters); ++} ++ ++static void ++fixture_subscribe (Fixture *f, ++ const TestSubscribe *subscribe, ++ guint step_number) ++{ ++ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; ++ const char *sender; ++ ++ if (subscribe->sender != TEST_CONN_NONE) ++ { ++ sender = f->unique_names[subscribe->sender]; ++ g_test_message ("\tSender: %s %s", ++ test_conn_descriptions[subscribe->sender], ++ sender); ++ } ++ else ++ { ++ sender = NULL; ++ g_test_message ("\tSender: (any)"); ++ } ++ ++ g_test_message ("\tPath: %s", nonnull (subscribe->path, "(any)")); ++ g_test_message ("\tInterface: %s", ++ nonnull (subscribe->iface, "(any)")); ++ g_test_message ("\tMember: %s", ++ nonnull (subscribe->member, "(any)")); ++ g_test_message ("\tString argument 0: %s", ++ nonnull (subscribe->arg0, "(any)")); ++ g_test_message ("\tFlags: %x", subscribe->flags); ++ ++ if (f->mode != SUBSCRIPTION_MODE_PROXY) ++ { ++ /* CONN or PARALLEL */ ++ guint id; ++ ++ g_test_message ("\tSubscribing via connection"); ++ id = g_dbus_connection_signal_subscribe (subscriber, ++ sender, ++ subscribe->iface, ++ subscribe->member, ++ subscribe->path, ++ subscribe->arg0, ++ subscribe->flags, ++ subscribed_signal_cb, ++ f, NULL); ++ g_assert_cmpuint (id, !=, 0); ++ f->subscriptions[step_number] = id; ++ } ++ ++ if (f->mode != SUBSCRIPTION_MODE_CONN) ++ { ++ /* PROXY or PARALLEL */ ++ ++ if (sender == NULL) ++ { ++ g_test_message ("\tCannot subscribe via proxy: no bus name"); ++ } ++ else if (subscribe->path == NULL) ++ { ++ g_test_message ("\tCannot subscribe via proxy: no path"); ++ } ++ else if (subscribe->iface == NULL) ++ { ++ g_test_message ("\tCannot subscribe via proxy: no interface"); ++ } ++ else ++ { ++ GDBusProxy *proxy; ++ ++ g_test_message ("\tSubscribing via proxy"); ++ proxy = g_dbus_proxy_new_sync (subscriber, ++ (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES ++ | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), ++ NULL, /* GDBusInterfaceInfo */ ++ sender, ++ subscribe->path, ++ subscribe->iface, ++ NULL, /* GCancellable */ ++ &f->error); ++ g_assert_no_error (f->error); ++ g_assert_nonnull (proxy); ++ g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal_cb), f); ++ g_ptr_array_add (f->proxies, g_steal_pointer (&proxy)); ++ } ++ } ++ ++ /* As in setup(), we need to wait for AddMatch to happen. */ ++ g_test_message ("Waiting for AddMatch to be processed"); ++ connection_wait_for_bus (subscriber); ++} ++ ++static void ++fixture_emit_signal (Fixture *f, ++ const TestEmitSignal *signal, ++ guint step_number) ++{ ++ GVariant *body; ++ const char *destination; ++ gboolean ok; ++ ++ g_test_message ("\tSender: %s", ++ test_conn_descriptions[signal->sender]); ++ ++ if (signal->unicast_to != TEST_CONN_NONE) ++ { ++ destination = f->unique_names[signal->unicast_to]; ++ g_test_message ("\tDestination: %s %s", ++ test_conn_descriptions[signal->unicast_to], ++ destination); ++ } ++ else ++ { ++ destination = NULL; ++ g_test_message ("\tDestination: (broadcast)"); ++ } ++ ++ g_assert_nonnull (signal->path); ++ g_test_message ("\tPath: %s", signal->path); ++ g_assert_nonnull (signal->iface); ++ g_test_message ("\tInterface: %s", signal->iface); ++ g_assert_nonnull (signal->member); ++ g_test_message ("\tMember: %s", signal->member); ++ ++ /* If arg0 is non-NULL, put it in the message's argument 0. ++ * Otherwise put something that will not match any arg0. ++ * Either way, put the sequence number in argument 1 so we can ++ * correlate sent messages with received messages later. */ ++ if (signal->arg0 != NULL) ++ { ++ g_test_message ("\tString argument 0: %s", signal->arg0); ++ /* floating */ ++ body = g_variant_new ("(su)", signal->arg0, (guint32) step_number); ++ } ++ else ++ { ++ g_test_message ("\tArgument 0: (not a string)"); ++ body = g_variant_new ("(uu)", (guint32) 0, (guint32) step_number); ++ } ++ ++ ok = g_dbus_connection_emit_signal (f->conns[signal->sender], ++ destination, ++ signal->path, ++ signal->iface, ++ signal->member, ++ /* steals floating reference */ ++ g_steal_pointer (&body), ++ &f->error); ++ g_assert_no_error (f->error); ++ g_assert_true (ok); ++ ++ /* Emitting the signal is asynchronous, so if we want subsequent steps ++ * to be guaranteed to happen after the signal from the message bus's ++ * perspective, we have to do a round-trip to the message bus to sync up. */ ++ g_test_message ("Waiting for signal to reach message bus"); ++ connection_wait_for_bus (f->conns[signal->sender]); ++} ++ ++static void ++fixture_run_plan (Fixture *f, ++ const TestPlan *plan, ++ SubscriptionMode mode) ++{ ++ guint i; ++ ++ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->subscriptions)); ++ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_conn)); ++ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_proxy)); ++ ++ f->mode = mode; ++ f->plan = plan; ++ ++ g_test_summary (plan->description); ++ ++ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++) ++ { ++ const TestStep *step = &plan->steps[i]; ++ ++ switch (step->action) ++ { ++ case TEST_ACTION_SUBSCRIBE: ++ g_test_message ("Step %u: adding subscription", i); ++ fixture_subscribe (f, &step->u.subscribe, i); ++ break; ++ ++ case TEST_ACTION_EMIT_SIGNAL: ++ g_test_message ("Step %u: emitting signal", i); ++ fixture_emit_signal (f, &step->u.signal, i); ++ break; ++ ++ case TEST_ACTION_NONE: ++ /* Padding to fill the rest of the array, do nothing */ ++ break; ++ ++ default: ++ g_return_if_reached (); ++ } ++ } ++ ++ /* Now that we have done everything we wanted to do, emit Finished ++ * from each connection. */ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ { ++ gboolean ok; ++ ++ ok = g_dbus_connection_emit_signal (f->conns[i], ++ NULL, ++ FINISHED_PATH, ++ FINISHED_INTERFACE, ++ FINISHED_SIGNAL, ++ NULL, ++ &f->error); ++ g_assert_no_error (f->error); ++ g_assert_true (ok); ++ } ++ ++ /* Wait until we have seen the Finished signal from each sender */ ++ while (TRUE) ++ { ++ gboolean all_finished = TRUE; ++ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ all_finished = all_finished && f->finished[i]; ++ ++ if (all_finished) ++ break; ++ ++ g_main_context_iteration (NULL, TRUE); ++ } ++ ++ /* Assert that the correct things happened before each Finished signal */ ++ for (i = 0; i < f->received->len; i++) ++ { ++ const ReceivedMessage *received = g_ptr_array_index (f->received, i); ++ ++ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn)); ++ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy)); ++ g_assert_cmpint (plan->steps[received->step].action, ++ ==, TEST_ACTION_EMIT_SIGNAL); ++ ++ if (received->received_by_proxy != NULL) ++ f->received_by_proxy[received->step] += 1; ++ else ++ f->received_by_conn[received->step] += 1; ++ } ++ ++ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++) ++ { ++ const TestStep *step = &plan->steps[i]; ++ ++ if (step->action == TEST_ACTION_EMIT_SIGNAL) ++ { ++ const TestEmitSignal *signal = &plan->steps[i].u.signal; ++ ++ if (mode != SUBSCRIPTION_MODE_PROXY) ++ { ++ g_test_message ("Signal from step %u was received %u times by " ++ "GDBusConnection, expected %u", ++ i, f->received_by_conn[i], signal->received_by_conn); ++ g_assert_cmpuint (f->received_by_conn[i], ==, signal->received_by_conn); ++ } ++ else ++ { ++ g_assert_cmpuint (f->received_by_conn[i], ==, 0); ++ } ++ ++ if (mode != SUBSCRIPTION_MODE_CONN) ++ { ++ g_test_message ("Signal from step %u was received %u times by " ++ "GDBusProxy, expected %u", ++ i, f->received_by_proxy[i], signal->received_by_proxy); ++ g_assert_cmpuint (f->received_by_proxy[i], ==, signal->received_by_proxy); ++ } ++ else ++ { ++ g_assert_cmpuint (f->received_by_proxy[i], ==, 0); ++ } ++ } ++ } ++} ++ ++static void ++setup (Fixture *f, ++ G_GNUC_UNUSED const void *context) ++{ ++ GDBusConnection *subscriber; ++ guint i; ++ ++ session_bus_up (); ++ ++ f->proxies = g_ptr_array_new_full (MAX_TEST_STEPS, g_object_unref); ++ f->received = g_ptr_array_new_full (MAX_TEST_STEPS, ++ (GDestroyNotify) received_message_free); ++ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ { ++ f->conns[i] = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &f->error); ++ g_assert_no_error (f->error); ++ g_assert_nonnull (f->conns[i]); ++ ++ f->unique_names[i] = g_dbus_connection_get_unique_name (f->conns[i]); ++ g_assert_nonnull (f->unique_names[i]); ++ g_test_message ("%s is %s", ++ test_conn_descriptions[i], ++ f->unique_names[i]); ++ } ++ ++ subscriber = f->conns[TEST_CONN_SUBSCRIBER]; ++ ++ /* Used to wait for all connections to finish sending whatever they ++ * wanted to send */ ++ f->finished_subscription = g_dbus_connection_signal_subscribe (subscriber, ++ NULL, ++ FINISHED_INTERFACE, ++ FINISHED_SIGNAL, ++ FINISHED_PATH, ++ NULL, ++ G_DBUS_SIGNAL_FLAGS_NONE, ++ subscriber_finished_cb, ++ f, NULL); ++ /* AddMatch is sent asynchronously, so we don't know how ++ * soon it will be processed. Before emitting signals, we ++ * need to wait for the message bus to get as far as processing ++ * AddMatch. */ ++ g_test_message ("Waiting for AddMatch to be processed"); ++ connection_wait_for_bus (subscriber); ++} ++ ++static void ++test_conn_subscribe (Fixture *f, ++ const void *context) ++{ ++ fixture_run_plan (f, context, SUBSCRIPTION_MODE_CONN); ++} ++ ++static void ++test_proxy_subscribe (Fixture *f, ++ const void *context) ++{ ++ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PROXY); ++} ++ ++static void ++test_parallel_subscribe (Fixture *f, ++ const void *context) ++{ ++ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PARALLEL); ++} ++ ++static void ++teardown (Fixture *f, ++ G_GNUC_UNUSED const void *context) ++{ ++ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; ++ guint i; ++ ++ g_ptr_array_unref (f->proxies); ++ ++ if (f->finished_subscription != 0) ++ g_dbus_connection_signal_unsubscribe (subscriber, f->finished_subscription); ++ ++ for (i = 0; i < G_N_ELEMENTS (f->subscriptions); i++) ++ { ++ if (f->subscriptions[i] != 0) ++ g_dbus_connection_signal_unsubscribe (subscriber, f->subscriptions[i]); ++ } ++ ++ g_ptr_array_unref (f->received); ++ ++ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++) ++ g_clear_object (&f->conns[i]); ++ ++ g_clear_error (&f->error); ++ ++ session_bus_down (); ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); ++ ++ g_test_dbus_unset (); ++ ++#define ADD_SUBSCRIBE_TEST(name) \ ++ do { \ ++ g_test_add ("/gdbus/subscribe/conn/" #name, \ ++ Fixture, &plan_ ## name, \ ++ setup, test_conn_subscribe, teardown); \ ++ g_test_add ("/gdbus/subscribe/proxy/" #name, \ ++ Fixture, &plan_ ## name, \ ++ setup, test_proxy_subscribe, teardown); \ ++ g_test_add ("/gdbus/subscribe/parallel/" #name, \ ++ Fixture, &plan_ ## name, \ ++ setup, test_parallel_subscribe, teardown); \ ++ } while (0) ++ ++ ADD_SUBSCRIBE_TEST (simple); ++ ADD_SUBSCRIBE_TEST (broadcast_from_anyone); ++ ADD_SUBSCRIBE_TEST (match_twice); ++ ADD_SUBSCRIBE_TEST (limit_by_unique_name); ++ ++ return g_test_run(); ++} +diff --git a/gio/tests/meson.build b/gio/tests/meson.build +index e1f583c70..1cd55e4ad 100644 +--- a/gio/tests/meson.build ++++ b/gio/tests/meson.build +@@ -314,6 +314,7 @@ if host_machine.system() != 'windows' + }, + 'gdbus-proxy-unique-name' : {'extra_sources' : extra_sources}, + 'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources}, ++ 'gdbus-subscribe' : {'extra_sources' : extra_sources}, + 'gdbus-test-codegen' : { + 'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info], + 'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'], +-- +2.45.0 + + +From 38ee87713f8cc99c67ef00a4960027676cc16b25 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 19:28:15 +0000 +Subject: [PATCH 04/19] tests: Add support for subscribing to signals from a + well-known name + +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 133 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 126 insertions(+), 7 deletions(-) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index 3f53e1d7f..3d2a14e03 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -7,6 +7,9 @@ + + #include "gdbus-tests.h" + ++/* From the D-Bus Specification */ ++#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 ++ + #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" + #define DBUS_PATH_DBUS "/org/freedesktop/DBus" + #define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS +@@ -22,6 +25,9 @@ + #define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface" + #define FOO_SIGNAL "Foo" + ++#define ALREADY_OWNED_NAME "org.gtk.Test.AlreadyOwned" ++#define OWNED_LATER_NAME "org.gtk.Test.OwnedLater" ++ + /* Log @s in a debug message. */ + static inline const char * + nonnull (const char *s, +@@ -101,7 +107,8 @@ typedef struct + + typedef struct + { +- TestConn sender; ++ const char *string_sender; ++ TestConn unique_sender; + const char *path; + const char *iface; + const char *member; +@@ -109,11 +116,18 @@ typedef struct + GDBusSignalFlags flags; + } TestSubscribe; + ++typedef struct ++{ ++ const char *name; ++ TestConn owner; ++} TestOwnName; ++ + typedef enum + { + TEST_ACTION_NONE = 0, + TEST_ACTION_SUBSCRIBE, + TEST_ACTION_EMIT_SIGNAL, ++ TEST_ACTION_OWN_NAME, + } TestAction; + + typedef struct +@@ -122,6 +136,7 @@ typedef struct + union { + TestEmitSignal signal; + TestSubscribe subscribe; ++ TestOwnName own_name; + } u; + } TestStep; + +@@ -247,7 +262,7 @@ static const TestPlan plan_match_twice = + { + .action = TEST_ACTION_SUBSCRIBE, + .u.subscribe = { +- .sender = TEST_CONN_SERVICE, ++ .unique_sender = TEST_CONN_SERVICE, + .path = EXAMPLE_PATH, + .iface = EXAMPLE_INTERFACE, + }, +@@ -267,7 +282,7 @@ static const TestPlan plan_match_twice = + { + .action = TEST_ACTION_SUBSCRIBE, + .u.subscribe = { +- .sender = TEST_CONN_SERVICE, ++ .unique_sender = TEST_CONN_SERVICE, + .path = EXAMPLE_PATH, + .iface = EXAMPLE_INTERFACE, + }, +@@ -296,7 +311,7 @@ static const TestPlan plan_limit_by_unique_name = + /* Subscriber wants to receive signals from service */ + .action = TEST_ACTION_SUBSCRIBE, + .u.subscribe = { +- .sender = TEST_CONN_SERVICE, ++ .unique_sender = TEST_CONN_SERVICE, + .path = EXAMPLE_PATH, + .iface = EXAMPLE_INTERFACE, + }, +@@ -343,6 +358,62 @@ static const TestPlan plan_limit_by_unique_name = + }, + }; + ++static const TestPlan plan_limit_by_well_known_name = ++{ ++ .description = "A subscription via a well-known name only accepts messages " ++ "sent by the owner of that well-known name", ++ .steps = { ++ { ++ /* Service already owns one name */ ++ .action = TEST_ACTION_OWN_NAME, ++ .u.own_name = { ++ .name = ALREADY_OWNED_NAME, ++ .owner = TEST_CONN_SERVICE ++ }, ++ }, ++ { ++ /* Subscriber wants to receive signals from service */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .string_sender = ALREADY_OWNED_NAME, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* Subscriber wants to receive signals from service by another name */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .string_sender = OWNED_LATER_NAME, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* Service claims another name */ ++ .action = TEST_ACTION_OWN_NAME, ++ .u.own_name = { ++ .name = OWNED_LATER_NAME, ++ .owner = TEST_CONN_SERVICE ++ }, ++ }, ++ { ++ /* Now the subscriber gets this signal twice, once for each ++ * subscription; and similarly each of the two proxies gets this ++ * signal twice */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 2, ++ .received_by_proxy = 2 ++ }, ++ }, ++ }, ++}; ++ + typedef struct + { + const TestPlan *plan; +@@ -540,11 +611,16 @@ fixture_subscribe (Fixture *f, + GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER]; + const char *sender; + +- if (subscribe->sender != TEST_CONN_NONE) ++ if (subscribe->string_sender != NULL) ++ { ++ sender = subscribe->string_sender; ++ g_test_message ("\tSender: %s", sender); ++ } ++ else if (subscribe->unique_sender != TEST_CONN_NONE) + { +- sender = f->unique_names[subscribe->sender]; ++ sender = f->unique_names[subscribe->unique_sender]; + g_test_message ("\tSender: %s %s", +- test_conn_descriptions[subscribe->sender], ++ test_conn_descriptions[subscribe->unique_sender], + sender); + } + else +@@ -689,6 +765,43 @@ fixture_emit_signal (Fixture *f, + connection_wait_for_bus (f->conns[signal->sender]); + } + ++static void ++fixture_own_name (Fixture *f, ++ const TestOwnName *own_name) ++{ ++ GVariant *call_result; ++ guint32 flags; ++ guint32 result_code; ++ ++ g_test_message ("\tName: %s", own_name->name); ++ g_test_message ("\tOwner: %s", ++ test_conn_descriptions[own_name->owner]); ++ ++ /* For simplicity, we do this via a direct bus call rather than ++ * using g_bus_own_name_on_connection(). The flags in ++ * GBusNameOwnerFlags are numerically equal to those in the ++ * D-Bus wire protocol. */ ++ flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE; ++ call_result = g_dbus_connection_call_sync (f->conns[own_name->owner], ++ DBUS_SERVICE_DBUS, ++ DBUS_PATH_DBUS, ++ DBUS_INTERFACE_DBUS, ++ "RequestName", ++ g_variant_new ("(su)", ++ own_name->name, ++ flags), ++ G_VARIANT_TYPE ("(u)"), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ &f->error); ++ g_assert_no_error (f->error); ++ g_assert_nonnull (call_result); ++ g_variant_get (call_result, "(u)", &result_code); ++ g_assert_cmpuint (result_code, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); ++ g_variant_unref (call_result); ++} ++ + static void + fixture_run_plan (Fixture *f, + const TestPlan *plan, +@@ -721,6 +834,11 @@ fixture_run_plan (Fixture *f, + fixture_emit_signal (f, &step->u.signal, i); + break; + ++ case TEST_ACTION_OWN_NAME: ++ g_test_message ("Step %u: claiming bus name", i); ++ fixture_own_name (f, &step->u.own_name); ++ break; ++ + case TEST_ACTION_NONE: + /* Padding to fill the rest of the array, do nothing */ + break; +@@ -933,6 +1051,7 @@ main (int argc, + ADD_SUBSCRIBE_TEST (broadcast_from_anyone); + ADD_SUBSCRIBE_TEST (match_twice); + ADD_SUBSCRIBE_TEST (limit_by_unique_name); ++ ADD_SUBSCRIBE_TEST (limit_by_well_known_name); + + return g_test_run(); + } +-- +2.45.0 + + +From 59ff44e332059175a47af3ed02da28714b70c51b Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 19:44:03 +0000 +Subject: [PATCH 05/19] tests: Add a test-case for what happens if a unique + name doesn't exist + +On GNOME/glib#3268 there was some concern about whether this would +allow an attacker to send signals and have them be matched to a +GDBusProxy in this situation, but it seems that was a false alarm. + +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 48 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index 3d2a14e03..350ec9f52 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -358,6 +358,53 @@ static const TestPlan plan_limit_by_unique_name = + }, + }; + ++static const TestPlan plan_nonexistent_unique_name = ++{ ++ .description = "A subscription via a unique name that doesn't exist " ++ "accepts no messages", ++ .steps = { ++ { ++ /* Subscriber wants to receive signals from service */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ /* This relies on the implementation detail that the dbus-daemon ++ * (and presumably other bus implementations) never actually generates ++ * a unique name in this format */ ++ .string_sender = ":0.this.had.better.not.exist", ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ }, ++ }, ++ { ++ /* Attacker wants to trick subscriber into thinking that service ++ * sent a signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Attacker tries harder, by sending a signal unicast directly to ++ * the subscriber */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ }, ++}; ++ + static const TestPlan plan_limit_by_well_known_name = + { + .description = "A subscription via a well-known name only accepts messages " +@@ -1051,6 +1098,7 @@ main (int argc, + ADD_SUBSCRIBE_TEST (broadcast_from_anyone); + ADD_SUBSCRIBE_TEST (match_twice); + ADD_SUBSCRIBE_TEST (limit_by_unique_name); ++ ADD_SUBSCRIBE_TEST (nonexistent_unique_name); + ADD_SUBSCRIBE_TEST (limit_by_well_known_name); + + return g_test_run(); +-- +2.45.0 + + +From 600d631e0978fd529877f481c10a7b3e971337cf Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 20:10:29 +0000 +Subject: [PATCH 06/19] tests: Add test coverage for signals that match the + message bus's name + +This is a special case of unique names, even though it's syntactically +a well-known name. + +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 161 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 154 insertions(+), 7 deletions(-) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index 350ec9f52..af100de7d 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -13,6 +13,7 @@ + #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" + #define DBUS_PATH_DBUS "/org/freedesktop/DBus" + #define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS ++#define NAME_OWNER_CHANGED "NameOwnerChanged" + + /* A signal that each connection emits to indicate that it has finished + * emitting other signals */ +@@ -101,6 +102,7 @@ typedef struct + const char *iface; + const char *member; + const char *arg0; ++ const char *args; + guint received_by_conn; + guint received_by_proxy; + } TestEmitSignal; +@@ -120,6 +122,8 @@ typedef struct + { + const char *name; + TestConn owner; ++ guint received_by_conn; ++ guint received_by_proxy; + } TestOwnName; + + typedef enum +@@ -461,6 +465,63 @@ static const TestPlan plan_limit_by_well_known_name = + }, + }; + ++static const TestPlan plan_limit_to_message_bus = ++{ ++ .description = "A subscription to the message bus only accepts messages " ++ "from the message bus", ++ .steps = { ++ { ++ /* Subscriber wants to receive signals from the message bus itself */ ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .string_sender = DBUS_SERVICE_DBUS, ++ .path = DBUS_PATH_DBUS, ++ .iface = DBUS_INTERFACE_DBUS, ++ }, ++ }, ++ { ++ /* Attacker wants to trick subscriber into thinking that the message ++ * bus sent a signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .path = DBUS_PATH_DBUS, ++ .iface = DBUS_INTERFACE_DBUS, ++ .member = NAME_OWNER_CHANGED, ++ .arg0 = "would I lie to you?", ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Attacker tries harder, by sending a signal unicast directly to ++ * the subscriber, and using more realistic arguments */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .sender = TEST_CONN_ATTACKER, ++ .path = DBUS_PATH_DBUS, ++ .iface = DBUS_INTERFACE_DBUS, ++ .member = NAME_OWNER_CHANGED, ++ .args = "('com.example.Name', '', ':1.12')", ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* When the message bus sends a signal (in this case triggered by ++ * owning a name), it should still get through */ ++ .action = TEST_ACTION_OWN_NAME, ++ .u.own_name = { ++ .name = OWNED_LATER_NAME, ++ .owner = TEST_CONN_SERVICE, ++ .received_by_conn = 1, ++ .received_by_proxy = 1 ++ }, ++ }, ++ }, ++}; ++ + typedef struct + { + const TestPlan *plan; +@@ -591,7 +652,18 @@ fixture_received_signal (Fixture *f, + } + } + +- g_assert_cmpint (received->sender, !=, TEST_CONN_NONE); ++ if (g_str_equal (sender_name, DBUS_SERVICE_DBUS)) ++ { ++ g_test_message ("Signal received from message bus %s", ++ sender_name); ++ } ++ else ++ { ++ g_test_message ("Signal received from %s %s", ++ test_conn_descriptions[received->sender], ++ sender_name); ++ g_assert_cmpint (received->sender, !=, TEST_CONN_NONE); ++ } + + g_test_message ("Signal received from %s %s via %s", + test_conn_descriptions[received->sender], +@@ -607,13 +679,56 @@ fixture_received_signal (Fixture *f, + g_test_message ("\tString argument 0: %s", received->arg0); + g_test_message ("\tSent in step: %u", received->step); + } +- else ++ else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) + { +- g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(uu)"); + g_variant_get (parameters, "(uu)", NULL, &received->step); + g_test_message ("\tArgument 0: (not a string)"); + g_test_message ("\tSent in step: %u", received->step); + } ++ else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) ++ { ++ const char *name; ++ const char *old_owner; ++ const char *new_owner; ++ ++ /* The only signal of this signature that we legitimately receive ++ * during this test is NameOwnerChanged, so just assert that it ++ * is from the message bus and can be matched to a plausible step. ++ * (This is less thorough than the above, and will not work if we ++ * add a test scenario where a name's ownership is repeatedly ++ * changed while watching NameOwnerChanged - so don't do that.) */ ++ g_assert_cmpstr (sender_name, ==, DBUS_SERVICE_DBUS); ++ g_assert_cmpstr (path, ==, DBUS_PATH_DBUS); ++ g_assert_cmpstr (iface, ==, DBUS_INTERFACE_DBUS); ++ g_assert_cmpstr (member, ==, NAME_OWNER_CHANGED); ++ ++ g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner); ++ ++ for (i = 0; i < G_N_ELEMENTS (f->plan->steps); i++) ++ { ++ const TestStep *step = &f->plan->steps[i]; ++ ++ if (step->action == TEST_ACTION_OWN_NAME) ++ { ++ const TestOwnName *own_name = &step->u.own_name; ++ ++ if (g_str_equal (name, own_name->name) ++ && g_str_equal (new_owner, f->unique_names[own_name->owner]) ++ && own_name->received_by_conn > 0) ++ { ++ received->step = i; ++ break; ++ } ++ } ++ ++ if (i >= G_N_ELEMENTS (f->plan->steps)) ++ g_error ("Could not match message to a test step"); ++ } ++ } ++ else ++ { ++ g_error ("Unexpected message received"); ++ } + + g_ptr_array_add (f->received, g_steal_pointer (&received)); + } +@@ -782,10 +897,15 @@ fixture_emit_signal (Fixture *f, + * Otherwise put something that will not match any arg0. + * Either way, put the sequence number in argument 1 so we can + * correlate sent messages with received messages later. */ +- if (signal->arg0 != NULL) ++ if (signal->args != NULL) + { +- g_test_message ("\tString argument 0: %s", signal->arg0); + /* floating */ ++ body = g_variant_new_parsed (signal->args); ++ g_assert_nonnull (body); ++ } ++ else if (signal->arg0 != NULL) ++ { ++ g_test_message ("\tString argument 0: %s", signal->arg0); + body = g_variant_new ("(su)", signal->arg0, (guint32) step_number); + } + else +@@ -933,8 +1053,6 @@ fixture_run_plan (Fixture *f, + + g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn)); + g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy)); +- g_assert_cmpint (plan->steps[received->step].action, +- ==, TEST_ACTION_EMIT_SIGNAL); + + if (received->received_by_proxy != NULL) + f->received_by_proxy[received->step] += 1; +@@ -974,6 +1092,34 @@ fixture_run_plan (Fixture *f, + g_assert_cmpuint (f->received_by_proxy[i], ==, 0); + } + } ++ else if (step->action == TEST_ACTION_OWN_NAME) ++ { ++ const TestOwnName *own_name = &plan->steps[i].u.own_name; ++ ++ if (mode != SUBSCRIPTION_MODE_PROXY) ++ { ++ g_test_message ("NameOwnerChanged from step %u was received %u " ++ "times by GDBusConnection, expected %u", ++ i, f->received_by_conn[i], own_name->received_by_conn); ++ g_assert_cmpuint (f->received_by_conn[i], ==, own_name->received_by_conn); ++ } ++ else ++ { ++ g_assert_cmpuint (f->received_by_conn[i], ==, 0); ++ } ++ ++ if (mode != SUBSCRIPTION_MODE_CONN) ++ { ++ g_test_message ("NameOwnerChanged from step %u was received %u " ++ "times by GDBusProxy, expected %u", ++ i, f->received_by_proxy[i], own_name->received_by_proxy); ++ g_assert_cmpuint (f->received_by_proxy[i], ==, own_name->received_by_proxy); ++ } ++ else ++ { ++ g_assert_cmpuint (f->received_by_proxy[i], ==, 0); ++ } ++ } + } + } + +@@ -1100,6 +1246,7 @@ main (int argc, + ADD_SUBSCRIBE_TEST (limit_by_unique_name); + ADD_SUBSCRIBE_TEST (nonexistent_unique_name); + ADD_SUBSCRIBE_TEST (limit_by_well_known_name); ++ ADD_SUBSCRIBE_TEST (limit_to_message_bus); + + return g_test_run(); + } +-- +2.45.0 + + +From d338bad6baa8a198953b109c00a96b02a18a8103 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 14 Mar 2024 19:18:15 +0000 +Subject: [PATCH 07/19] gdbusprivate: Add symbolic constants for the message + bus itself + +Using these is a bit more clearly correct than repeating them everywhere. +To avoid excessive diffstat in a branch for a bug fix, I'm not +immediately replacing all existing occurrences of the same literals with +these names. + +The names of these constants are chosen to be consistent with libdbus, +despite using somewhat outdated terminology (D-Bus now uses the term +"well-known bus name" for what used to be called a service name, +reserving the word "service" to mean specifically the programs that +have .service files and participate in service activation). + +Signed-off-by: Simon McVittie +--- + gio/gdbusprivate.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h +index 8f8a93ba7..e9bca11ca 100644 +--- a/gio/gdbusprivate.h ++++ b/gio/gdbusprivate.h +@@ -29,6 +29,11 @@ + + G_BEGIN_DECLS + ++/* Bus name, interface and object path of the message bus itself */ ++#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" ++#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS ++#define DBUS_PATH_DBUS "/org/freedesktop/DBus" ++ + /* ---------------------------------------------------------------------------------------------------- */ + + typedef struct GDBusWorker GDBusWorker; +-- +2.45.0 + + +From a207a6f5adc9e6cea929c07a9d7533cc90043d01 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Wed, 1 May 2024 19:43:24 +0100 +Subject: [PATCH 08/19] gdbusconnection: Move SignalData, SignalSubscriber + higher up + +Subsequent changes will need to access these data structures from +on_worker_message_received(). No functional change here, only moving +code around. + +[Backport to 2.66.x: fix minor conflicts] +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 128 +++++++++++++++++++++--------------------- + 1 file changed, 65 insertions(+), 63 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index 2808b1e46..8d841d78b 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -299,6 +299,71 @@ _g_strv_has_string (const gchar* const *haystack, + + /* ---------------------------------------------------------------------------------------------------- */ + ++typedef struct ++{ ++ /* All fields are immutable after construction. */ ++ gatomicrefcount ref_count; ++ GDBusSignalCallback callback; ++ gpointer user_data; ++ GDestroyNotify user_data_free_func; ++ guint id; ++ GMainContext *context; ++} SignalSubscriber; ++ ++static SignalSubscriber * ++signal_subscriber_ref (SignalSubscriber *subscriber) ++{ ++ g_atomic_ref_count_inc (&subscriber->ref_count); ++ return subscriber; ++} ++ ++static void ++signal_subscriber_unref (SignalSubscriber *subscriber) ++{ ++ if (g_atomic_ref_count_dec (&subscriber->ref_count)) ++ { ++ /* Destroy the user data. It doesn’t matter which thread ++ * signal_subscriber_unref() is called in (or whether it’s called with a ++ * lock held), as call_destroy_notify() always defers to the next ++ * #GMainContext iteration. */ ++ call_destroy_notify (subscriber->context, ++ subscriber->user_data_free_func, ++ subscriber->user_data); ++ ++ g_main_context_unref (subscriber->context); ++ g_free (subscriber); ++ } ++} ++ ++typedef struct ++{ ++ gchar *rule; ++ gchar *sender; ++ gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ ++ gchar *interface_name; ++ gchar *member; ++ gchar *object_path; ++ gchar *arg0; ++ GDBusSignalFlags flags; ++ GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ ++} SignalData; ++ ++static void ++signal_data_free (SignalData *signal_data) ++{ ++ g_free (signal_data->rule); ++ g_free (signal_data->sender); ++ g_free (signal_data->sender_unique_name); ++ g_free (signal_data->interface_name); ++ g_free (signal_data->member); ++ g_free (signal_data->object_path); ++ g_free (signal_data->arg0); ++ g_ptr_array_unref (signal_data->subscribers); ++ g_free (signal_data); ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ + #ifdef G_OS_WIN32 + #define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE) + #else +@@ -3253,69 +3318,6 @@ g_dbus_connection_remove_filter (GDBusConnection *connection, + + /* ---------------------------------------------------------------------------------------------------- */ + +-typedef struct +-{ +- gchar *rule; +- gchar *sender; +- gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ +- gchar *interface_name; +- gchar *member; +- gchar *object_path; +- gchar *arg0; +- GDBusSignalFlags flags; +- GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ +-} SignalData; +- +-static void +-signal_data_free (SignalData *signal_data) +-{ +- g_free (signal_data->rule); +- g_free (signal_data->sender); +- g_free (signal_data->sender_unique_name); +- g_free (signal_data->interface_name); +- g_free (signal_data->member); +- g_free (signal_data->object_path); +- g_free (signal_data->arg0); +- g_ptr_array_unref (signal_data->subscribers); +- g_free (signal_data); +-} +- +-typedef struct +-{ +- /* All fields are immutable after construction. */ +- gatomicrefcount ref_count; +- GDBusSignalCallback callback; +- gpointer user_data; +- GDestroyNotify user_data_free_func; +- guint id; +- GMainContext *context; +-} SignalSubscriber; +- +-static SignalSubscriber * +-signal_subscriber_ref (SignalSubscriber *subscriber) +-{ +- g_atomic_ref_count_inc (&subscriber->ref_count); +- return subscriber; +-} +- +-static void +-signal_subscriber_unref (SignalSubscriber *subscriber) +-{ +- if (g_atomic_ref_count_dec (&subscriber->ref_count)) +- { +- /* Destroy the user data. It doesn’t matter which thread +- * signal_subscriber_unref() is called in (or whether it’s called with a +- * lock held), as call_destroy_notify() always defers to the next +- * #GMainContext iteration. */ +- call_destroy_notify (subscriber->context, +- subscriber->user_data_free_func, +- subscriber->user_data); +- +- g_main_context_unref (subscriber->context); +- g_free (subscriber); +- } +-} +- + static gchar * + args_to_rule (const gchar *sender, + const gchar *interface_name, +-- +2.45.0 + + +From 998cee11197dda4f48334921900bca0e6a8cd62b Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 14 Mar 2024 19:30:12 +0000 +Subject: [PATCH 09/19] gdbusconnection: Factor out signal_data_new_take() + +No functional changes, except that the implicit ownership-transfer +for the rule field becomes explicit (the local variable is set to NULL +afterwards). + +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 42 ++++++++++++++++++++++++++++++++---------- + 1 file changed, 32 insertions(+), 10 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index 8d841d78b..67091574a 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -348,6 +348,30 @@ typedef struct + GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ + } SignalData; + ++static SignalData * ++signal_data_new_take (gchar *rule, ++ gchar *sender, ++ gchar *sender_unique_name, ++ gchar *interface_name, ++ gchar *member, ++ gchar *object_path, ++ gchar *arg0, ++ GDBusSignalFlags flags) ++{ ++ SignalData *signal_data = g_new0 (SignalData, 1); ++ ++ signal_data->rule = rule; ++ signal_data->sender = sender; ++ signal_data->sender_unique_name = sender_unique_name; ++ signal_data->interface_name = interface_name; ++ signal_data->member = member; ++ signal_data->object_path = object_path; ++ signal_data->arg0 = arg0; ++ signal_data->flags = flags; ++ signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref); ++ return g_steal_pointer (&signal_data); ++} ++ + static void + signal_data_free (SignalData *signal_data) + { +@@ -3584,16 +3608,14 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + goto out; + } + +- signal_data = g_new0 (SignalData, 1); +- signal_data->rule = rule; +- signal_data->sender = g_strdup (sender); +- signal_data->sender_unique_name = g_strdup (sender_unique_name); +- signal_data->interface_name = g_strdup (interface_name); +- signal_data->member = g_strdup (member); +- signal_data->object_path = g_strdup (object_path); +- signal_data->arg0 = g_strdup (arg0); +- signal_data->flags = flags; +- signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref); ++ signal_data = signal_data_new_take (g_steal_pointer (&rule), ++ g_strdup (sender), ++ g_strdup (sender_unique_name), ++ g_strdup (interface_name), ++ g_strdup (member), ++ g_strdup (object_path), ++ g_strdup (arg0), ++ flags); + g_ptr_array_add (signal_data->subscribers, subscriber); + + g_hash_table_insert (connection->map_rule_to_signal_data, +-- +2.45.0 + + +From 6df7b138b1f5b4c53ad124182b80e05cdba16c3c Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 23 Apr 2024 20:31:57 +0100 +Subject: [PATCH 10/19] gdbusconnection: Factor out add_signal_data() + +No functional changes. + +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 64 +++++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 27 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index 67091574a..a570e5856 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -3462,6 +3462,42 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) + + /* ---------------------------------------------------------------------------------------------------- */ + ++/* called in any thread, connection lock is held */ ++static void ++add_signal_data (GDBusConnection *connection, ++ SignalData *signal_data) ++{ ++ GPtrArray *signal_data_array; ++ ++ g_hash_table_insert (connection->map_rule_to_signal_data, ++ signal_data->rule, ++ signal_data); ++ ++ /* Add the match rule to the bus... ++ * ++ * Avoid adding match rules for NameLost and NameAcquired messages - the bus will ++ * always send such messages to us. ++ */ ++ if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) ++ { ++ if (!is_signal_data_for_name_lost_or_acquired (signal_data)) ++ add_match_rule (connection, signal_data->rule); ++ } ++ ++ signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ++ signal_data->sender_unique_name); ++ if (signal_data_array == NULL) ++ { ++ signal_data_array = g_ptr_array_new (); ++ g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, ++ g_strdup (signal_data->sender_unique_name), ++ signal_data_array); ++ } ++ g_ptr_array_add (signal_data_array, signal_data); ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ + /** + * g_dbus_connection_signal_subscribe: + * @connection: a #GDBusConnection +@@ -3551,7 +3587,6 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + gchar *rule; + SignalData *signal_data; + SignalSubscriber *subscriber; +- GPtrArray *signal_data_array; + const gchar *sender_unique_name; + + /* Right now we abort if AddMatch() fails since it can only fail with the bus being in +@@ -3617,32 +3652,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + g_strdup (arg0), + flags); + g_ptr_array_add (signal_data->subscribers, subscriber); +- +- g_hash_table_insert (connection->map_rule_to_signal_data, +- signal_data->rule, +- signal_data); +- +- /* Add the match rule to the bus... +- * +- * Avoid adding match rules for NameLost and NameAcquired messages - the bus will +- * always send such messages to us. +- */ +- if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) +- { +- if (!is_signal_data_for_name_lost_or_acquired (signal_data)) +- add_match_rule (connection, signal_data->rule); +- } +- +- signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name); +- if (signal_data_array == NULL) +- { +- signal_data_array = g_ptr_array_new (); +- g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, +- g_strdup (signal_data->sender_unique_name), +- signal_data_array); +- } +- g_ptr_array_add (signal_data_array, signal_data); ++ add_signal_data (connection, signal_data); + + out: + g_hash_table_insert (connection->map_id_to_signal_data, +-- +2.45.0 + + +From dd4a94708ef1bd63763bedcb93161746cbc9c7e6 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 14 Mar 2024 19:51:59 +0000 +Subject: [PATCH 11/19] gdbusconnection: Factor out + remove_signal_data_if_unused + +No functional change, just removing some nesting. The check for whether +signal_data->subscribers is empty changes from a conditional that tests +whether it is into an early-return if it isn't. + +A subsequent commit will add additional conditions that make us consider +a SignalData to be still in use and therefore not eligible to be removed. + +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 83 +++++++++++++++++++++++++------------------ + 1 file changed, 48 insertions(+), 35 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index a570e5856..b2268e637 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -3666,6 +3666,52 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + + /* ---------------------------------------------------------------------------------------------------- */ + ++/* ++ * Called in any thread. ++ * Must hold the connection lock when calling this, unless ++ * connection->finalizing is TRUE. ++ * May free signal_data, so do not dereference it after this. ++ */ ++static void ++remove_signal_data_if_unused (GDBusConnection *connection, ++ SignalData *signal_data) ++{ ++ GPtrArray *signal_data_array; ++ ++ if (signal_data->subscribers->len != 0) ++ return; ++ ++ g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); ++ ++ signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ++ signal_data->sender_unique_name); ++ g_warn_if_fail (signal_data_array != NULL); ++ g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); ++ ++ if (signal_data_array->len == 0) ++ { ++ g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, ++ signal_data->sender_unique_name)); ++ } ++ ++ /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ ++ if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && ++ !is_signal_data_for_name_lost_or_acquired (signal_data) && ++ !g_dbus_connection_is_closed (connection) && ++ !connection->finalizing) ++ { ++ /* The check for g_dbus_connection_is_closed() means that ++ * sending the RemoveMatch message can't fail with ++ * G_IO_ERROR_CLOSED, because we're holding the lock, ++ * so on_worker_closed() can't happen between the check we just ++ * did, and releasing the lock later. ++ */ ++ remove_match_rule (connection, signal_data->rule); ++ } ++ ++ signal_data_free (signal_data); ++} ++ + /* called in any thread */ + /* must hold lock when calling this (except if connection->finalizing is TRUE) + * returns the number of removed subscribers */ +@@ -3674,7 +3720,6 @@ unsubscribe_id_internal (GDBusConnection *connection, + guint subscription_id) + { + SignalData *signal_data; +- GPtrArray *signal_data_array; + guint n; + guint n_removed = 0; + +@@ -3701,40 +3746,8 @@ unsubscribe_id_internal (GDBusConnection *connection, + GUINT_TO_POINTER (subscription_id))); + n_removed++; + g_ptr_array_remove_index_fast (signal_data->subscribers, n); +- +- if (signal_data->subscribers->len == 0) +- { +- g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); +- +- signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name); +- g_warn_if_fail (signal_data_array != NULL); +- g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); +- +- if (signal_data_array->len == 0) +- { +- g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name)); +- } +- +- /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ +- if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && +- !is_signal_data_for_name_lost_or_acquired (signal_data) && +- !g_dbus_connection_is_closed (connection) && +- !connection->finalizing) +- { +- /* The check for g_dbus_connection_is_closed() means that +- * sending the RemoveMatch message can't fail with +- * G_IO_ERROR_CLOSED, because we're holding the lock, +- * so on_worker_closed() can't happen between the check we just +- * did, and releasing the lock later. +- */ +- remove_match_rule (connection, signal_data->rule); +- } +- +- signal_data_free (signal_data); +- } +- ++ /* May free signal_data */ ++ remove_signal_data_if_unused (connection, signal_data); + goto out; + } + +-- +2.45.0 + + +From 64758288c6e71dd92270771b6b81891e4be87fa9 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 23 Apr 2024 20:39:05 +0100 +Subject: [PATCH 12/19] gdbusconnection: Stop storing sender_unique_name in + SignalData + +This will become confusing when we start tracking the owner of a +well-known-name sender, and it's redundant anyway. Instead, track the +1 bit of data that we actually need: whether it's a well-known name. + +Strictly speaking this too is redundant, because it's syntactically +derivable from the sender, but only via extra string operations. +A subsequent commit will add a data structure to keep track of the +owner of a well-known-name sender, at which point this boolean will +be replaced by the presence or absence of that data structure. + +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 36 ++++++++++++++++++++++++------------ + 1 file changed, 24 insertions(+), 12 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index b2268e637..e17703d0f 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -339,19 +339,19 @@ typedef struct + { + gchar *rule; + gchar *sender; +- gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ + gchar *interface_name; + gchar *member; + gchar *object_path; + gchar *arg0; + GDBusSignalFlags flags; + GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ ++ gboolean sender_is_its_own_owner; + } SignalData; + + static SignalData * + signal_data_new_take (gchar *rule, + gchar *sender, +- gchar *sender_unique_name, ++ gboolean sender_is_its_own_owner, + gchar *interface_name, + gchar *member, + gchar *object_path, +@@ -362,7 +362,7 @@ signal_data_new_take (gchar *rule, + + signal_data->rule = rule; + signal_data->sender = sender; +- signal_data->sender_unique_name = sender_unique_name; ++ signal_data->sender_is_its_own_owner = sender_is_its_own_owner; + signal_data->interface_name = interface_name; + signal_data->member = member; + signal_data->object_path = object_path; +@@ -377,7 +377,6 @@ signal_data_free (SignalData *signal_data) + { + g_free (signal_data->rule); + g_free (signal_data->sender); +- g_free (signal_data->sender_unique_name); + g_free (signal_data->interface_name); + g_free (signal_data->member); + g_free (signal_data->object_path); +@@ -3453,7 +3452,7 @@ remove_match_rule (GDBusConnection *connection, + static gboolean + is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) + { +- return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 && ++ return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && + g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && + g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && + (g_strcmp0 (signal_data->member, "NameLost") == 0 || +@@ -3465,7 +3464,8 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) + /* called in any thread, connection lock is held */ + static void + add_signal_data (GDBusConnection *connection, +- SignalData *signal_data) ++ SignalData *signal_data, ++ const char *sender_unique_name) + { + GPtrArray *signal_data_array; + +@@ -3485,12 +3485,12 @@ add_signal_data (GDBusConnection *connection, + } + + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name); ++ sender_unique_name); + if (signal_data_array == NULL) + { + signal_data_array = g_ptr_array_new (); + g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, +- g_strdup (signal_data->sender_unique_name), ++ g_strdup (sender_unique_name), + signal_data_array); + } + g_ptr_array_add (signal_data_array, signal_data); +@@ -3587,6 +3587,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + gchar *rule; + SignalData *signal_data; + SignalSubscriber *subscriber; ++ gboolean sender_is_its_own_owner; + const gchar *sender_unique_name; + + /* Right now we abort if AddMatch() fails since it can only fail with the bus being in +@@ -3622,6 +3623,11 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags); + + if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) ++ sender_is_its_own_owner = TRUE; ++ else ++ sender_is_its_own_owner = FALSE; ++ ++ if (sender_is_its_own_owner) + sender_unique_name = sender; + else + sender_unique_name = ""; +@@ -3645,14 +3651,14 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + + signal_data = signal_data_new_take (g_steal_pointer (&rule), + g_strdup (sender), +- g_strdup (sender_unique_name), ++ sender_is_its_own_owner, + g_strdup (interface_name), + g_strdup (member), + g_strdup (object_path), + g_strdup (arg0), + flags); + g_ptr_array_add (signal_data->subscribers, subscriber); +- add_signal_data (connection, signal_data); ++ add_signal_data (connection, signal_data, sender_unique_name); + + out: + g_hash_table_insert (connection->map_id_to_signal_data, +@@ -3676,22 +3682,28 @@ static void + remove_signal_data_if_unused (GDBusConnection *connection, + SignalData *signal_data) + { ++ const gchar *sender_unique_name; + GPtrArray *signal_data_array; + + if (signal_data->subscribers->len != 0) + return; + ++ if (signal_data->sender_is_its_own_owner) ++ sender_unique_name = signal_data->sender; ++ else ++ sender_unique_name = ""; ++ + g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); + + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name); ++ sender_unique_name); + g_warn_if_fail (signal_data_array != NULL); + g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); + + if (signal_data_array->len == 0) + { + g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, +- signal_data->sender_unique_name)); ++ sender_unique_name)); + } + + /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ +-- +2.45.0 + + +From 3dd78b5a67c0dea7c453d29345cf42f6c30a600b Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Wed, 1 May 2024 15:43:09 +0100 +Subject: [PATCH 13/19] gdbus: Track name owners for signal subscriptions + +We will use this in a subsequent commit to prevent signals from an +impostor from being delivered to a subscriber. + +To avoid message reordering leading to misleading situations, this does +not use the existing mechanism for watching bus name ownership, which +delivers the ownership changes to other main-contexts. Instead, it all +happens on the single thread used by the GDBusWorker, so the order in +which messages are received is the order in which they are processed. + +[Backported to glib-2-74, resolving minor conflicts] +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 350 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 343 insertions(+), 7 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index e17703d0f..6d18b2457 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -336,6 +336,31 @@ signal_subscriber_unref (SignalSubscriber *subscriber) + } + + typedef struct ++{ ++ /* ++ * 1 reference while waiting for GetNameOwner() to finish ++ * 1 reference for each SignalData that points to this one as its ++ * shared_name_watcher ++ */ ++ grefcount ref_count; ++ ++ gchar *owner; ++ guint32 get_name_owner_serial; ++} WatchedName; ++ ++static WatchedName * ++watched_name_new (void) ++{ ++ WatchedName *watched_name = g_new0 (WatchedName, 1); ++ ++ g_ref_count_init (&watched_name->ref_count); ++ watched_name->owner = NULL; ++ return g_steal_pointer (&watched_name); ++} ++ ++typedef struct SignalData SignalData; ++ ++struct SignalData + { + gchar *rule; + gchar *sender; +@@ -345,13 +370,36 @@ typedef struct + gchar *arg0; + GDBusSignalFlags flags; + GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */ +- gboolean sender_is_its_own_owner; +-} SignalData; ++ ++ /* ++ * If the sender is a well-known name, this is an unowned SignalData ++ * representing the NameOwnerChanged signal that tracks its owner. ++ * NULL if sender is NULL. ++ * NULL if sender is its own owner (a unique name or DBUS_SERVICE_DBUS). ++ * ++ * Invariants: if not NULL, then ++ * shared_name_watcher->sender == DBUS_SERVICE_DBUS ++ * shared_name_watcher->interface_name == DBUS_INTERFACE_DBUS ++ * shared_name_watcher->member == "NameOwnerChanged" ++ * shared_name_watcher->object_path == DBUS_PATH_DBUS ++ * shared_name_watcher->arg0 == sender ++ * shared_name_watcher->flags == NONE ++ * shared_name_watcher->watched_name == NULL ++ */ ++ SignalData *shared_name_watcher; ++ ++ /* ++ * Non-NULL if this SignalData is another SignalData's shared_name_watcher. ++ * One reference for each SignalData that has this one as its ++ * shared_name_watcher. ++ * Otherwise NULL. ++ */ ++ WatchedName *watched_name; ++}; + + static SignalData * + signal_data_new_take (gchar *rule, + gchar *sender, +- gboolean sender_is_its_own_owner, + gchar *interface_name, + gchar *member, + gchar *object_path, +@@ -362,7 +410,6 @@ signal_data_new_take (gchar *rule, + + signal_data->rule = rule; + signal_data->sender = sender; +- signal_data->sender_is_its_own_owner = sender_is_its_own_owner; + signal_data->interface_name = interface_name; + signal_data->member = member; + signal_data->object_path = object_path; +@@ -375,6 +422,17 @@ signal_data_new_take (gchar *rule, + static void + signal_data_free (SignalData *signal_data) + { ++ /* The SignalData should not be freed while it still has subscribers */ ++ g_assert (signal_data->subscribers->len == 0); ++ ++ /* The SignalData should not be freed while it is watching for ++ * NameOwnerChanged on behalf of another SignalData */ ++ g_assert (signal_data->watched_name == NULL); ++ ++ /* The SignalData should be detached from its name watcher, if any, ++ * before it is freed */ ++ g_assert (signal_data->shared_name_watcher == NULL); ++ + g_free (signal_data->rule); + g_free (signal_data->sender); + g_free (signal_data->interface_name); +@@ -382,6 +440,7 @@ signal_data_free (SignalData *signal_data) + g_free (signal_data->object_path); + g_free (signal_data->arg0); + g_ptr_array_unref (signal_data->subscribers); ++ + g_free (signal_data); + } + +@@ -513,6 +572,7 @@ struct _GDBusConnection + + /* Map used for managing method replies, protected by @lock */ + GHashTable *map_method_serial_to_task; /* guint32 -> GTask* */ ++ GHashTable *map_method_serial_to_name_watcher; /* guint32 -> unowned SignalData* */ + + /* Maps used for managing signal subscription, protected by @lock */ + GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ +@@ -760,6 +820,7 @@ g_dbus_connection_finalize (GObject *object) + g_error_free (connection->initialization_error); + + g_hash_table_unref (connection->map_method_serial_to_task); ++ g_hash_table_unref (connection->map_method_serial_to_name_watcher); + + g_hash_table_unref (connection->map_rule_to_signal_data); + g_hash_table_unref (connection->map_id_to_signal_data); +@@ -1155,6 +1216,7 @@ g_dbus_connection_init (GDBusConnection *connection) + g_mutex_init (&connection->init_lock); + + connection->map_method_serial_to_task = g_hash_table_new (g_direct_hash, g_direct_equal); ++ connection->map_method_serial_to_name_watcher = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash, + g_str_equal); +@@ -2281,6 +2343,191 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connecti + + /* ---------------------------------------------------------------------------------------------------- */ + ++/* ++ * Called in any thread. ++ * Must hold the connection lock when calling this, unless ++ * connection->finalizing is TRUE. ++ */ ++static void ++name_watcher_unref_watched_name (GDBusConnection *connection, ++ SignalData *name_watcher) ++{ ++ WatchedName *watched_name = name_watcher->watched_name; ++ ++ g_assert (watched_name != NULL); ++ ++ if (!g_ref_count_dec (&watched_name->ref_count)) ++ return; ++ ++ /* Removing watched_name from the name_watcher may result in ++ * name_watcher being freed, so we must make sure name_watcher is no ++ * longer in map_method_serial_to_name_watcher. ++ * ++ * If we stop watching the name while our GetNameOwner call was still ++ * in-flight, then when the reply eventually arrives, we will not find ++ * its serial number in the map and harmlessly ignore it as a result. */ ++ if (watched_name->get_name_owner_serial != 0) ++ g_hash_table_remove (connection->map_method_serial_to_name_watcher, ++ GUINT_TO_POINTER (watched_name->get_name_owner_serial)); ++ ++ name_watcher->watched_name = NULL; ++ g_free (watched_name->owner); ++ g_free (watched_name); ++} ++ ++/* called in GDBusWorker thread with lock held */ ++static void ++name_watcher_set_name_owner_unlocked (SignalData *name_watcher, ++ const char *new_owner) ++{ ++ if (new_owner != NULL && new_owner[0] == '\0') ++ new_owner = NULL; ++ ++ g_assert (name_watcher->watched_name != NULL); ++ g_set_str (&name_watcher->watched_name->owner, new_owner); ++} ++ ++/* called in GDBusWorker thread with lock held */ ++static void ++name_watcher_deliver_name_owner_changed_unlocked (SignalData *name_watcher, ++ GDBusMessage *message) ++{ ++ GVariant *body; ++ ++ body = g_dbus_message_get_body (message); ++ ++ if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(sss)")))) ++ { ++ const char *name; ++ const char *new_owner; ++ ++ g_variant_get (body, "(&s&s&s)", &name, NULL, &new_owner); ++ ++ /* Our caller already checked this */ ++ g_assert (g_strcmp0 (name_watcher->arg0, name) == 0); ++ ++ if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_unique_name (new_owner))) ++ name_watcher_set_name_owner_unlocked (name_watcher, new_owner); ++ else ++ g_warning ("Received NameOwnerChanged signal with invalid owner \"%s\" for \"%s\"", ++ new_owner, name); ++ } ++ else ++ { ++ g_warning ("Received NameOwnerChanged signal with unexpected " ++ "signature %s", ++ body == NULL ? "()" : g_variant_get_type_string (body)); ++ ++ } ++} ++ ++/* called in GDBusWorker thread with lock held */ ++static void ++name_watcher_deliver_get_name_owner_reply_unlocked (SignalData *name_watcher, ++ GDBusConnection *connection, ++ GDBusMessage *message) ++{ ++ GDBusMessageType type; ++ GVariant *body; ++ WatchedName *watched_name; ++ ++ watched_name = name_watcher->watched_name; ++ g_assert (watched_name != NULL); ++ g_assert (watched_name->get_name_owner_serial != 0); ++ ++ type = g_dbus_message_get_message_type (message); ++ body = g_dbus_message_get_body (message); ++ ++ if (type == G_DBUS_MESSAGE_TYPE_ERROR) ++ { ++ if (g_strcmp0 (g_dbus_message_get_error_name (message), ++ "org.freedesktop.DBus.Error.NameHasNoOwner")) ++ name_watcher_set_name_owner_unlocked (name_watcher, NULL); ++ /* else it's something like NoReply or AccessDenied, which tells ++ * us nothing - leave the owner set to whatever we most recently ++ * learned from NameOwnerChanged, or NULL */ ++ } ++ else if (type != G_DBUS_MESSAGE_TYPE_METHOD_RETURN) ++ { ++ g_warning ("Received GetNameOwner reply with unexpected type %d", ++ type); ++ } ++ else if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))) ++ { ++ const char *new_owner; ++ ++ g_variant_get (body, "(&s)", &new_owner); ++ ++ if (G_LIKELY (g_dbus_is_unique_name (new_owner))) ++ name_watcher_set_name_owner_unlocked (name_watcher, new_owner); ++ else ++ g_warning ("Received GetNameOwner reply with invalid owner \"%s\" for \"%s\"", ++ new_owner, name_watcher->arg0); ++ } ++ else ++ { ++ g_warning ("Received GetNameOwner reply with unexpected signature %s", ++ body == NULL ? "()" : g_variant_get_type_string (body)); ++ } ++ ++ g_hash_table_remove (connection->map_method_serial_to_name_watcher, ++ GUINT_TO_POINTER (watched_name->get_name_owner_serial)); ++ watched_name->get_name_owner_serial = 0; ++} ++ ++/* Called in a user thread, lock is held */ ++static void ++name_watcher_call_get_name_owner_unlocked (GDBusConnection *connection, ++ SignalData *name_watcher) ++{ ++ GDBusMessage *message; ++ GError *local_error = NULL; ++ WatchedName *watched_name; ++ ++ g_assert (g_strcmp0 (name_watcher->sender, DBUS_SERVICE_DBUS) == 0); ++ g_assert (g_strcmp0 (name_watcher->interface_name, DBUS_INTERFACE_DBUS) == 0); ++ g_assert (g_strcmp0 (name_watcher->member, "NameOwnerChanged") == 0); ++ g_assert (g_strcmp0 (name_watcher->object_path, DBUS_PATH_DBUS) == 0); ++ /* arg0 of the NameOwnerChanged message is the well-known name whose owner ++ * we are interested in */ ++ g_assert (g_dbus_is_name (name_watcher->arg0)); ++ g_assert (name_watcher->flags == G_DBUS_SIGNAL_FLAGS_NONE); ++ ++ watched_name = name_watcher->watched_name; ++ g_assert (watched_name != NULL); ++ g_assert (watched_name->owner == NULL); ++ g_assert (watched_name->get_name_owner_serial == 0); ++ g_assert (name_watcher->shared_name_watcher == NULL); ++ ++ message = g_dbus_message_new_method_call (DBUS_SERVICE_DBUS, ++ DBUS_PATH_DBUS, ++ DBUS_INTERFACE_DBUS, ++ "GetNameOwner"); ++ g_dbus_message_set_body (message, g_variant_new ("(s)", name_watcher->arg0)); ++ ++ if (g_dbus_connection_send_message_unlocked (connection, message, ++ G_DBUS_SEND_MESSAGE_FLAGS_NONE, ++ &watched_name->get_name_owner_serial, ++ &local_error)) ++ { ++ g_assert (watched_name->get_name_owner_serial != 0); ++ g_hash_table_insert (connection->map_method_serial_to_name_watcher, ++ GUINT_TO_POINTER (watched_name->get_name_owner_serial), ++ name_watcher); ++ } ++ else ++ { ++ g_critical ("Error while sending GetNameOwner() message: %s", ++ local_error->message); ++ g_clear_error (&local_error); ++ g_assert (watched_name->get_name_owner_serial == 0); ++ } ++ ++ g_object_unref (message); ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ + typedef struct + { + guint id; +@@ -2392,6 +2639,7 @@ on_worker_message_received (GDBusWorker *worker, + { + guint32 reply_serial; + GTask *task; ++ SignalData *name_watcher; + + reply_serial = g_dbus_message_get_reply_serial (message); + CONNECTION_LOCK (connection); +@@ -2407,6 +2655,19 @@ on_worker_message_received (GDBusWorker *worker, + { + //g_debug ("message reply/error for serial %d but no SendMessageData found for %p", reply_serial, connection); + } ++ ++ name_watcher = g_hash_table_lookup (connection->map_method_serial_to_name_watcher, ++ GUINT_TO_POINTER (reply_serial)); ++ ++ if (name_watcher != NULL) ++ { ++ g_assert (name_watcher->watched_name != NULL); ++ g_assert (name_watcher->watched_name->get_name_owner_serial == reply_serial); ++ name_watcher_deliver_get_name_owner_reply_unlocked (name_watcher, ++ connection, ++ message); ++ } ++ + CONNECTION_UNLOCK (connection); + } + else if (message_type == G_DBUS_MESSAGE_TYPE_SIGNAL) +@@ -3586,6 +3847,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + { + gchar *rule; + SignalData *signal_data; ++ SignalData *name_watcher = NULL; + SignalSubscriber *subscriber; + gboolean sender_is_its_own_owner; + const gchar *sender_unique_name; +@@ -3651,13 +3913,59 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, + + signal_data = signal_data_new_take (g_steal_pointer (&rule), + g_strdup (sender), +- sender_is_its_own_owner, + g_strdup (interface_name), + g_strdup (member), + g_strdup (object_path), + g_strdup (arg0), + flags); + g_ptr_array_add (signal_data->subscribers, subscriber); ++ ++ /* If subscribing to a signal from a specific sender with a well-known ++ * name, we must first subscribe to NameOwnerChanged signals for that ++ * well-known name, so that we can match the current owner of the name ++ * against the sender of each signal. */ ++ if (sender != NULL && !sender_is_its_own_owner) ++ { ++ gchar *name_owner_rule = NULL; ++ ++ /* We already checked that sender != NULL implies MESSAGE_BUS_CONNECTION */ ++ g_assert (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION); ++ ++ name_owner_rule = args_to_rule (DBUS_SERVICE_DBUS, ++ DBUS_INTERFACE_DBUS, ++ "NameOwnerChanged", ++ DBUS_PATH_DBUS, ++ sender, ++ G_DBUS_SIGNAL_FLAGS_NONE); ++ name_watcher = g_hash_table_lookup (connection->map_rule_to_signal_data, name_owner_rule); ++ ++ if (name_watcher == NULL) ++ { ++ name_watcher = signal_data_new_take (g_steal_pointer (&name_owner_rule), ++ g_strdup (DBUS_SERVICE_DBUS), ++ g_strdup (DBUS_INTERFACE_DBUS), ++ g_strdup ("NameOwnerChanged"), ++ g_strdup (DBUS_PATH_DBUS), ++ g_strdup (sender), ++ G_DBUS_SIGNAL_FLAGS_NONE); ++ add_signal_data (connection, name_watcher, DBUS_SERVICE_DBUS); ++ } ++ ++ if (name_watcher->watched_name == NULL) ++ { ++ name_watcher->watched_name = watched_name_new (); ++ name_watcher_call_get_name_owner_unlocked (connection, name_watcher); ++ } ++ else ++ { ++ g_ref_count_inc (&name_watcher->watched_name->ref_count); ++ } ++ ++ signal_data->shared_name_watcher = name_watcher; ++ ++ g_clear_pointer (&name_owner_rule, g_free); ++ } ++ + add_signal_data (connection, signal_data, sender_unique_name); + + out: +@@ -3685,10 +3993,18 @@ remove_signal_data_if_unused (GDBusConnection *connection, + const gchar *sender_unique_name; + GPtrArray *signal_data_array; + ++ /* Cannot remove while there are still subscribers */ + if (signal_data->subscribers->len != 0) + return; + +- if (signal_data->sender_is_its_own_owner) ++ /* Cannot remove while another SignalData is still using this one ++ * as its shared_name_watcher, which holds watched_name->ref_count > 0 */ ++ if (signal_data->watched_name != NULL) ++ return; ++ ++ /* Point of no return: we have committed to removing it */ ++ ++ if (signal_data->sender != NULL && signal_data->shared_name_watcher == NULL) + sender_unique_name = signal_data->sender; + else + sender_unique_name = ""; +@@ -3721,6 +4037,15 @@ remove_signal_data_if_unused (GDBusConnection *connection, + remove_match_rule (connection, signal_data->rule); + } + ++ if (signal_data->shared_name_watcher != NULL) ++ { ++ SignalData *name_watcher = g_steal_pointer (&signal_data->shared_name_watcher); ++ ++ name_watcher_unref_watched_name (connection, name_watcher); ++ /* May free signal_data */ ++ remove_signal_data_if_unused (connection, name_watcher); ++ } ++ + signal_data_free (signal_data); + } + +@@ -3990,6 +4315,17 @@ schedule_callbacks (GDBusConnection *connection, + continue; + } + ++ if (signal_data->watched_name != NULL) ++ { ++ /* Invariant: SignalData should only have a watched_name if it ++ * represents the NameOwnerChanged signal */ ++ g_assert (g_strcmp0 (sender, DBUS_SERVICE_DBUS) == 0); ++ g_assert (g_strcmp0 (interface, DBUS_INTERFACE_DBUS) == 0); ++ g_assert (g_strcmp0 (path, DBUS_PATH_DBUS) == 0); ++ g_assert (g_strcmp0 (member, "NameOwnerChanged") == 0); ++ name_watcher_deliver_name_owner_changed_unlocked (signal_data, message); ++ } ++ + for (m = 0; m < signal_data->subscribers->len; m++) + { + SignalSubscriber *subscriber = signal_data->subscribers->pdata[m]; +@@ -4051,7 +4387,7 @@ distribute_signals (GDBusConnection *connection, + schedule_callbacks (connection, signal_data_array, message, sender); + } + +- /* collect subscribers not matching on sender */ ++ /* collect subscribers not matching on sender, or matching a well-known name */ + signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ""); + if (signal_data_array != NULL) + schedule_callbacks (connection, signal_data_array, message, sender); +-- +2.45.0 + + +From ccaea1caa6ebafd931b6221f241dc90d5fd96e62 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Thu, 14 Mar 2024 20:42:41 +0000 +Subject: [PATCH 14/19] gdbusconnection: Don't deliver signals if the sender + doesn't match + +Otherwise a malicious connection on a shared bus, especially the system +bus, could trick GDBus clients into processing signals sent by the +malicious connection as though they had come from the real owner of a +well-known service name. + +Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3268 +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 40 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index 6d18b2457..85f99c5c6 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -4296,6 +4296,46 @@ schedule_callbacks (GDBusConnection *connection, + if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0) + continue; + ++ if (signal_data->shared_name_watcher != NULL) ++ { ++ /* We want signals from a specified well-known name, which means ++ * the signal's sender needs to be the unique name that currently ++ * owns that well-known name, and we will have found this ++ * SignalData in ++ * connection->map_sender_unique_name_to_signal_data_array[""]. */ ++ const WatchedName *watched_name; ++ const char *current_owner; ++ ++ g_assert (signal_data->sender != NULL); ++ /* Invariant: We never need to watch for the owner of a unique ++ * name, or for the owner of DBUS_SERVICE_DBUS, either of which ++ * is always its own owner */ ++ g_assert (!g_dbus_is_unique_name (signal_data->sender)); ++ g_assert (g_strcmp0 (signal_data->sender, DBUS_SERVICE_DBUS) != 0); ++ ++ watched_name = signal_data->shared_name_watcher->watched_name; ++ g_assert (watched_name != NULL); ++ current_owner = watched_name->owner; ++ ++ /* Skip the signal if the actual sender is not known to own ++ * the required name */ ++ if (current_owner == NULL || g_strcmp0 (current_owner, sender) != 0) ++ continue; ++ } ++ else if (signal_data->sender != NULL) ++ { ++ /* We want signals from a unique name or o.fd.DBus... */ ++ g_assert (g_dbus_is_unique_name (signal_data->sender) ++ || g_str_equal (signal_data->sender, DBUS_SERVICE_DBUS)); ++ ++ /* ... which means we must have found this SignalData in ++ * connection->map_sender_unique_name_to_signal_data_array[signal_data->sender], ++ * therefore we would only have found it if the signal's ++ * actual sender matches the required signal_data->sender */ ++ g_assert (g_strcmp0 (signal_data->sender, sender) == 0); ++ } ++ /* else the sender is unspecified and we will accept anything */ ++ + if (signal_data->arg0 != NULL) + { + if (arg0 == NULL) +-- +2.45.0 + + +From 9fe1b11d43ca8f8d77ecf5e3f0976306d6d099ec Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 19:51:50 +0000 +Subject: [PATCH 15/19] tests: Add a test for matching by two well-known names + +The expected result is that because TEST_CONN_SERVICE owns +ALREADY_OWNED_NAME but not (yet) OWNED_LATER_NAME, the signal will be +delivered to the subscriber for the former but not the latter. +Before #3268 was fixed, it was incorrectly delivered to both. + +Reproduces: https://gitlab.gnome.org/GNOME/glib/-/issues/3268 (partially) +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index af100de7d..171d6107d 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -440,6 +440,19 @@ static const TestPlan plan_limit_by_well_known_name = + .iface = EXAMPLE_INTERFACE, + }, + }, ++ { ++ /* When the service sends a signal with the name it already owns, ++ * it should get through */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 1, ++ .received_by_proxy = 1 ++ }, ++ }, + { + /* Service claims another name */ + .action = TEST_ACTION_OWN_NAME, +-- +2.45.0 + + +From d9c20bc802c6f2fc8e36c51acad7a751de5f1b3f Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Fri, 8 Mar 2024 19:53:22 +0000 +Subject: [PATCH 16/19] tests: Add a test for signal filtering by well-known + name + +The vulnerability reported as GNOME/glib#3268 can be characterized +as: these signals from an attacker should not be delivered to either +the GDBusConnection or the GDBusProxy, but in fact they are (in at +least some scenarios). + +Reproduces: https://gitlab.gnome.org/GNOME/glib/-/issues/3268 +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index 171d6107d..5406ba7e2 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -440,6 +440,33 @@ static const TestPlan plan_limit_by_well_known_name = + .iface = EXAMPLE_INTERFACE, + }, + }, ++ { ++ /* Attacker wants to trick subscriber into thinking that service ++ * sent a signal */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, ++ { ++ /* Attacker tries harder, by sending a signal unicast directly to ++ * the subscriber */ ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_ATTACKER, ++ .unicast_to = TEST_CONN_SUBSCRIBER, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ .received_by_proxy = 0 ++ }, ++ }, + { + /* When the service sends a signal with the name it already owns, + * it should get through */ +-- +2.45.0 + + +From 420402151349d1e34ea13b7585c76d3a51ba48f7 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 23 Apr 2024 21:39:43 +0100 +Subject: [PATCH 17/19] tests: Ensure that unsubscribing with GetNameOwner + in-flight doesn't crash + +This was a bug that existed during development of this branch; make sure +it doesn't come back. + +This test fails with a use-after-free and crash if we comment out the +part of name_watcher_unref_watched_name() that removes the name watcher +from `map_method_serial_to_name_watcher`. + +It would also fail with an assertion failure if we asserted in +name_watcher_unref_watched_name() that get_name_owner_serial == 0 +(i.e. that GetNameOwner is not in-flight at destruction). + +Signed-off-by: Simon McVittie +--- + gio/tests/gdbus-subscribe.c | 52 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 51 insertions(+), 1 deletion(-) + +diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c +index 5406ba7e2..4cba4f565 100644 +--- a/gio/tests/gdbus-subscribe.c ++++ b/gio/tests/gdbus-subscribe.c +@@ -116,6 +116,7 @@ typedef struct + const char *member; + const char *arg0; + GDBusSignalFlags flags; ++ gboolean unsubscribe_immediately; + } TestSubscribe; + + typedef struct +@@ -141,6 +142,7 @@ typedef struct + TestEmitSignal signal; + TestSubscribe subscribe; + TestOwnName own_name; ++ guint unsubscribe_undo_step; + } u; + } TestStep; + +@@ -505,6 +507,43 @@ static const TestPlan plan_limit_by_well_known_name = + }, + }; + ++static const TestPlan plan_unsubscribe_immediately = ++{ ++ .description = "Unsubscribing before GetNameOwner can return doesn't result in a crash", ++ .steps = { ++ { ++ /* Service already owns one name */ ++ .action = TEST_ACTION_OWN_NAME, ++ .u.own_name = { ++ .name = ALREADY_OWNED_NAME, ++ .owner = TEST_CONN_SERVICE ++ }, ++ }, ++ { ++ .action = TEST_ACTION_SUBSCRIBE, ++ .u.subscribe = { ++ .string_sender = ALREADY_OWNED_NAME, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .unsubscribe_immediately = TRUE ++ }, ++ }, ++ { ++ .action = TEST_ACTION_EMIT_SIGNAL, ++ .u.signal = { ++ .sender = TEST_CONN_SERVICE, ++ .path = EXAMPLE_PATH, ++ .iface = EXAMPLE_INTERFACE, ++ .member = FOO_SIGNAL, ++ .received_by_conn = 0, ++ /* The proxy can't unsubscribe, except by destroying the proxy ++ * completely, which we don't currently implement in this test */ ++ .received_by_proxy = 1 ++ }, ++ }, ++ }, ++}; ++ + static const TestPlan plan_limit_to_message_bus = + { + .description = "A subscription to the message bus only accepts messages " +@@ -855,8 +894,18 @@ fixture_subscribe (Fixture *f, + subscribe->flags, + subscribed_signal_cb, + f, NULL); ++ + g_assert_cmpuint (id, !=, 0); +- f->subscriptions[step_number] = id; ++ ++ if (subscribe->unsubscribe_immediately) ++ { ++ g_test_message ("\tImmediately unsubscribing"); ++ g_dbus_connection_signal_unsubscribe (subscriber, id); ++ } ++ else ++ { ++ f->subscriptions[step_number] = id; ++ } + } + + if (f->mode != SUBSCRIPTION_MODE_CONN) +@@ -1287,6 +1336,7 @@ main (int argc, + ADD_SUBSCRIBE_TEST (nonexistent_unique_name); + ADD_SUBSCRIBE_TEST (limit_by_well_known_name); + ADD_SUBSCRIBE_TEST (limit_to_message_bus); ++ ADD_SUBSCRIBE_TEST (unsubscribe_immediately); + + return g_test_run(); + } +-- +2.45.0 + + +From 6762e278c870dfaad4b26329da5e24b2e9bdceda Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Wed, 8 May 2024 14:46:08 +0000 +Subject: [PATCH 18/19] gdbusconnection: Allow name owners to have the syntax + of a well-known name + +In a D-Bus-Specification-compliant message bus, the owner of a well-known +name is a unique name. However, ibus has its own small implementation +of a message bus (src/ibusbus.c) in which org.freedesktop.IBus is +special-cased to also have itself as its owner (like org.freedesktop.DBus +on a standard message bus), and connects to that bus with the +G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION flag. The ability to do +this regressed when CVE-2024-34397 was fixed. + +Relax the checks to allow the owner of a well-known name to be any valid +D-Bus name, even if it is not syntactically a unique name. + +Fixes: 683b14b9 "gdbus: Track name owners for signal subscriptions" +Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3353 +Bug-Debian: https://bugs.debian.org/1070730 +Bug-Debian: https://bugs.debian.org/1070736 +Bug-Debian: https://bugs.debian.org/1070743 +Bug-Debian: https://bugs.debian.org/1070745 +Signed-off-by: Simon McVittie +--- + gio/gdbusconnection.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c +index 85f99c5c6..348b5b985 100644 +--- a/gio/gdbusconnection.c ++++ b/gio/gdbusconnection.c +@@ -2406,7 +2406,10 @@ name_watcher_deliver_name_owner_changed_unlocked (SignalData *name_watcher, + /* Our caller already checked this */ + g_assert (g_strcmp0 (name_watcher->arg0, name) == 0); + +- if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_unique_name (new_owner))) ++ /* FIXME: This should be validating that `new_owner` is a unique name, ++ * but IBus’ implementation of a message bus is not compliant with the spec. ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/3353 */ ++ if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_name (new_owner))) + name_watcher_set_name_owner_unlocked (name_watcher, new_owner); + else + g_warning ("Received NameOwnerChanged signal with invalid owner \"%s\" for \"%s\"", +@@ -2458,7 +2461,10 @@ name_watcher_deliver_get_name_owner_reply_unlocked (SignalData *name_watcher, + + g_variant_get (body, "(&s)", &new_owner); + +- if (G_LIKELY (g_dbus_is_unique_name (new_owner))) ++ /* FIXME: This should be validating that `new_owner` is a unique name, ++ * but IBus’ implementation of a message bus is not compliant with the spec. ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/3353 */ ++ if (G_LIKELY (g_dbus_is_name (new_owner))) + name_watcher_set_name_owner_unlocked (name_watcher, new_owner); + else + g_warning ("Received GetNameOwner reply with invalid owner \"%s\" for \"%s\"", +-- +2.45.0 + + +From 9ad45e2d6699157d8b233bc67881e55f9b71f613 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Wed, 8 May 2024 22:53:51 +0200 +Subject: [PATCH 19/19] gdbusmessage: Clean the cached arg0 when setting the + message body + +We're now caching arg0 but such value is not cleared when a new body is +set as it's in the connection filter test cases where we've a leak as +highlighted by both valgrind and leak sanitizer +--- + gio/gdbusmessage.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c +index 40a77dd92..7489c0ac4 100644 +--- a/gio/gdbusmessage.c ++++ b/gio/gdbusmessage.c +@@ -1127,10 +1127,12 @@ g_dbus_message_set_body (GDBusMessage *message, + + if (message->body != NULL) + g_variant_unref (message->body); ++ ++ g_clear_pointer (&message->arg0_cache, g_variant_unref); ++ + if (body == NULL) + { + message->body = NULL; +- message->arg0_cache = NULL; + g_dbus_message_set_signature (message, NULL); + } + else +@@ -1144,8 +1146,6 @@ g_dbus_message_set_body (GDBusMessage *message, + if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) && + g_variant_n_children (message->body) > 0) + message->arg0_cache = g_variant_get_child_value (message->body, 0); +- else +- message->arg0_cache = NULL; + + type_string = g_variant_get_type_string (body); + type_string_len = strlen (type_string); +-- +2.45.0 + diff --git a/base/rx/rx-glib2/glib-change-version.patch b/base/rx/rx-glib2/glib-change-version.patch new file mode 100644 index 0000000..a8b378b --- /dev/null +++ b/base/rx/rx-glib2/glib-change-version.patch @@ -0,0 +1,2133 @@ +diff -Naur a/gio/gdbus-2.0/codegen/meson.build b/gio/gdbus-2.0/codegen/meson.build +--- a/gio/gdbus-2.0/codegen/meson.build 2021-08-19 21:27:25.686339000 +0600 ++++ b/gio/gdbus-2.0/codegen/meson.build 2024-06-20 09:59:13.236516507 +0600 +@@ -24,7 +24,7 @@ + # Provide tools for others when we're a subproject and they use the Meson GNOME module + meson.override_find_program('gdbus-codegen', gdbus_codegen) + +-codegen_dir = join_paths(glib_datadir, 'glib-2.0', 'codegen') ++codegen_dir = join_paths(glib_datadir, 'glib-2.00', 'codegen') + + gdbus_codegen_built_files = [] + gdbus_codegen_built_files += configure_file(input : 'config.py.in', +diff -Naur a/gio/gschema-2.00.its b/gio/gschema-2.00.its +--- a/gio/gschema-2.00.its 1970-01-01 06:00:00.000000000 +0600 ++++ b/gio/gschema-2.00.its 2021-08-19 21:27:25.713339800 +0600 +@@ -0,0 +1,25 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Naur a/gio/gschema-2.00.loc b/gio/gschema-2.00.loc +--- a/gio/gschema-2.00.loc 1970-01-01 06:00:00.000000000 +0600 ++++ b/gio/gschema-2.00.loc 2021-08-19 21:27:25.713339800 +0600 +@@ -0,0 +1,10 @@ ++ ++ ++ ++ ++ ++ ++ +diff -Naur a/gio/gschema.its b/gio/gschema.its +--- a/gio/gschema.its 2021-08-19 21:27:25.713339000 +0600 ++++ b/gio/gschema.its 1970-01-01 06:00:00.000000000 +0600 +@@ -1,25 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff -Naur a/gio/gschema.loc b/gio/gschema.loc +--- a/gio/gschema.loc 2021-08-19 21:27:25.713339000 +0600 ++++ b/gio/gschema.loc 1970-01-01 06:00:00.000000000 +0600 +@@ -1,10 +0,0 @@ +- +- +- +- +- +- +- +diff -Naur a/gio/meson.build b/gio/meson.build +--- a/gio/meson.build 2021-08-19 21:27:25.727340000 +0600 ++++ b/gio/meson.build 2024-06-20 10:39:38.014030594 +0600 +@@ -170,7 +170,7 @@ + + gnetworking_h = configure_file(input : 'gnetworking.h.in', + output : 'gnetworking.h', +- install_dir : join_paths(get_option('includedir'), 'glib-2.0/gio'), ++ install_dir : join_paths(get_option('includedir'), 'glib-2.00/gio'), + configuration : gnetworking_h_conf) + + gdbus_headers = files( +@@ -420,7 +420,7 @@ + internal_deps += [xdgmime_lib] + internal_objects += [xdgmime_lib.extract_all_objects()] + +- install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.0/gio') ++ install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.00/gio') + + if glib_conf.has('HAVE_NETLINK') + unix_sources += files( +@@ -725,7 +725,7 @@ + gio_headers += application_headers + gio_headers += settings_headers + gio_headers += gdbus_headers +-install_headers(gio_headers, subdir : 'glib-2.0/gio/') ++install_headers(gio_headers, subdir : 'glib-2.00/gio/') + + # We can't use gnome.mkenums() because the GNOME module looks for glib-mkenums + # in PATH, which means you can't bootstrap glib with its own glib-mkenums. +@@ -734,7 +734,7 @@ + capture : true, + input : gio_headers, + install : true, +- install_dir : join_paths(get_option('includedir'), 'glib-2.0/gio'), ++ install_dir : join_paths(get_option('includedir'), 'glib-2.00/gio'), + command : [python, glib_mkenums, + '--template', files('gioenumtypes.h.template'), + '@INPUT@', gnetworking_h]) +@@ -800,7 +800,7 @@ + gio_dtrace_hdr = [] + endif + +-libgio = library('gio-2.0', ++libgio = library('gio-2.00', + gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources, + gio_dtrace_hdr, gio_dtrace_obj, + objects : internal_objects, +@@ -825,7 +825,7 @@ + pkgconfig_giomodulesdir = join_paths('${libdir}', 'gio', 'modules') + endif + +-schemas_subdir = join_paths('glib-2.0', 'schemas') ++schemas_subdir = join_paths('glib-2.00', 'schemas') + + libgio_dep = declare_dependency(link_with : libgio, + dependencies : [libgmodule_dep, libgobject_dep, gioenumtypes_dep], +@@ -833,7 +833,7 @@ + + pkg.generate(libgio, + libraries_private : [osx_ldflags], +- requires : ['glib-2.0', 'gobject-2.0'], ++ requires : ['glib-2.00', 'gobject-2.00'], + variables : ['datadir=' + join_paths('${prefix}', get_option('datadir')), + 'schemasdir=' + join_paths('${datadir}', schemas_subdir), + 'bindir=' + join_paths('${prefix}', get_option('bindir')), +@@ -848,13 +848,13 @@ + 'gsettings=' + join_paths('${bindir}', 'gsettings')], + version : glib_version, + install_dir : glib_pkgconfigreldir, +- filebase : 'gio-2.0', ++ filebase : 'gio-2.00', + name : 'GIO', + description : 'glib I/O library', + ) + + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('gio-2.0', libgio_dep) ++ meson.override_dependency('gio-2.00', libgio_dep) + endif + + +@@ -871,16 +871,16 @@ + meson.override_dependency('gio-win32-2.0', libgio_dep) + endif + else +- pkg.generate(requires : ['gobject-2.0', 'gio-2.0'], +- subdirs : ['gio-unix-2.0'], ++ pkg.generate(requires : ['gobject-2.00', 'gio-2.00'], ++ subdirs : ['gio-unix-2.00'], + version : glib_version, + install_dir : glib_pkgconfigreldir, +- filebase : 'gio-unix-2.0', ++ filebase : 'gio-unix-2.00', + name : 'GIO unix specific APIs', + description : 'unix specific headers for glib I/O library', + ) + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('gio-unix-2.0', libgio_dep) ++ meson.override_dependency('gio-unix-2.00', libgio_dep) + endif + endif + +@@ -997,7 +997,7 @@ + install_data('gschema.dtd', + install_dir : join_paths(get_option('datadir'), schemas_subdir)) + +-install_data(['gschema.loc', 'gschema.its'], ++install_data(['gschema-2.00.loc', 'gschema-2.00.its'], + install_dir : join_paths(get_option('datadir'), 'gettext/its')) + + executable('gdbus', 'gdbus-tool.c', +diff -Naur a/glib/meson.build b/glib/meson.build +--- a/glib/meson.build 2021-08-19 21:27:25.779340000 +0600 ++++ b/glib/meson.build 2024-06-20 09:41:17.032294858 +0600 +@@ -1,5 +1,5 @@ + configure_file(input : 'glibconfig.h.in', output : 'glibconfig.h', +- install_dir : join_paths(get_option('libdir'), 'glib-2.0/include'), ++ install_dir : join_paths(get_option('libdir'), 'glib-2.00/include'), + configuration : glibconfig_conf) + + subdir('libcharset') +@@ -128,7 +128,7 @@ + 'glib-unix.h', + 'glib-object.h', + ) +-install_headers(glib_headers, subdir : 'glib-2.0') ++install_headers(glib_headers, subdir : 'glib-2.00') + + glib_deprecated_headers = files( + 'deprecated/gallocator.h', +@@ -138,7 +138,7 @@ + 'deprecated/grel.h', + 'deprecated/gthread.h', + ) +-install_headers(glib_deprecated_headers, subdir : 'glib-2.0/glib/deprecated') ++install_headers(glib_deprecated_headers, subdir : 'glib-2.00/glib/deprecated') + + glib_sub_headers = files( + 'glib-autocleanups.h', +@@ -220,7 +220,7 @@ + 'gwin32.h', + 'gprintf.h', + ) +-install_headers(glib_sub_headers, subdir : 'glib-2.0/glib') ++install_headers(glib_sub_headers, subdir : 'glib-2.00/glib') + + deprecated_sources = files( + 'deprecated/gallocator.c', +@@ -366,7 +366,7 @@ + endif + + glib_c_args = ['-DG_LOG_DOMAIN="GLib"', '-DGLIB_COMPILATION'] + pcre_static_args + glib_hidden_visibility_args +-libglib = library('glib-2.0', ++libglib = library('glib-2.00', + glib_dtrace_obj, glib_dtrace_hdr, + sources : [deprecated_sources, glib_sources], + objects : [charset_lib.extract_all_objects()] + gnulib_objects + pcre_objects, +@@ -391,8 +391,8 @@ + pkg.generate(libglib, + libraries : [libintl_deps], + libraries_private : [osx_ldflags, win32_ldflags], +- subdirs : ['glib-2.0'], +- extra_cflags : ['-I${libdir}/glib-2.0/include'] + win32_cflags, ++ subdirs : ['glib-2.00'], ++ extra_cflags : ['-I${libdir}/glib-2.00/include'] + win32_cflags, + variables : ['bindir=' + join_paths('${prefix}', get_option('bindir')), + 'glib_genmarshal=' + join_paths('${bindir}', 'glib-genmarshal'), + 'gobject_query=' + join_paths('${bindir}', 'gobject-query'), +@@ -405,7 +405,7 @@ + ) + + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('glib-2.0', libglib_dep) ++ meson.override_dependency('glib-2.00', libglib_dep) + endif + + # On Windows, glib needs a spawn helper for g_spawn* API +@@ -468,7 +468,7 @@ + endif + configure_file( + input: 'libglib-gdb.py.in', +- output: 'libglib-2.0.so.@0@-gdb.py'.format(library_version), ++ output: 'libglib-2.00.so.@0@-gdb.py'.format(library_version), + configuration: gdb_conf, + install_dir: gdb_install_dir, + ) +diff -Naur a/glib-gettextize.in b/glib-gettextize.in +--- a/glib-gettextize.in 2021-08-19 21:27:25.746340000 +0600 ++++ b/glib-gettextize.in 2024-06-20 10:31:50.068973445 +0600 +@@ -51,7 +51,7 @@ + datarootdir=@datarootdir@ + datadir=@datadir@ + +-gettext_dir=$datadir/glib-2.0/gettext ++gettext_dir=$datadir/glib-2.00/gettext + + while test $# -gt 0; do + case "$1" in +diff -Naur a/gmodule/meson.build b/gmodule/meson.build +--- a/gmodule/meson.build 2021-08-19 21:27:25.798340000 +0600 ++++ b/gmodule/meson.build 2024-06-20 09:41:58.491030409 +0600 +@@ -59,7 +59,7 @@ + output : 'gmoduleconf.h', + configuration : gmoduleconf_conf) + +-install_headers(['gmodule.h'], subdir : 'glib-2.0') ++install_headers(['gmodule.h'], subdir : 'glib-2.00') + + gmodule_sources = ['gmodule.c'] + if host_system == 'windows' +@@ -72,7 +72,7 @@ + gmodule_sources += [gmodule_win_res] + endif + +-libgmodule = library('gmodule-2.0', ++libgmodule = library('gmodule-2.00', + sources : gmodule_sources, + version : library_version, + soversion : soversion, +@@ -88,31 +88,31 @@ + + pkg.generate(libgmodule, + libraries : [thread_dep], +- requires : ['glib-2.0'], ++ requires : ['glib-2.00'], + version : glib_version, + variables : [supported_var], + install_dir : glib_pkgconfigreldir, +- filebase : 'gmodule-no-export-2.0', ++ filebase : 'gmodule-no-export-2.00', + name : 'GModule', + description : 'Dynamic module loader for GLib', + ) + + pkg.generate(libraries : [libgmodule, export_dynamic_ldflags], +- requires : ['glib-2.0'], ++ requires : ['glib-2.00'], + version : glib_version, + variables : [supported_var], + install_dir : glib_pkgconfigreldir, +- filebase : 'gmodule-export-2.0', ++ filebase : 'gmodule-export-2.00', + name : 'GModule', + description : 'Dynamic module loader for GLib', + ) + + pkg.generate(libraries : [libgmodule, export_dynamic_ldflags], +- requires : ['glib-2.0'], ++ requires : ['glib-2.00'], + version : glib_version, + variables : [supported_var], + install_dir : glib_pkgconfigreldir, +- filebase : 'gmodule-2.0', ++ filebase : 'gmodule-2.00', + name : 'GModule', + description : 'Dynamic module loader for GLib', + ) +@@ -122,7 +122,7 @@ + dependencies : [libglib_dep]) + + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('gmodule-no-export-2.0', libgmodule_dep) +- meson.override_dependency('gmodule-export-2.0', libgmodule_dep) +- meson.override_dependency('gmodule-2.0', libgmodule_dep) ++ meson.override_dependency('gmodule-no-export-2.00', libgmodule_dep) ++ meson.override_dependency('gmodule-export-2.00', libgmodule_dep) ++ meson.override_dependency('gmodule-2.00', libgmodule_dep) + endif +diff -Naur a/gobject/meson.build b/gobject/meson.build +--- a/gobject/meson.build 2021-08-19 21:27:25.803341000 +0600 ++++ b/gobject/meson.build 2024-06-20 09:42:28.155841189 +0600 +@@ -20,7 +20,7 @@ + 'gvaluetypes.h', + 'gobjectnotifyqueue.c', # sic + ) +-install_headers(gobject_install_headers, subdir : 'glib-2.0/gobject') ++install_headers(gobject_install_headers, subdir : 'glib-2.00/gobject') + + gobject_sources = files( + 'gatomicarray.c', +@@ -101,7 +101,7 @@ + capture : true, + input : glib_enumtypes_input_headers, + install : true, +- install_dir : join_paths(get_option('includedir'), 'glib-2.0/gobject'), ++ install_dir : join_paths(get_option('includedir'), 'glib-2.00/gobject'), + command : [python, glib_mkenums, + '--template', files('glib-enumtypes.h.template'), + '@INPUT@']) +@@ -117,7 +117,7 @@ + + glib_enumtypes_dep = declare_dependency(sources : [glib_enumtypes_h]) + +-libgobject = library('gobject-2.0', ++libgobject = library('gobject-2.00', + gobject_dtrace_obj, gobject_dtrace_hdr, glib_enumtypes_h, glib_enumtypes_c, + sources : gobject_sources, + version : library_version, +@@ -131,10 +131,10 @@ + ) + + pkg.generate(libgobject, +- requires : ['glib-2.0'], ++ requires : ['glib-2.00'], + version : glib_version, + install_dir : glib_pkgconfigreldir, +- filebase : 'gobject-2.0', ++ filebase : 'gobject-2.00', + name : 'GObject', + description : 'GLib Type, Object, Parameter and Signal Library', + ) +@@ -144,7 +144,7 @@ + dependencies : [libglib_dep, glib_enumtypes_dep]) + + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('gobject-2.0', libgobject_dep) ++ meson.override_dependency('gobject-2.00', libgobject_dep) + endif + + executable('gobject-query', 'gobject-query.c', +@@ -156,7 +156,7 @@ + gdb_conf.set('datadir', glib_datadir) + configure_file( + input: 'libgobject-gdb.py.in', +- output: 'libgobject-2.0.so.@0@-gdb.py'.format(library_version), ++ output: 'libgobject-2.00.so.@0@-gdb.py'.format(library_version), + configuration: gdb_conf, + install_dir: gdb_install_dir, + ) +diff -Naur a/gthread/meson.build b/gthread/meson.build +--- a/gthread/meson.build 2021-08-19 21:27:25.805341000 +0600 ++++ b/gthread/meson.build 2024-06-20 09:42:40.519762324 +0600 +@@ -12,7 +12,7 @@ + gthread_sources += [gthread_win_res] + endif + +-libgthread = library('gthread-2.0', ++libgthread = library('gthread-2.00', + sources : gthread_sources, + version : library_version, + soversion : soversion, +@@ -25,10 +25,10 @@ + + pkg.generate(libgthread, + libraries : [thread_dep], +- requires : ['glib-2.0'], ++ requires : ['glib-2.00'], + version : glib_version, + install_dir : glib_pkgconfigreldir, +- filebase : 'gthread-2.0', ++ filebase : 'gthread-2.00', + name : 'GThread', + description : 'Thread support for GLib', + ) +@@ -36,5 +36,5 @@ + libgthread_dep = declare_dependency(link_with : libgthread) + + if meson.version().version_compare('>=0.54.0') +- meson.override_dependency('gthread-2.0', libgthread_dep) ++ meson.override_dependency('gthread-2.00', libgthread_dep) + endif +diff -Naur a/m4macros/glib-2.00.m4 b/m4macros/glib-2.00.m4 +--- a/m4macros/glib-2.00.m4 1970-01-01 06:00:00.000000000 +0600 ++++ b/m4macros/glib-2.00.m4 2024-06-20 09:43:31.857434862 +0600 +@@ -0,0 +1,215 @@ ++# Configure paths for GLIB ++# Owen Taylor 1997-2001 ++ ++# Increment this whenever this file is changed. ++#serial 4 ++ ++dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) ++dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject, ++dnl gthread, or gio is specified in MODULES, pass to pkg-config ++dnl ++AC_DEFUN([AM_PATH_GLIB_2_0], ++[dnl ++dnl Get the cflags and libraries from pkg-config ++dnl ++ ++dnl We can't use PKG_PREREQ because that needs 0.29. ++m4_ifndef([PKG_PROG_PKG_CONFIG], ++ [pkg.m4 version 0.28 or later is required]) ++ ++AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program], ++ , enable_glibtest=yes) ++ ++ min_glib_version=ifelse([$1], [], [2.0.0], [$1]) ++ pkg_config_args="glib-2.00 >= $min_glib_version" ++ for module in . $4 ++ do ++ case "$module" in ++ gmodule) ++ pkg_config_args="$pkg_config_args gmodule-2.00" ++ ;; ++ gmodule-no-export) ++ pkg_config_args="$pkg_config_args gmodule-no-export-2.00" ++ ;; ++ gobject) ++ pkg_config_args="$pkg_config_args gobject-2.00" ++ ;; ++ gthread) ++ pkg_config_args="$pkg_config_args gthread-2.00" ++ ;; ++ gio*) ++ pkg_config_args="$pkg_config_args $module-2.00" ++ ;; ++ esac ++ done ++ ++ PKG_PROG_PKG_CONFIG([0.16]) ++ ++ no_glib="" ++ ++ if test "x$PKG_CONFIG" = x ; then ++ no_glib=yes ++ PKG_CONFIG=no ++ fi ++ ++ dnl For GLIB_CFLAGS and GLIB_LIBS ++ PKG_CHECK_MODULES([GLIB], [$pkg_config_args], [:], [:]) ++ ++ dnl For the tools ++ PKG_CHECK_VAR([GLIB_GENMARSHAL], [glib-2.00], [glib_genmarshal]) ++ PKG_CHECK_VAR([GOBJECT_QUERY], [glib-2.00], [gobject_query]) ++ PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.00], [glib_mkenums]) ++ PKG_CHECK_VAR([GLIB_COMPILE_RESOURCES], [gio-2.00], [glib_compile_resources]) ++ ++ AC_MSG_CHECKING(for GLIB - version >= $min_glib_version) ++ ++ if test x$PKG_CONFIG != xno ; then ++ ## don't try to run the test against uninstalled libtool libs ++ if $PKG_CONFIG --uninstalled $pkg_config_args; then ++ echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH" ++ enable_glibtest=no ++ fi ++ ++ if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then ++ : ++ else ++ no_glib=yes ++ fi ++ fi ++ ++ if test x"$no_glib" = x ; then ++ glib_config_major_version=`$PKG_CONFIG --modversion glib-2.00 | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` ++ glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.00 | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` ++ glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.00 | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` ++ if test "x$enable_glibtest" = "xyes" ; then ++ ac_save_CFLAGS="$CFLAGS" ++ ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $GLIB_CFLAGS" ++ LIBS="$GLIB_LIBS $LIBS" ++dnl ++dnl Now check if the installed GLib is sufficiently new. (Also sanity ++dnl checks the results of pkg-config to some extent) ++dnl ++ rm -f conf.glibtest ++ AC_RUN_IFELSE([AC_LANG_SOURCE([[ ++#include ++#include ++#include ++ ++int ++main (void) ++{ ++ unsigned int major, minor, micro; ++ ++ fclose (fopen ("conf.glibtest", "w")); ++ ++ if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, µ) != 3) { ++ printf("%s, bad version string\n", "$min_glib_version"); ++ exit(1); ++ } ++ ++ if ((glib_major_version != $glib_config_major_version) || ++ (glib_minor_version != $glib_config_minor_version) || ++ (glib_micro_version != $glib_config_micro_version)) ++ { ++ printf("\n*** 'pkg-config --modversion glib-2.00' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", ++ $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version, ++ glib_major_version, glib_minor_version, glib_micro_version); ++ printf ("*** was found! If pkg-config was correct, then it is best\n"); ++ printf ("*** to remove the old version of GLib. You may also be able to fix the error\n"); ++ printf("*** by modifying your LD_LIBRARY_PATH environment variable, or by editing\n"); ++ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); ++ printf("*** required on your system.\n"); ++ printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); ++ printf("*** to point to the correct configuration files\n"); ++ } ++ else if ((glib_major_version != GLIB_MAJOR_VERSION) || ++ (glib_minor_version != GLIB_MINOR_VERSION) || ++ (glib_micro_version != GLIB_MICRO_VERSION)) ++ { ++ printf("*** GLib header files (version %d.%d.%d) do not match\n", ++ GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); ++ printf("*** library (version %d.%d.%d)\n", ++ glib_major_version, glib_minor_version, glib_micro_version); ++ } ++ else ++ { ++ if ((glib_major_version > major) || ++ ((glib_major_version == major) && (glib_minor_version > minor)) || ++ ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro))) ++ { ++ return 0; ++ } ++ else ++ { ++ printf("\n*** An old version of GLib (%u.%u.%u) was found.\n", ++ glib_major_version, glib_minor_version, glib_micro_version); ++ printf("*** You need a version of GLib newer than %u.%u.%u. The latest version of\n", ++ major, minor, micro); ++ printf("*** GLib is always available from ftp://ftp.gtk.org.\n"); ++ printf("***\n"); ++ printf("*** If you have already installed a sufficiently new version, this error\n"); ++ printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); ++ printf("*** being found. The easiest way to fix this is to remove the old version\n"); ++ printf("*** of GLib, but you can also set the PKG_CONFIG environment to point to the\n"); ++ printf("*** correct copy of pkg-config. (In this case, you will have to\n"); ++ printf("*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf\n"); ++ printf("*** so that the correct libraries are found at run-time))\n"); ++ } ++ } ++ return 1; ++} ++]])],[],[no_glib=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) ++ CFLAGS="$ac_save_CFLAGS" ++ LIBS="$ac_save_LIBS" ++ fi ++ fi ++ if test "x$no_glib" = x ; then ++ AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)) ++ ifelse([$2], , :, [$2]) ++ else ++ AC_MSG_RESULT(no) ++ if test "$PKG_CONFIG" = "no" ; then ++ echo "*** A new enough version of pkg-config was not found." ++ echo "*** See http://www.freedesktop.org/software/pkgconfig/" ++ else ++ if test -f conf.glibtest ; then ++ : ++ else ++ echo "*** Could not run GLib test program, checking why..." ++ ac_save_CFLAGS="$CFLAGS" ++ ac_save_LIBS="$LIBS" ++ CFLAGS="$CFLAGS $GLIB_CFLAGS" ++ LIBS="$LIBS $GLIB_LIBS" ++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ ++#include ++#include ++]], [[ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ]])], ++ [ echo "*** The test program compiled, but did not run. This usually means" ++ echo "*** that the run-time linker is not finding GLib or finding the wrong" ++ echo "*** version of GLib. If it is not finding GLib, you'll need to set your" ++ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" ++ echo "*** to the installed location Also, make sure you have run ldconfig if that" ++ echo "*** is required on your system" ++ echo "***" ++ echo "*** If you have an old version installed, it is best to remove it, although" ++ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], ++ [ echo "*** The test program failed to compile or link. See the file config.log for the" ++ echo "*** exact error that occurred. This usually means GLib is incorrectly installed."]) ++ CFLAGS="$ac_save_CFLAGS" ++ LIBS="$ac_save_LIBS" ++ fi ++ fi ++ GLIB_CFLAGS="" ++ GLIB_LIBS="" ++ GLIB_GENMARSHAL="" ++ GOBJECT_QUERY="" ++ GLIB_MKENUMS="" ++ GLIB_COMPILE_RESOURCES="" ++ ifelse([$3], , :, [$3]) ++ fi ++ rm -f conf.glibtest ++]) +diff -Naur a/m4macros/glib-2.0.m4 b/m4macros/glib-2.0.m4 +--- a/m4macros/glib-2.0.m4 2021-08-19 21:27:25.805341000 +0600 ++++ b/m4macros/glib-2.0.m4 1970-01-01 06:00:00.000000000 +0600 +@@ -1,215 +0,0 @@ +-# Configure paths for GLIB +-# Owen Taylor 1997-2001 +- +-# Increment this whenever this file is changed. +-#serial 4 +- +-dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) +-dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject, +-dnl gthread, or gio is specified in MODULES, pass to pkg-config +-dnl +-AC_DEFUN([AM_PATH_GLIB_2_0], +-[dnl +-dnl Get the cflags and libraries from pkg-config +-dnl +- +-dnl We can't use PKG_PREREQ because that needs 0.29. +-m4_ifndef([PKG_PROG_PKG_CONFIG], +- [pkg.m4 version 0.28 or later is required]) +- +-AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program], +- , enable_glibtest=yes) +- +- min_glib_version=ifelse([$1], [], [2.0.0], [$1]) +- pkg_config_args="glib-2.0 >= $min_glib_version" +- for module in . $4 +- do +- case "$module" in +- gmodule) +- pkg_config_args="$pkg_config_args gmodule-2.0" +- ;; +- gmodule-no-export) +- pkg_config_args="$pkg_config_args gmodule-no-export-2.0" +- ;; +- gobject) +- pkg_config_args="$pkg_config_args gobject-2.0" +- ;; +- gthread) +- pkg_config_args="$pkg_config_args gthread-2.0" +- ;; +- gio*) +- pkg_config_args="$pkg_config_args $module-2.0" +- ;; +- esac +- done +- +- PKG_PROG_PKG_CONFIG([0.16]) +- +- no_glib="" +- +- if test "x$PKG_CONFIG" = x ; then +- no_glib=yes +- PKG_CONFIG=no +- fi +- +- dnl For GLIB_CFLAGS and GLIB_LIBS +- PKG_CHECK_MODULES([GLIB], [$pkg_config_args], [:], [:]) +- +- dnl For the tools +- PKG_CHECK_VAR([GLIB_GENMARSHAL], [glib-2.0], [glib_genmarshal]) +- PKG_CHECK_VAR([GOBJECT_QUERY], [glib-2.0], [gobject_query]) +- PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.0], [glib_mkenums]) +- PKG_CHECK_VAR([GLIB_COMPILE_RESOURCES], [gio-2.0], [glib_compile_resources]) +- +- AC_MSG_CHECKING(for GLIB - version >= $min_glib_version) +- +- if test x$PKG_CONFIG != xno ; then +- ## don't try to run the test against uninstalled libtool libs +- if $PKG_CONFIG --uninstalled $pkg_config_args; then +- echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH" +- enable_glibtest=no +- fi +- +- if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then +- : +- else +- no_glib=yes +- fi +- fi +- +- if test x"$no_glib" = x ; then +- glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \ +- sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` +- glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \ +- sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` +- glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \ +- sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` +- if test "x$enable_glibtest" = "xyes" ; then +- ac_save_CFLAGS="$CFLAGS" +- ac_save_LIBS="$LIBS" +- CFLAGS="$CFLAGS $GLIB_CFLAGS" +- LIBS="$GLIB_LIBS $LIBS" +-dnl +-dnl Now check if the installed GLib is sufficiently new. (Also sanity +-dnl checks the results of pkg-config to some extent) +-dnl +- rm -f conf.glibtest +- AC_RUN_IFELSE([AC_LANG_SOURCE([[ +-#include +-#include +-#include +- +-int +-main (void) +-{ +- unsigned int major, minor, micro; +- +- fclose (fopen ("conf.glibtest", "w")); +- +- if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, µ) != 3) { +- printf("%s, bad version string\n", "$min_glib_version"); +- exit(1); +- } +- +- if ((glib_major_version != $glib_config_major_version) || +- (glib_minor_version != $glib_config_minor_version) || +- (glib_micro_version != $glib_config_micro_version)) +- { +- printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", +- $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version, +- glib_major_version, glib_minor_version, glib_micro_version); +- printf ("*** was found! If pkg-config was correct, then it is best\n"); +- printf ("*** to remove the old version of GLib. You may also be able to fix the error\n"); +- printf("*** by modifying your LD_LIBRARY_PATH environment variable, or by editing\n"); +- printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); +- printf("*** required on your system.\n"); +- printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); +- printf("*** to point to the correct configuration files\n"); +- } +- else if ((glib_major_version != GLIB_MAJOR_VERSION) || +- (glib_minor_version != GLIB_MINOR_VERSION) || +- (glib_micro_version != GLIB_MICRO_VERSION)) +- { +- printf("*** GLib header files (version %d.%d.%d) do not match\n", +- GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); +- printf("*** library (version %d.%d.%d)\n", +- glib_major_version, glib_minor_version, glib_micro_version); +- } +- else +- { +- if ((glib_major_version > major) || +- ((glib_major_version == major) && (glib_minor_version > minor)) || +- ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro))) +- { +- return 0; +- } +- else +- { +- printf("\n*** An old version of GLib (%u.%u.%u) was found.\n", +- glib_major_version, glib_minor_version, glib_micro_version); +- printf("*** You need a version of GLib newer than %u.%u.%u. The latest version of\n", +- major, minor, micro); +- printf("*** GLib is always available from ftp://ftp.gtk.org.\n"); +- printf("***\n"); +- printf("*** If you have already installed a sufficiently new version, this error\n"); +- printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); +- printf("*** being found. The easiest way to fix this is to remove the old version\n"); +- printf("*** of GLib, but you can also set the PKG_CONFIG environment to point to the\n"); +- printf("*** correct copy of pkg-config. (In this case, you will have to\n"); +- printf("*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf\n"); +- printf("*** so that the correct libraries are found at run-time))\n"); +- } +- } +- return 1; +-} +-]])],[],[no_glib=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +- CFLAGS="$ac_save_CFLAGS" +- LIBS="$ac_save_LIBS" +- fi +- fi +- if test "x$no_glib" = x ; then +- AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)) +- ifelse([$2], , :, [$2]) +- else +- AC_MSG_RESULT(no) +- if test "$PKG_CONFIG" = "no" ; then +- echo "*** A new enough version of pkg-config was not found." +- echo "*** See http://www.freedesktop.org/software/pkgconfig/" +- else +- if test -f conf.glibtest ; then +- : +- else +- echo "*** Could not run GLib test program, checking why..." +- ac_save_CFLAGS="$CFLAGS" +- ac_save_LIBS="$LIBS" +- CFLAGS="$CFLAGS $GLIB_CFLAGS" +- LIBS="$LIBS $GLIB_LIBS" +- AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +-#include +-#include +-]], [[ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ]])], +- [ echo "*** The test program compiled, but did not run. This usually means" +- echo "*** that the run-time linker is not finding GLib or finding the wrong" +- echo "*** version of GLib. If it is not finding GLib, you'll need to set your" +- echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" +- echo "*** to the installed location Also, make sure you have run ldconfig if that" +- echo "*** is required on your system" +- echo "***" +- echo "*** If you have an old version installed, it is best to remove it, although" +- echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], +- [ echo "*** The test program failed to compile or link. See the file config.log for the" +- echo "*** exact error that occurred. This usually means GLib is incorrectly installed."]) +- CFLAGS="$ac_save_CFLAGS" +- LIBS="$ac_save_LIBS" +- fi +- fi +- GLIB_CFLAGS="" +- GLIB_LIBS="" +- GLIB_GENMARSHAL="" +- GOBJECT_QUERY="" +- GLIB_MKENUMS="" +- GLIB_COMPILE_RESOURCES="" +- ifelse([$3], , :, [$3]) +- fi +- rm -f conf.glibtest +-]) +diff -Naur a/m4macros/glib-gettext-2.00.m4 b/m4macros/glib-gettext-2.00.m4 +--- a/m4macros/glib-gettext-2.00.m4 1970-01-01 06:00:00.000000000 +0600 ++++ b/m4macros/glib-gettext-2.00.m4 2021-08-19 21:27:25.805341000 +0600 +@@ -0,0 +1,486 @@ ++# Copyright (C) 1995-2002 Free Software Foundation, Inc. ++# Copyright (C) 2001-2003,2004 Red Hat, Inc. ++# ++# This file is free software, distributed under the terms of the GNU ++# General Public License. As a special exception to the GNU General ++# Public License, this file may be distributed as part of a program ++# that contains a configuration script generated by Autoconf, under ++# the same distribution terms as the rest of that program. ++# ++# This file can be copied and used freely without restrictions. It can ++# be used in projects which are not available under the GNU Public License ++# but which still want to provide support for the GNU gettext functionality. ++# ++# Macro to add for using GNU gettext. ++# Ulrich Drepper , 1995, 1996 ++# ++# Modified to never use included libintl. ++# Owen Taylor , 12/15/1998 ++# ++# Major rework to remove unused code ++# Owen Taylor , 12/11/2002 ++# ++# Added better handling of ALL_LINGUAS from GNU gettext version ++# written by Bruno Haible, Owen Taylor 5/30/3002 ++# ++# Modified to require ngettext ++# Matthias Clasen 08/06/2004 ++ ++# Increment this whenever this file is changed. ++#serial 1 ++ ++# We need this here as well, since someone might use autoconf-2.5x ++# to configure GLib then an older version to configure a package ++# using AM_GLIB_GNU_GETTEXT ++AC_PREREQ(2.53) ++ ++dnl ++dnl We go to great lengths to make sure that aclocal won't ++dnl try to pull in the installed version of these macros ++dnl when running aclocal in the glib directory. ++dnl ++m4_copy([AC_DEFUN],[glib_DEFUN]) ++m4_copy([AC_REQUIRE],[glib_REQUIRE]) ++dnl ++dnl At the end, if we're not within glib, we'll define the public ++dnl definitions in terms of our private definitions. ++dnl ++ ++# GLIB_LC_MESSAGES ++#-------------------- ++glib_DEFUN([GLIB_LC_MESSAGES], ++ [AC_CHECK_HEADERS([locale.h]) ++ if test $ac_cv_header_locale_h = yes; then ++ AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, ++ [AC_TRY_LINK([#include ], [return LC_MESSAGES], ++ am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) ++ if test $am_cv_val_LC_MESSAGES = yes; then ++ AC_DEFINE(HAVE_LC_MESSAGES, 1, ++ [Define if your file defines LC_MESSAGES.]) ++ fi ++ fi]) ++ ++# GLIB_PATH_PROG_WITH_TEST ++#---------------------------- ++dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, ++dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) ++glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], ++[# Extract the first word of "$2", so it can be a program name with args. ++set dummy $2; ac_word=[$]2 ++AC_MSG_CHECKING([for $ac_word]) ++AC_CACHE_VAL(ac_cv_path_$1, ++[case "[$]$1" in ++ /*) ++ ac_cv_path_$1="[$]$1" # Let the user override the test with a path. ++ ;; ++ *) ++ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" ++ for ac_dir in ifelse([$5], , $PATH, [$5]); do ++ test -z "$ac_dir" && ac_dir=. ++ if test -f $ac_dir/$ac_word; then ++ if [$3]; then ++ ac_cv_path_$1="$ac_dir/$ac_word" ++ break ++ fi ++ fi ++ done ++ IFS="$ac_save_ifs" ++dnl If no 4th arg is given, leave the cache variable unset, ++dnl so AC_PATH_PROGS will keep looking. ++ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" ++])dnl ++ ;; ++esac])dnl ++$1="$ac_cv_path_$1" ++if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then ++ AC_MSG_RESULT([$]$1) ++else ++ AC_MSG_RESULT(no) ++fi ++AC_SUBST($1)dnl ++]) ++ ++dnl Checks for special options needed on Mac OS X. ++dnl Defines INTL_MACOSX_LIBS. ++dnl ++dnl Copied from intlmacosx.m4 in gettext, GPL. ++dnl Copyright (C) 2004-2013 Free Software Foundation, Inc. ++glib_DEFUN([glib_gt_INTL_MACOSX], ++[ ++ dnl Check for API introduced in Mac OS X 10.2. ++ AC_CACHE_CHECK([for CFPreferencesCopyAppValue], ++ [gt_cv_func_CFPreferencesCopyAppValue], ++ [gt_save_LIBS="$LIBS" ++ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" ++ AC_LINK_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[#include ]], ++ [[CFPreferencesCopyAppValue(NULL, NULL)]])], ++ [gt_cv_func_CFPreferencesCopyAppValue=yes], ++ [gt_cv_func_CFPreferencesCopyAppValue=no]) ++ LIBS="$gt_save_LIBS"]) ++ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then ++ AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], ++ [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) ++ fi ++ dnl Check for API introduced in Mac OS X 10.3. ++ AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], ++ [gt_save_LIBS="$LIBS" ++ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" ++ AC_LINK_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[#include ]], ++ [[CFLocaleCopyCurrent();]])], ++ [gt_cv_func_CFLocaleCopyCurrent=yes], ++ [gt_cv_func_CFLocaleCopyCurrent=no]) ++ LIBS="$gt_save_LIBS"]) ++ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then ++ AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], ++ [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) ++ fi ++ INTL_MACOSX_LIBS= ++ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then ++ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" ++ fi ++ AC_SUBST([INTL_MACOSX_LIBS]) ++]) ++ ++# GLIB_WITH_NLS ++#----------------- ++glib_DEFUN([GLIB_WITH_NLS], ++ dnl NLS is obligatory ++ [USE_NLS=yes ++ AC_SUBST(USE_NLS) ++ ++ gt_cv_have_gettext=no ++ ++ CATOBJEXT=NONE ++ XGETTEXT=: ++ INTLLIBS= ++ ++ glib_gt_INTL_MACOSX ++ ++ AC_CHECK_HEADER(libintl.h, ++ [gt_cv_func_dgettext_libintl="no" ++ libintl_extra_libs="" ++ ++ # ++ # First check in libc ++ # ++ AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, ++ [AC_TRY_LINK([ ++#include ++], ++ [return !ngettext ("","", 1)], ++ gt_cv_func_ngettext_libc=yes, ++ gt_cv_func_ngettext_libc=no) ++ ]) ++ ++ if test "$gt_cv_func_ngettext_libc" = "yes" ; then ++ AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, ++ [AC_TRY_LINK([ ++#include ++], ++ [return !dgettext ("","")], ++ gt_cv_func_dgettext_libc=yes, ++ gt_cv_func_dgettext_libc=no) ++ ]) ++ fi ++ ++ if test "$gt_cv_func_ngettext_libc" = "yes" ; then ++ AC_CHECK_FUNCS(bind_textdomain_codeset) ++ fi ++ ++ # ++ # If we don't have everything we want, check in libintl ++ # ++ if test "$gt_cv_func_dgettext_libc" != "yes" \ ++ || test "$gt_cv_func_ngettext_libc" != "yes" \ ++ || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then ++ ++ AC_CHECK_LIB(intl, bindtextdomain, ++ [AC_CHECK_LIB(intl, ngettext, ++ [AC_CHECK_LIB(intl, dgettext, ++ gt_cv_func_dgettext_libintl=yes)])]) ++ ++ if test "$gt_cv_func_dgettext_libintl" != "yes" ; then ++ AC_MSG_CHECKING([if -liconv is needed to use gettext]) ++ AC_MSG_RESULT([]) ++ AC_CHECK_LIB(intl, ngettext, ++ [AC_CHECK_LIB(intl, dcgettext, ++ [gt_cv_func_dgettext_libintl=yes ++ libintl_extra_libs=-liconv], ++ :,-liconv)], ++ :,-liconv) ++ fi ++ ++ # ++ # If we found libintl, then check in it for bind_textdomain_codeset(); ++ # we'll prefer libc if neither have bind_textdomain_codeset(), ++ # and both have dgettext and ngettext ++ # ++ if test "$gt_cv_func_dgettext_libintl" = "yes" ; then ++ glib_save_LIBS="$LIBS" ++ LIBS="$LIBS -lintl $libintl_extra_libs" ++ unset ac_cv_func_bind_textdomain_codeset ++ AC_CHECK_FUNCS(bind_textdomain_codeset) ++ LIBS="$glib_save_LIBS" ++ ++ if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then ++ gt_cv_func_dgettext_libc=no ++ else ++ if test "$gt_cv_func_dgettext_libc" = "yes" \ ++ && test "$gt_cv_func_ngettext_libc" = "yes"; then ++ gt_cv_func_dgettext_libintl=no ++ fi ++ fi ++ fi ++ fi ++ ++ if test "$gt_cv_func_dgettext_libc" = "yes" \ ++ || test "$gt_cv_func_dgettext_libintl" = "yes"; then ++ gt_cv_have_gettext=yes ++ fi ++ ++ if test "$gt_cv_func_dgettext_libintl" = "yes"; then ++ INTLLIBS="-lintl $libintl_extra_libs $INTL_MACOSX_LIBS" ++ fi ++ ++ if test "$gt_cv_have_gettext" = "yes"; then ++ AC_DEFINE(HAVE_GETTEXT,1, ++ [Define if the GNU gettext() function is already present or preinstalled.]) ++ GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, ++ [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl ++ if test "$MSGFMT" != "no"; then ++ glib_save_LIBS="$LIBS" ++ LIBS="$LIBS $INTLLIBS" ++ AC_CHECK_FUNCS(dcgettext) ++ MSGFMT_OPTS= ++ AC_MSG_CHECKING([if msgfmt accepts -c]) ++ GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[ ++msgid "" ++msgstr "" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Project-Id-Version: test 1.0\n" ++"PO-Revision-Date: 2007-02-15 12:01+0100\n" ++"Last-Translator: test \n" ++"Language-Team: C \n" ++"MIME-Version: 1.0\n" ++"Content-Transfer-Encoding: 8bit\n" ++], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) ++ AC_SUBST(MSGFMT_OPTS) ++ AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) ++ GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, ++ [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) ++ AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; ++ return _nl_msg_cat_cntr], ++ [CATOBJEXT=.gmo ++ DATADIRNAME=share], ++ [case $host in ++ *-*-solaris*) ++ dnl On Solaris, if bind_textdomain_codeset is in libc, ++ dnl GNU format message catalog is always supported, ++ dnl since both are added to the libc all together. ++ dnl Hence, we'd like to go with DATADIRNAME=share and ++ dnl and CATOBJEXT=.gmo in this case. ++ AC_CHECK_FUNC(bind_textdomain_codeset, ++ [CATOBJEXT=.gmo ++ DATADIRNAME=share], ++ [CATOBJEXT=.mo ++ DATADIRNAME=lib]) ++ ;; ++ *-*-openbsd*) ++ CATOBJEXT=.mo ++ DATADIRNAME=share ++ ;; ++ *) ++ CATOBJEXT=.mo ++ DATADIRNAME=lib ++ ;; ++ esac]) ++ LIBS="$glib_save_LIBS" ++ INSTOBJEXT=.mo ++ else ++ gt_cv_have_gettext=no ++ fi ++ fi ++ ]) ++ ++ if test "$gt_cv_have_gettext" = "yes" ; then ++ AC_DEFINE(ENABLE_NLS, 1, ++ [always defined to indicate that i18n is enabled]) ++ fi ++ ++ dnl Test whether we really found GNU xgettext. ++ if test "$XGETTEXT" != ":"; then ++ dnl If it is not GNU xgettext we define it as : so that the ++ dnl Makefiles still can work. ++ if $XGETTEXT --omit-header /dev/null 2> /dev/null; then ++ : ; ++ else ++ AC_MSG_RESULT( ++ [found xgettext program is not GNU xgettext; ignore it]) ++ XGETTEXT=":" ++ fi ++ fi ++ ++ # We need to process the po/ directory. ++ POSUB=po ++ ++ AC_OUTPUT_COMMANDS( ++ [case "$CONFIG_FILES" in *po/Makefile.in*) ++ sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile ++ esac]) ++ ++ dnl These rules are solely for the distribution goal. While doing this ++ dnl we only have to keep exactly one list of the available catalogs ++ dnl in configure.ac. ++ for lang in $ALL_LINGUAS; do ++ GMOFILES="$GMOFILES $lang.gmo" ++ POFILES="$POFILES $lang.po" ++ done ++ ++ dnl Make all variables we use known to autoconf. ++ AC_SUBST(CATALOGS) ++ AC_SUBST(CATOBJEXT) ++ AC_SUBST(DATADIRNAME) ++ AC_SUBST(GMOFILES) ++ AC_SUBST(INSTOBJEXT) ++ AC_SUBST(INTLLIBS) ++ AC_SUBST(PO_IN_DATADIR_TRUE) ++ AC_SUBST(PO_IN_DATADIR_FALSE) ++ AC_SUBST(POFILES) ++ AC_SUBST(POSUB) ++ ]) ++ ++# AM_GLIB_GNU_GETTEXT ++# ------------------- ++# Do checks necessary for use of gettext. If a suitable implementation ++# of gettext is found in either in libintl or in the C library, ++# it will set INTLLIBS to the libraries needed for use of gettext ++# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable ++# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() ++# on various variables needed by the Makefile.in.in installed by ++# glib-gettextize. ++dnl ++AU_DEFUN([GLIB_GNU_GETTEXT], ++ [AC_REQUIRE([AC_PROG_CC])dnl ++ ++ GLIB_LC_MESSAGES ++ GLIB_WITH_NLS ++ ++ if test "$gt_cv_have_gettext" = "yes"; then ++ if test "x$ALL_LINGUAS" = "x"; then ++ LINGUAS= ++ else ++ AC_MSG_CHECKING(for catalogs to be installed) ++ NEW_LINGUAS= ++ for presentlang in $ALL_LINGUAS; do ++ useit=no ++ if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then ++ desiredlanguages="$LINGUAS" ++ else ++ desiredlanguages="$ALL_LINGUAS" ++ fi ++ for desiredlang in $desiredlanguages; do ++ # Use the presentlang catalog if desiredlang is ++ # a. equal to presentlang, or ++ # b. a variant of presentlang (because in this case, ++ # presentlang can be used as a fallback for messages ++ # which are not translated in the desiredlang catalog). ++ case "$desiredlang" in ++ "$presentlang"*) useit=yes;; ++ esac ++ done ++ if test $useit = yes; then ++ NEW_LINGUAS="$NEW_LINGUAS $presentlang" ++ fi ++ done ++ LINGUAS=$NEW_LINGUAS ++ AC_MSG_RESULT($LINGUAS) ++ fi ++ ++ dnl Construct list of names of catalog files to be constructed. ++ if test -n "$LINGUAS"; then ++ for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done ++ fi ++ fi ++ ++ dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly ++ dnl find the mkinstalldirs script in another subdir but ($top_srcdir). ++ dnl Try to locate is. ++ MKINSTALLDIRS= ++ if test -n "$ac_aux_dir"; then ++ MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" ++ fi ++ if test -z "$MKINSTALLDIRS"; then ++ MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" ++ fi ++ AC_SUBST(MKINSTALLDIRS) ++ ++ dnl Generate list of files to be processed by xgettext which will ++ dnl be included in po/Makefile. ++ test -d po || mkdir po ++ if test "x$srcdir" != "x."; then ++ if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then ++ posrcprefix="$srcdir/" ++ else ++ posrcprefix="../$srcdir/" ++ fi ++ else ++ posrcprefix="../" ++ fi ++ rm -f po/POTFILES ++ sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ ++ < $srcdir/po/POTFILES.in > po/POTFILES ++ ], ++ [[$0: This macro is deprecated. You should use upstream gettext instead.]]) ++ ++# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) ++# ------------------------------- ++# Define VARIABLE to the location where catalog files will ++# be installed by po/Makefile. ++glib_DEFUN([GLIB_DEFINE_LOCALEDIR], ++[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl ++glib_save_prefix="$prefix" ++glib_save_exec_prefix="$exec_prefix" ++glib_save_datarootdir="$datarootdir" ++test "x$prefix" = xNONE && prefix=$ac_default_prefix ++test "x$exec_prefix" = xNONE && exec_prefix=$prefix ++datarootdir=`eval echo "${datarootdir}"` ++if test "x$CATOBJEXT" = "x.mo" ; then ++ localedir=`eval echo "${libdir}/locale"` ++else ++ localedir=`eval echo "${datadir}/locale"` ++fi ++prefix="$glib_save_prefix" ++exec_prefix="$glib_save_exec_prefix" ++datarootdir="$glib_save_datarootdir" ++AC_DEFINE_UNQUOTED($1, "$localedir", ++ [Define the location where the catalogs will be installed]) ++]) ++ ++dnl ++dnl Now the definitions that aclocal will find ++dnl ++ifdef(glib_configure_ac,[],[ ++AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) ++AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) ++])dnl ++ ++# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL]) ++# ++# Create a temporary file with TEST-FILE as its contents and pass the ++# file name to PROGRAM. Perform ACTION-IF-PASS if PROGRAM exits with ++# 0 and perform ACTION-IF-FAIL for any other exit status. ++AC_DEFUN([GLIB_RUN_PROG], ++[cat >conftest.foo <<_ACEOF ++$2 ++_ACEOF ++if AC_RUN_LOG([$1 conftest.foo]); then ++ m4_ifval([$3], [$3], [:]) ++m4_ifvaln([$4], [else $4])dnl ++echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD ++sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD ++fi]) ++ +diff -Naur a/m4macros/glib-gettext.m4 b/m4macros/glib-gettext.m4 +--- a/m4macros/glib-gettext.m4 2021-08-19 21:27:25.805341000 +0600 ++++ b/m4macros/glib-gettext.m4 1970-01-01 06:00:00.000000000 +0600 +@@ -1,486 +0,0 @@ +-# Copyright (C) 1995-2002 Free Software Foundation, Inc. +-# Copyright (C) 2001-2003,2004 Red Hat, Inc. +-# +-# This file is free software, distributed under the terms of the GNU +-# General Public License. As a special exception to the GNU General +-# Public License, this file may be distributed as part of a program +-# that contains a configuration script generated by Autoconf, under +-# the same distribution terms as the rest of that program. +-# +-# This file can be copied and used freely without restrictions. It can +-# be used in projects which are not available under the GNU Public License +-# but which still want to provide support for the GNU gettext functionality. +-# +-# Macro to add for using GNU gettext. +-# Ulrich Drepper , 1995, 1996 +-# +-# Modified to never use included libintl. +-# Owen Taylor , 12/15/1998 +-# +-# Major rework to remove unused code +-# Owen Taylor , 12/11/2002 +-# +-# Added better handling of ALL_LINGUAS from GNU gettext version +-# written by Bruno Haible, Owen Taylor 5/30/3002 +-# +-# Modified to require ngettext +-# Matthias Clasen 08/06/2004 +- +-# Increment this whenever this file is changed. +-#serial 1 +- +-# We need this here as well, since someone might use autoconf-2.5x +-# to configure GLib then an older version to configure a package +-# using AM_GLIB_GNU_GETTEXT +-AC_PREREQ(2.53) +- +-dnl +-dnl We go to great lengths to make sure that aclocal won't +-dnl try to pull in the installed version of these macros +-dnl when running aclocal in the glib directory. +-dnl +-m4_copy([AC_DEFUN],[glib_DEFUN]) +-m4_copy([AC_REQUIRE],[glib_REQUIRE]) +-dnl +-dnl At the end, if we're not within glib, we'll define the public +-dnl definitions in terms of our private definitions. +-dnl +- +-# GLIB_LC_MESSAGES +-#-------------------- +-glib_DEFUN([GLIB_LC_MESSAGES], +- [AC_CHECK_HEADERS([locale.h]) +- if test $ac_cv_header_locale_h = yes; then +- AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, +- [AC_TRY_LINK([#include ], [return LC_MESSAGES], +- am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) +- if test $am_cv_val_LC_MESSAGES = yes; then +- AC_DEFINE(HAVE_LC_MESSAGES, 1, +- [Define if your file defines LC_MESSAGES.]) +- fi +- fi]) +- +-# GLIB_PATH_PROG_WITH_TEST +-#---------------------------- +-dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +-dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +-glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], +-[# Extract the first word of "$2", so it can be a program name with args. +-set dummy $2; ac_word=[$]2 +-AC_MSG_CHECKING([for $ac_word]) +-AC_CACHE_VAL(ac_cv_path_$1, +-[case "[$]$1" in +- /*) +- ac_cv_path_$1="[$]$1" # Let the user override the test with a path. +- ;; +- *) +- IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" +- for ac_dir in ifelse([$5], , $PATH, [$5]); do +- test -z "$ac_dir" && ac_dir=. +- if test -f $ac_dir/$ac_word; then +- if [$3]; then +- ac_cv_path_$1="$ac_dir/$ac_word" +- break +- fi +- fi +- done +- IFS="$ac_save_ifs" +-dnl If no 4th arg is given, leave the cache variable unset, +-dnl so AC_PATH_PROGS will keep looking. +-ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +-])dnl +- ;; +-esac])dnl +-$1="$ac_cv_path_$1" +-if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then +- AC_MSG_RESULT([$]$1) +-else +- AC_MSG_RESULT(no) +-fi +-AC_SUBST($1)dnl +-]) +- +-dnl Checks for special options needed on Mac OS X. +-dnl Defines INTL_MACOSX_LIBS. +-dnl +-dnl Copied from intlmacosx.m4 in gettext, GPL. +-dnl Copyright (C) 2004-2013 Free Software Foundation, Inc. +-glib_DEFUN([glib_gt_INTL_MACOSX], +-[ +- dnl Check for API introduced in Mac OS X 10.2. +- AC_CACHE_CHECK([for CFPreferencesCopyAppValue], +- [gt_cv_func_CFPreferencesCopyAppValue], +- [gt_save_LIBS="$LIBS" +- LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" +- AC_LINK_IFELSE( +- [AC_LANG_PROGRAM( +- [[#include ]], +- [[CFPreferencesCopyAppValue(NULL, NULL)]])], +- [gt_cv_func_CFPreferencesCopyAppValue=yes], +- [gt_cv_func_CFPreferencesCopyAppValue=no]) +- LIBS="$gt_save_LIBS"]) +- if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then +- AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], +- [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) +- fi +- dnl Check for API introduced in Mac OS X 10.3. +- AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], +- [gt_save_LIBS="$LIBS" +- LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" +- AC_LINK_IFELSE( +- [AC_LANG_PROGRAM( +- [[#include ]], +- [[CFLocaleCopyCurrent();]])], +- [gt_cv_func_CFLocaleCopyCurrent=yes], +- [gt_cv_func_CFLocaleCopyCurrent=no]) +- LIBS="$gt_save_LIBS"]) +- if test $gt_cv_func_CFLocaleCopyCurrent = yes; then +- AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], +- [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) +- fi +- INTL_MACOSX_LIBS= +- if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then +- INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" +- fi +- AC_SUBST([INTL_MACOSX_LIBS]) +-]) +- +-# GLIB_WITH_NLS +-#----------------- +-glib_DEFUN([GLIB_WITH_NLS], +- dnl NLS is obligatory +- [USE_NLS=yes +- AC_SUBST(USE_NLS) +- +- gt_cv_have_gettext=no +- +- CATOBJEXT=NONE +- XGETTEXT=: +- INTLLIBS= +- +- glib_gt_INTL_MACOSX +- +- AC_CHECK_HEADER(libintl.h, +- [gt_cv_func_dgettext_libintl="no" +- libintl_extra_libs="" +- +- # +- # First check in libc +- # +- AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, +- [AC_TRY_LINK([ +-#include +-], +- [return !ngettext ("","", 1)], +- gt_cv_func_ngettext_libc=yes, +- gt_cv_func_ngettext_libc=no) +- ]) +- +- if test "$gt_cv_func_ngettext_libc" = "yes" ; then +- AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, +- [AC_TRY_LINK([ +-#include +-], +- [return !dgettext ("","")], +- gt_cv_func_dgettext_libc=yes, +- gt_cv_func_dgettext_libc=no) +- ]) +- fi +- +- if test "$gt_cv_func_ngettext_libc" = "yes" ; then +- AC_CHECK_FUNCS(bind_textdomain_codeset) +- fi +- +- # +- # If we don't have everything we want, check in libintl +- # +- if test "$gt_cv_func_dgettext_libc" != "yes" \ +- || test "$gt_cv_func_ngettext_libc" != "yes" \ +- || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then +- +- AC_CHECK_LIB(intl, bindtextdomain, +- [AC_CHECK_LIB(intl, ngettext, +- [AC_CHECK_LIB(intl, dgettext, +- gt_cv_func_dgettext_libintl=yes)])]) +- +- if test "$gt_cv_func_dgettext_libintl" != "yes" ; then +- AC_MSG_CHECKING([if -liconv is needed to use gettext]) +- AC_MSG_RESULT([]) +- AC_CHECK_LIB(intl, ngettext, +- [AC_CHECK_LIB(intl, dcgettext, +- [gt_cv_func_dgettext_libintl=yes +- libintl_extra_libs=-liconv], +- :,-liconv)], +- :,-liconv) +- fi +- +- # +- # If we found libintl, then check in it for bind_textdomain_codeset(); +- # we'll prefer libc if neither have bind_textdomain_codeset(), +- # and both have dgettext and ngettext +- # +- if test "$gt_cv_func_dgettext_libintl" = "yes" ; then +- glib_save_LIBS="$LIBS" +- LIBS="$LIBS -lintl $libintl_extra_libs" +- unset ac_cv_func_bind_textdomain_codeset +- AC_CHECK_FUNCS(bind_textdomain_codeset) +- LIBS="$glib_save_LIBS" +- +- if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then +- gt_cv_func_dgettext_libc=no +- else +- if test "$gt_cv_func_dgettext_libc" = "yes" \ +- && test "$gt_cv_func_ngettext_libc" = "yes"; then +- gt_cv_func_dgettext_libintl=no +- fi +- fi +- fi +- fi +- +- if test "$gt_cv_func_dgettext_libc" = "yes" \ +- || test "$gt_cv_func_dgettext_libintl" = "yes"; then +- gt_cv_have_gettext=yes +- fi +- +- if test "$gt_cv_func_dgettext_libintl" = "yes"; then +- INTLLIBS="-lintl $libintl_extra_libs $INTL_MACOSX_LIBS" +- fi +- +- if test "$gt_cv_have_gettext" = "yes"; then +- AC_DEFINE(HAVE_GETTEXT,1, +- [Define if the GNU gettext() function is already present or preinstalled.]) +- GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, +- [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl +- if test "$MSGFMT" != "no"; then +- glib_save_LIBS="$LIBS" +- LIBS="$LIBS $INTLLIBS" +- AC_CHECK_FUNCS(dcgettext) +- MSGFMT_OPTS= +- AC_MSG_CHECKING([if msgfmt accepts -c]) +- GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[ +-msgid "" +-msgstr "" +-"Content-Type: text/plain; charset=UTF-8\n" +-"Project-Id-Version: test 1.0\n" +-"PO-Revision-Date: 2007-02-15 12:01+0100\n" +-"Last-Translator: test \n" +-"Language-Team: C \n" +-"MIME-Version: 1.0\n" +-"Content-Transfer-Encoding: 8bit\n" +-], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) +- AC_SUBST(MSGFMT_OPTS) +- AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) +- GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, +- [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) +- AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; +- return _nl_msg_cat_cntr], +- [CATOBJEXT=.gmo +- DATADIRNAME=share], +- [case $host in +- *-*-solaris*) +- dnl On Solaris, if bind_textdomain_codeset is in libc, +- dnl GNU format message catalog is always supported, +- dnl since both are added to the libc all together. +- dnl Hence, we'd like to go with DATADIRNAME=share and +- dnl and CATOBJEXT=.gmo in this case. +- AC_CHECK_FUNC(bind_textdomain_codeset, +- [CATOBJEXT=.gmo +- DATADIRNAME=share], +- [CATOBJEXT=.mo +- DATADIRNAME=lib]) +- ;; +- *-*-openbsd*) +- CATOBJEXT=.mo +- DATADIRNAME=share +- ;; +- *) +- CATOBJEXT=.mo +- DATADIRNAME=lib +- ;; +- esac]) +- LIBS="$glib_save_LIBS" +- INSTOBJEXT=.mo +- else +- gt_cv_have_gettext=no +- fi +- fi +- ]) +- +- if test "$gt_cv_have_gettext" = "yes" ; then +- AC_DEFINE(ENABLE_NLS, 1, +- [always defined to indicate that i18n is enabled]) +- fi +- +- dnl Test whether we really found GNU xgettext. +- if test "$XGETTEXT" != ":"; then +- dnl If it is not GNU xgettext we define it as : so that the +- dnl Makefiles still can work. +- if $XGETTEXT --omit-header /dev/null 2> /dev/null; then +- : ; +- else +- AC_MSG_RESULT( +- [found xgettext program is not GNU xgettext; ignore it]) +- XGETTEXT=":" +- fi +- fi +- +- # We need to process the po/ directory. +- POSUB=po +- +- AC_OUTPUT_COMMANDS( +- [case "$CONFIG_FILES" in *po/Makefile.in*) +- sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile +- esac]) +- +- dnl These rules are solely for the distribution goal. While doing this +- dnl we only have to keep exactly one list of the available catalogs +- dnl in configure.ac. +- for lang in $ALL_LINGUAS; do +- GMOFILES="$GMOFILES $lang.gmo" +- POFILES="$POFILES $lang.po" +- done +- +- dnl Make all variables we use known to autoconf. +- AC_SUBST(CATALOGS) +- AC_SUBST(CATOBJEXT) +- AC_SUBST(DATADIRNAME) +- AC_SUBST(GMOFILES) +- AC_SUBST(INSTOBJEXT) +- AC_SUBST(INTLLIBS) +- AC_SUBST(PO_IN_DATADIR_TRUE) +- AC_SUBST(PO_IN_DATADIR_FALSE) +- AC_SUBST(POFILES) +- AC_SUBST(POSUB) +- ]) +- +-# AM_GLIB_GNU_GETTEXT +-# ------------------- +-# Do checks necessary for use of gettext. If a suitable implementation +-# of gettext is found in either in libintl or in the C library, +-# it will set INTLLIBS to the libraries needed for use of gettext +-# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable +-# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() +-# on various variables needed by the Makefile.in.in installed by +-# glib-gettextize. +-dnl +-AU_DEFUN([GLIB_GNU_GETTEXT], +- [AC_REQUIRE([AC_PROG_CC])dnl +- +- GLIB_LC_MESSAGES +- GLIB_WITH_NLS +- +- if test "$gt_cv_have_gettext" = "yes"; then +- if test "x$ALL_LINGUAS" = "x"; then +- LINGUAS= +- else +- AC_MSG_CHECKING(for catalogs to be installed) +- NEW_LINGUAS= +- for presentlang in $ALL_LINGUAS; do +- useit=no +- if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then +- desiredlanguages="$LINGUAS" +- else +- desiredlanguages="$ALL_LINGUAS" +- fi +- for desiredlang in $desiredlanguages; do +- # Use the presentlang catalog if desiredlang is +- # a. equal to presentlang, or +- # b. a variant of presentlang (because in this case, +- # presentlang can be used as a fallback for messages +- # which are not translated in the desiredlang catalog). +- case "$desiredlang" in +- "$presentlang"*) useit=yes;; +- esac +- done +- if test $useit = yes; then +- NEW_LINGUAS="$NEW_LINGUAS $presentlang" +- fi +- done +- LINGUAS=$NEW_LINGUAS +- AC_MSG_RESULT($LINGUAS) +- fi +- +- dnl Construct list of names of catalog files to be constructed. +- if test -n "$LINGUAS"; then +- for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done +- fi +- fi +- +- dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly +- dnl find the mkinstalldirs script in another subdir but ($top_srcdir). +- dnl Try to locate is. +- MKINSTALLDIRS= +- if test -n "$ac_aux_dir"; then +- MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" +- fi +- if test -z "$MKINSTALLDIRS"; then +- MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" +- fi +- AC_SUBST(MKINSTALLDIRS) +- +- dnl Generate list of files to be processed by xgettext which will +- dnl be included in po/Makefile. +- test -d po || mkdir po +- if test "x$srcdir" != "x."; then +- if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then +- posrcprefix="$srcdir/" +- else +- posrcprefix="../$srcdir/" +- fi +- else +- posrcprefix="../" +- fi +- rm -f po/POTFILES +- sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ +- < $srcdir/po/POTFILES.in > po/POTFILES +- ], +- [[$0: This macro is deprecated. You should use upstream gettext instead.]]) +- +-# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) +-# ------------------------------- +-# Define VARIABLE to the location where catalog files will +-# be installed by po/Makefile. +-glib_DEFUN([GLIB_DEFINE_LOCALEDIR], +-[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl +-glib_save_prefix="$prefix" +-glib_save_exec_prefix="$exec_prefix" +-glib_save_datarootdir="$datarootdir" +-test "x$prefix" = xNONE && prefix=$ac_default_prefix +-test "x$exec_prefix" = xNONE && exec_prefix=$prefix +-datarootdir=`eval echo "${datarootdir}"` +-if test "x$CATOBJEXT" = "x.mo" ; then +- localedir=`eval echo "${libdir}/locale"` +-else +- localedir=`eval echo "${datadir}/locale"` +-fi +-prefix="$glib_save_prefix" +-exec_prefix="$glib_save_exec_prefix" +-datarootdir="$glib_save_datarootdir" +-AC_DEFINE_UNQUOTED($1, "$localedir", +- [Define the location where the catalogs will be installed]) +-]) +- +-dnl +-dnl Now the definitions that aclocal will find +-dnl +-ifdef(glib_configure_ac,[],[ +-AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) +-AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) +-])dnl +- +-# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL]) +-# +-# Create a temporary file with TEST-FILE as its contents and pass the +-# file name to PROGRAM. Perform ACTION-IF-PASS if PROGRAM exits with +-# 0 and perform ACTION-IF-FAIL for any other exit status. +-AC_DEFUN([GLIB_RUN_PROG], +-[cat >conftest.foo <<_ACEOF +-$2 +-_ACEOF +-if AC_RUN_LOG([$1 conftest.foo]); then +- m4_ifval([$3], [$3], [:]) +-m4_ifvaln([$4], [else $4])dnl +-echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD +-sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD +-fi]) +- +diff -Naur a/m4macros/gsettings-2.00.m4 b/m4macros/gsettings-2.00.m4 +--- a/m4macros/gsettings-2.00.m4 1970-01-01 06:00:00.000000000 +0600 ++++ b/m4macros/gsettings-2.00.m4 2024-06-20 09:44:11.288183349 +0600 +@@ -0,0 +1,88 @@ ++# Increment this whenever this file is changed. ++#serial 2 ++ ++dnl GLIB_GSETTINGS ++dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether ++dnl the schema should be compiled ++dnl ++ ++AC_DEFUN([GLIB_GSETTINGS], ++[ ++ dnl We can't use PKG_PREREQ because that needs 0.29. ++ m4_ifndef([PKG_PROG_PKG_CONFIG], ++ [pkg.m4 version 0.28 or later is required]) ++ ++ m4_pattern_allow([AM_V_GEN]) ++ AC_ARG_ENABLE(schemas-compile, ++ AS_HELP_STRING([--disable-schemas-compile], ++ [Disable regeneration of gschemas.compiled on install]), ++ [case ${enableval} in ++ yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE="" ;; ++ no) GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;; ++ *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;; ++ esac]) ++ AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE]) ++ PKG_PROG_PKG_CONFIG([0.16]) ++ AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.00/schemas]) ++ AS_IF([test x$cross_compiling != xyes], ++ [PKG_CHECK_VAR([GLIB_COMPILE_SCHEMAS], [gio-2.00], [glib_compile_schemas])], ++ [AC_PATH_PROG([GLIB_COMPILE_SCHEMAS], [glib-compile-schemas])]) ++ AC_SUBST(GLIB_COMPILE_SCHEMAS) ++ if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then ++ ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2]) ++ else ++ ifelse([$1],,[:],[$1]) ++ fi ++ ++ GSETTINGS_RULES=' ++.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas ++ ++mostlyclean-am: clean-gsettings-schemas ++ ++gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE)) ++ ++%.gschema.valid: %.gschema.xml $(gsettings__enum_file) ++ $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@ ++ ++all-am: $(gsettings_SCHEMAS:.xml=.valid) ++uninstall-am: uninstall-gsettings-schemas ++install-data-am: install-gsettings-schemas ++ ++.SECONDARY: $(gsettings_SCHEMAS) ++ ++install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file) ++ @$(NORMAL_INSTALL) ++ if test -n "$^"; then \ ++ test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \ ++ $(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \ ++ test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \ ++ fi ++ ++uninstall-gsettings-schemas: ++ @$(NORMAL_UNINSTALL) ++ @list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \ ++ files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \ ++ test -n "$$files" || exit 0; \ ++ echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \ ++ cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files ++ test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir) ++ ++clean-gsettings-schemas: ++ rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file) ++ ++ifdef gsettings_ENUM_NAMESPACE ++$(gsettings__enum_file): $(gsettings_ENUM_FILES) ++ $(AM_V_GEN) glib-mkenums --comments '\'''\'' --fhead "" --vhead " <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod " " --vtail " " --ftail "" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@ ++endif ++' ++ _GSETTINGS_SUBST(GSETTINGS_RULES) ++]) ++ ++dnl _GSETTINGS_SUBST(VARIABLE) ++dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST ++AC_DEFUN([_GSETTINGS_SUBST], ++[ ++AC_SUBST([$1]) ++m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) ++] ++) +diff -Naur a/m4macros/gsettings.m4 b/m4macros/gsettings.m4 +--- a/m4macros/gsettings.m4 2021-08-19 21:27:25.805341000 +0600 ++++ b/m4macros/gsettings.m4 1970-01-01 06:00:00.000000000 +0600 +@@ -1,88 +0,0 @@ +-# Increment this whenever this file is changed. +-#serial 2 +- +-dnl GLIB_GSETTINGS +-dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether +-dnl the schema should be compiled +-dnl +- +-AC_DEFUN([GLIB_GSETTINGS], +-[ +- dnl We can't use PKG_PREREQ because that needs 0.29. +- m4_ifndef([PKG_PROG_PKG_CONFIG], +- [pkg.m4 version 0.28 or later is required]) +- +- m4_pattern_allow([AM_V_GEN]) +- AC_ARG_ENABLE(schemas-compile, +- AS_HELP_STRING([--disable-schemas-compile], +- [Disable regeneration of gschemas.compiled on install]), +- [case ${enableval} in +- yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE="" ;; +- no) GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;; +- *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;; +- esac]) +- AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE]) +- PKG_PROG_PKG_CONFIG([0.16]) +- AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.0/schemas]) +- AS_IF([test x$cross_compiling != xyes], +- [PKG_CHECK_VAR([GLIB_COMPILE_SCHEMAS], [gio-2.0], [glib_compile_schemas])], +- [AC_PATH_PROG([GLIB_COMPILE_SCHEMAS], [glib-compile-schemas])]) +- AC_SUBST(GLIB_COMPILE_SCHEMAS) +- if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then +- ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2]) +- else +- ifelse([$1],,[:],[$1]) +- fi +- +- GSETTINGS_RULES=' +-.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas +- +-mostlyclean-am: clean-gsettings-schemas +- +-gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE)) +- +-%.gschema.valid: %.gschema.xml $(gsettings__enum_file) +- $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@ +- +-all-am: $(gsettings_SCHEMAS:.xml=.valid) +-uninstall-am: uninstall-gsettings-schemas +-install-data-am: install-gsettings-schemas +- +-.SECONDARY: $(gsettings_SCHEMAS) +- +-install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file) +- @$(NORMAL_INSTALL) +- if test -n "$^"; then \ +- test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \ +- $(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \ +- test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \ +- fi +- +-uninstall-gsettings-schemas: +- @$(NORMAL_UNINSTALL) +- @list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \ +- files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \ +- test -n "$$files" || exit 0; \ +- echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \ +- cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files +- test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir) +- +-clean-gsettings-schemas: +- rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file) +- +-ifdef gsettings_ENUM_NAMESPACE +-$(gsettings__enum_file): $(gsettings_ENUM_FILES) +- $(AM_V_GEN) glib-mkenums --comments '\'''\'' --fhead "" --vhead " <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod " " --vtail " " --ftail "" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@ +-endif +-' +- _GSETTINGS_SUBST(GSETTINGS_RULES) +-]) +- +-dnl _GSETTINGS_SUBST(VARIABLE) +-dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST +-AC_DEFUN([_GSETTINGS_SUBST], +-[ +-AC_SUBST([$1]) +-m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) +-] +-) +diff -Naur a/meson.build b/meson.build +--- a/meson.build 2021-08-19 21:27:25.806341000 +0600 ++++ b/meson.build 2024-06-20 10:30:45.898377006 +0600 +@@ -71,7 +71,7 @@ + glib_libdir = join_paths(glib_prefix, get_option('libdir')) + glib_libexecdir = join_paths(glib_prefix, get_option('libexecdir')) + glib_datadir = join_paths(glib_prefix, get_option('datadir')) +-glib_pkgdatadir = join_paths(glib_datadir, 'glib-2.0') ++glib_pkgdatadir = join_paths(glib_datadir, 'glib-2.00') + glib_includedir = join_paths(glib_prefix, get_option('includedir')) + if get_option('gio_module_dir') != '' + glib_giomodulesdir = join_paths(glib_prefix, get_option('gio_module_dir')) +@@ -129,10 +129,10 @@ + glib_conf.set('GLIB_MICRO_VERSION', micro_version) + glib_conf.set('GLIB_INTERFACE_AGE', interface_age) + glib_conf.set('GLIB_BINARY_AGE', binary_age) +-glib_conf.set_quoted('GETTEXT_PACKAGE', 'glib20') ++glib_conf.set_quoted('GETTEXT_PACKAGE', 'glib200') + glib_conf.set_quoted('PACKAGE_BUGREPORT', 'https://gitlab.gnome.org/GNOME/glib/issues/new') + glib_conf.set_quoted('PACKAGE_NAME', 'glib') +-glib_conf.set_quoted('PACKAGE_STRING', 'glib @0@'.format(meson.project_version())) ++glib_conf.set_quoted('PACKAGE_STRING', 'glib @00@'.format(meson.project_version())) + glib_conf.set_quoted('PACKAGE_TARNAME', 'glib') + glib_conf.set_quoted('PACKAGE_URL', '') + glib_conf.set_quoted('PACKAGE_VERSION', meson.project_version()) +@@ -2356,20 +2356,20 @@ + endif + + # Install m4 macros that other projects use +-install_data('m4macros/glib-2.0.m4', 'm4macros/glib-gettext.m4', 'm4macros/gsettings.m4', ++install_data('m4macros/glib-2.00.m4', 'm4macros/glib-gettext-2.00.m4', 'm4macros/gsettings-2.00.m4', + install_dir : join_paths(get_option('datadir'), 'aclocal')) + + if host_system != 'windows' + # Install Valgrind suppression file (except on Windows, + # as Valgrind is currently not supported on Windows) + install_data('glib.supp', +- install_dir : join_paths(get_option('datadir'), 'glib-2.0', 'valgrind')) ++ install_dir : join_paths(get_option('datadir'), 'glib-2.00', 'valgrind')) + endif + + configure_file(output : 'config.h', configuration : glib_conf) + + if host_system == 'windows' +- install_headers([ 'msvc_recommended_pragmas.h' ], subdir : 'glib-2.0') ++ install_headers([ 'msvc_recommended_pragmas.h' ], subdir : 'glib-2.00') + endif + + if get_option('man') +diff -Naur a/po/Makefile.in.in b/po/Makefile.in.in +--- a/po/Makefile.in.in 2021-08-19 21:27:25.806341000 +0600 ++++ b/po/Makefile.in.in 2024-06-20 09:44:43.530977685 +0600 +@@ -30,7 +30,7 @@ + libdir = @libdir@ + localedir = $(libdir)/locale + gnulocaledir = $(datadir)/locale +-gettextsrcdir = $(datadir)/glib-2.0/gettext/po ++gettextsrcdir = $(datadir)/glib-2.00/gettext/po + subdir = po + + INSTALL = @INSTALL@ +diff -Naur a/po/meson.build b/po/meson.build +--- a/po/meson.build 2021-08-19 21:27:25.851341000 +0600 ++++ b/po/meson.build 2024-06-20 09:44:23.055108292 +0600 +@@ -1,5 +1,5 @@ + i18n = import('i18n') + +-i18n.gettext('glib20', preset: 'glib') ++i18n.gettext('glib200', preset: 'glib') + + install_data('Makefile.in.in', install_dir : glib_pkgdatadir + '/gettext/po') diff --git a/base/rx/rx-glib2/glib2.spec b/base/rx/rx-glib2/glib2.spec new file mode 100644 index 0000000..e6266cf --- /dev/null +++ b/base/rx/rx-glib2/glib2.spec @@ -0,0 +1,1012 @@ +%bcond_without sysprof + +Name: glib2.00 +Version: 2.68.4 +Release: 15%{?dist} +Summary: A library of handy utility functions + +License: LGPLv2+ +URL: http://www.gtk.org +Source0: http://download.gnome.org/sources/glib/2.68/glib-%{version}.tar.xz + +# Required for RHEL core crypto components policy. Good for Fedora too. +# https://bugzilla.redhat.com/show_bug.cgi?id=1630260 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 +Patch0: gnutls-hmac.patch + +# Add GPowerProfileMonitor +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1965 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2194 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2222 +Patch1: 1965.patch +Patch2: 2194.patch +Patch3: 2222.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2244 +Patch4: 2244.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2291 +Patch5: 2291.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1968 +Patch6: 1968.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2435 +Patch7: 2435.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3126 +Patch8: 3126.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3136 +Patch9: 3136.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3163 +Patch10: 3163.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2826 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3272 +Patch11: 2826.patch +Patch12: 3272.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2408 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2816 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2847 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3158 +Patch13: 2408.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3353 +Patch14: 3353.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3845 +Patch15: 3845.patch + +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3720 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4038 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4053 +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4057 +Patch16: 4038.patch + + +Patch200: glib-change-version.patch + +BuildRequires: chrpath +BuildRequires: gcc +BuildRequires: gcc-c++ +BuildRequires: gettext +BuildRequires: gtk-doc +BuildRequires: perl-interpreter +# for sys/inotify.h +BuildRequires: glibc-devel +BuildRequires: libattr-devel +BuildRequires: libselinux-devel +BuildRequires: meson +# for sys/sdt.h +BuildRequires: systemtap-sdt-devel +BuildRequires: pkgconfig(libelf) +BuildRequires: pkgconfig(libffi) +BuildRequires: pkgconfig(libpcre) +BuildRequires: pkgconfig(mount) +%if %{with sysprof} +BuildRequires: pkgconfig(sysprof-capture-4) +%endif +BuildRequires: pkgconfig(zlib) +BuildRequires: python3-devel + +# For gnutls-hmac.patch. We now dlopen libgnutls.so.30 so that we can build a +# static glib2 without depending on a static build of GnuTLS as well. This will +# ensure we notice if the GnuTLS soname bumps, so that we can update our patch. +%if 0%{?__isa_bits} == 64 +Requires: libgnutls.so.30()(64bit) +%else +Requires: libgnutls.so.30 +%endif + +# Remove gamin dependency +#Obsoletes: glib2-fam < 2.67.1-3 + +# glib 2.59.0 hash table changes broke older gcr versions / password prompts in gnome-shell +#Conflicts: gcr < 3.28.1 + +Provides: bundled(gnulib) +Provides: bundled(gvdb) +Provides: bundled(libcharset) +Provides: bundled(xdgmime) + +%description +GLib is the low-level core library that forms the basis for projects +such as GTK+ and GNOME. It 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. + +%package devel +Summary: A library of handy utility functions +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The glib2-devel package includes the header files for the GLib library. + + +%package static +Summary: glib static +Requires: %{name}-devel = %{version}-%{release} + +%description static +The %{name}-static subpackage contains static libraries for %{name}. + + +%prep +%autosetup -n glib-%{version} -p1 + +%build +# Bug 1324770: Also explicitly remove PCRE sources since we use --with-pcre=system +rm glib/pcre/*.[ch] + +export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} +export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} $LDFLAGS" + +%meson \ + -Dman=false \ + -Ddtrace=true \ + -Dsystemtap=true \ +%if %{with sysprof} + -Dsysprof=enabled \ +%endif + -Dglib_debug=disabled \ + -Dgtk_doc=false \ + -Dinstalled_tests=false \ + -Dgnutls=true \ + --default-library=both \ + %{nil} + +%meson_build + +%install +%meson_install +# Since this is a generated .py file, set it to a known timestamp for +# better reproducibility. +# Also copy the timestamp for other .py files, because meson doesn't +# do this, see https://github.com/mesonbuild/meson/issues/5027. +touch -r gio/gdbus-2.0/codegen/config.py.in %{buildroot}%{_datadir}/glib-2.00/codegen/*.py +chrpath --delete %{buildroot}%{_libdir}/*.so + +# Perform byte compilation manually to avoid issues with +# irreproducibility of the default invalidation mode, see +# https://www.python.org/dev/peps/pep-0552/ and +# https://bugzilla.redhat.com/show_bug.cgi?id=1686078 +export PYTHONHASHSEED=0 +%py_byte_compile %{__python3} %{buildroot}%{_datadir} + +mv %{buildroot}%{_bindir}/gio-querymodules %{buildroot}%{_bindir}/gio-querymodules-%{__isa_bits} +sed -i -e "/^gio_querymodules=/s/gio-querymodules/gio-querymodules-%{__isa_bits}/" %{buildroot}%{_libdir}/pkgconfig/gio-2.00.pc + +mkdir -p %{buildroot}%{_libdir}/gio/modules +touch %{buildroot}%{_libdir}/gio/modules/giomodule.cache + +# remove unneeded +rm -fr %{buildroot}%{_bindir} +rm -fr %{buildroot}%{_datadir}/bash-completion + +%find_lang glib200 + +%transfiletriggerin -- %{_libdir}/gio/modules +gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : + +%transfiletriggerpostun -- %{_libdir}/gio/modules +gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || : + +%transfiletriggerin -- %{_datadir}/glib-2.00/schemas +glib-compile-schemas %{_datadir}/glib-2.00/schemas &> /dev/null || : + +%transfiletriggerpostun -- %{_datadir}/glib-2.00/schemas +glib-compile-schemas %{_datadir}/glib-2.00/schemas &> /dev/null || : + +%files -f glib200.lang +%license COPYING +%doc AUTHORS NEWS README +%{_libdir}/libglib-2.00.so.* +%{_libdir}/libgthread-2.00.so.* +%{_libdir}/libgmodule-2.00.so.* +%{_libdir}/libgobject-2.00.so.* +%{_libdir}/libgio-2.00.so.* +%dir %{_datadir}/glib-2.00 +%dir %{_datadir}/glib-2.00/schemas +%dir %{_libdir}/gio +%dir %{_libdir}/gio/modules +%ghost %{_libdir}/gio/modules/giomodule.cache + + +%files devel +%{_libdir}/lib*.so +%{_libdir}/glib-2.00 +%{_includedir}/* +%{_datadir}/aclocal/* +%{_libdir}/pkgconfig/* +%{_datadir}/glib-2.00/gdb +%{_datadir}/glib-2.00/gettext +%{_datadir}/glib-2.00/schemas/gschema.dtd +%{_datadir}/glib-2.00/valgrind/glib.supp +%{_datadir}/glib-2.00/codegen +%{_datadir}/gdb/ +%{_datadir}/gettext/ +%{_datadir}/systemtap/ + + +%files static +%{_libdir}/libgio-2.00.a +%{_libdir}/libglib-2.00.a +%{_libdir}/libgmodule-2.00.a +%{_libdir}/libgobject-2.00.a +%{_libdir}/libgthread-2.00.a + + +%changelog +* Mon May 13 2024 Michael Catanzaro - 2.68.4-15 +- Fix CVE-2024-34397, signal subscription vulnerabilities +- Resolves: RHEL-35775 + +* Wed Feb 21 2024 Michael Catanzaro - 2.68.4-14 +- Rebuild against newer util-linux for libmnt changes +- Resolves: RHEL-23637 + +* Thu Feb 01 2024 Michael Catanzaro - 2.68.4-13 +- Backport GUnixMountMonitor port to libmnt_monitor +- Resolves: RHEL-23637 + +* Fri Nov 03 2023 Michael Catanzaro - 2.68.4-12 +- Fix race with waitpid() and child watcher sources +- Resolves: RHEL-14761 + +* Wed Jul 19 2023 Michael Catanzaro - 2.68.4-11 +- Really fix authentication failures when sd-bus clients connect to GDBus servers +- Resolves: #2217771 + +* Thu Jul 06 2023 Michael Catanzaro - 2.68.4-10 +- Fix authentication failures when sd-bus clients connect to GDBus servers +- Resolves: #2217771 + +* Thu May 25 2023 Michael Catanzaro - 2.68.4-9 +- Resolve s390x crashes introduced by fixes for CVE-2023-24593/CVE-2023-25180 +- Related: #2181196 +- Related: #2181200 + +* Wed May 17 2023 Michael Catanzaro - 2.68.4-8 +- Resolve use after free introduced by fixes for CVE-2023-24593/CVE-2023-25180 +- Related: #2181196 +- Related: #2181200 + +* Fri Mar 24 2023 Michael Catanzaro - 2.68.4-7 +- Fix CVE-2023-24593 and CVE-2023-25180 +- Resolves: #2181196 +- Resolves: #2181200 + +* Fri Dec 02 2022 Michael Catanzaro - 2.68.4-6 +- Drop gdesktopappinfo patchset +- Resolves: #2150307 + +* Fri Jan 21 2022 Michael Catanzaro - 2.68.4-5 +- Add one more upstream patch to gspawn patchset +- Related: #1910092 + +* Fri Jan 21 2022 Michael Catanzaro - 2.68.4-4 +- Add gspawn patchset +- Resolves: #1910092 + +* Wed Dec 01 2021 Michael Catanzaro - 2.68.4-3 +- Fix GNetworkMonitor after NetworkManager D-Bus API changes +- Resolves: #2014624 + +* Wed Sep 15 2021 Michael Catanzaro - 2.68.4-2 +- Fix g_get_user_database_entry() crash when used with nss-systemd +- Resolves: #2004711 + +* Sat Aug 21 2021 Kalev Lember - 2.68.4-1 +- Update to 2.68.4 + +* Wed Aug 18 2021 DJ Delorie - 2.68.3-6 +- Rebuilt for libffi 3.4.2 SONAME transition. + Related: rhbz#1891914 + +* Tue Aug 17 2021 Michael Catanzaro - 2.68.3-5 +- Backport GPowerProfileMonitor +- Resolves: #1994466 + +* Mon Aug 09 2021 Mohan Boddu - 2.68.3-4 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Tue Jul 27 2021 Michael Catanzaro - 2.68.3-3 +- Fix build with glibc 2.34 +- Resolves: #1984626 + +* Thu Jul 01 2021 Michael Catanzaro - 2.68.3-2 +- Refresh gnutls-hmac patchset to fix leaks in error path +- Related: #1971823 + +* Mon Jun 28 2021 Michael Catanzaro - 2.68.3-1 +- Update to 2.68.3 +- Resolves: #1976713 +- Remove Recommends: shared-mime-info +- Resolves: #1947897 + +* Wed Jun 23 2021 Michael Catanzaro - 2.68.2-2 +- Update GHmac patchset and reenable glib2-static +- Resolves: #1971823 + +* Wed May 19 2021 Michael Catanzaro - 2.68.2-1 +- Update to 2.68.2 +- Resolves: #1961039 + +* Tue May 11 2021 Michael Catanzaro - 2.68.1-4 +- No changes, bump revision to retry gating +- Related: #1951126 + +* Fri May 07 2021 Michael Catanzaro - 2.68.1-3 +- Add missing bundled provides +- Add rpminspect gating configuration +- Consolidate GDesktopAppInfo patchset +- Resolves: #1951126 + +* Wed Apr 28 2021 Michael Catanzaro - 2.68.1-2 +- Refresh GDesktopAppInfo patchset +- Related: #1951126 + +* Thu Apr 22 2021 Kalev Lember - 2.68.1-1 +- Update to 2.68.1 + +* Thu Apr 15 2021 Mohan Boddu - 2.68.0-3 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Fri Mar 26 2021 Kalev Lember - 2.68.0-2 +- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it + +* Thu Mar 18 2021 Kalev Lember - 2.68.0-1 +- Update to 2.68.0 + +* Thu Mar 18 2021 Petr Pisar - 2.67.6-2 +- Disable debugging glib (bug #1936339) + +* Thu Mar 11 2021 Kalev Lember - 2.67.6-1 +- Update to 2.67.6 + +* Tue Mar 02 2021 Kalev Lember - 2.67.5-1 +- Update to 2.67.5 + +* Wed Feb 24 2021 Kalev Lember - 2.67.4-3 +- Enable sysprof capture support + +* Fri Feb 19 2021 Kalev Lember - 2.67.4-2 +- Backport a fix for gsubprocesslauncher regression + +* Tue Feb 16 2021 Kalev Lember - 2.67.4-1 +- Update to 2.67.4 + +* Tue Feb 09 2021 Benjamin Berg - 2.67.3-2 +- Add patches to move applications into systemd scopes + +* Thu Feb 04 2021 Kalev Lember - 2.67.3-1 +- Update to 2.67.3 +- Fix gtk-doc directory ownership + +* Tue Jan 26 2021 Fedora Release Engineering - 2.67.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Jan 22 2021 Peter Robinson - 2.67.1-3 +- Drop dependency on gamin + +* Sat Dec 19 2020 Kevin Fenzi - 2.67.1-2 +- Add already upstream patch to fix gdm crasher. + +* Sat Dec 19 2020 Kalev Lember - 2.67.1-1 +- Update to 2.67.1 + +* Fri Dec 04 2020 Ondrej Holy - 2.67.0-7 +- Explicitly create modules dir to fix ELN build + +* Tue Dec 01 2020 Ondrej Holy and Michael Catanzaro - 2.67.0-6 +- Disable glib2-fam in RHEL + +* Tue Nov 24 2020 Kalev Lember - 2.67.0-5 +- Backport upstream patches to fix invalid use of volatile objects + (gcc 11 support) + +* Wed Nov 11 2020 Michael Catanzaro - 2.67.0-4 +- Make GnuTLS patch RHEL-specific, and make glib2-static subpackage Fedora-specific + +* Tue Nov 10 2020 Michael Catanzaro - 2.67.0-3 +- Use GnuTLS to implement GHmac (thanks to Colin Walters) + +* Wed Nov 04 2020 Michael Catanzaro - 2.67.0-2 +- Backport fix for GSocketClient crash + +* Thu Oct 29 2020 Kalev Lember - 2.67.0-1 +- Update to 2.67.0 + +* Mon Oct 19 2020 Kalev Lember - 2.66.2-1 +- Update to 2.66.2 +- Drop gtk-doc patch as we finally have a new enough gtk-doc + +* Wed Oct 14 2020 Michael Catanzaro - 2.66.1-3 +- Fix yet another timezone bug + +* Wed Oct 14 2020 Michael Catanzaro - 2.66.1-2 +- Fix timezone-related bugs in many applications caused by new glib timezone cache + +* Thu Oct 1 2020 Kalev Lember - 2.66.1-1 +- Update to 2.66.1 + +* Thu Sep 10 2020 Kalev Lember - 2.66.0-1 +- Update to 2.66.0 + +* Wed Sep 02 2020 Kalev Lember - 2.65.3-1 +- Update to 2.65.3 + +* Tue Aug 25 2020 Adam Williamson - 2.65.2-3 +- Backport fix for GGO #2189 (error accessing some filesystems) + +* Thu Aug 20 2020 Jeff Law - 2.65.2-2 +- Re-enable LTO + +* Tue Aug 18 2020 Kalev Lember - 2.65.2-1 +- Update to 2.65.2 + +* Mon Aug 17 2020 Kalev Lember - 2.65.1-1 +- Update to 2.65.1 + +* Sat Aug 01 2020 Fedora Release Engineering - 2.65.0-5 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 2.65.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jun 30 2020 Jeff Law - 2.65.0-3 +Disable LTO + +* Mon Jun 22 2020 Kalev Lember - 2.65.0-2 +- Update gio-2.0.pc with correct gio-querymodules name when renaming it + (#1849441) + +* Mon Jun 22 2020 Kalev Lember - 2.65.0-1 +- Update to 2.65.0 + +* Wed May 20 2020 Kalev Lember - 2.64.3-1 +- Update to 2.64.3 + +* Tue Apr 28 2020 Tomas Popela - 2.64.2-2 +- Backport fix for a race condition in GCancellable (rhbz#1825230) + +* Fri Apr 10 2020 Kalev Lember - 2.64.2-1 +- Update to 2.64.2 + +* Wed Mar 11 2020 Kalev Lember - 2.64.1-1 +- Update to 2.64.1 + +* Mon Mar 02 2020 Kalev Lember - 2.64.0-1 +- Update to 2.64.0 + +* Mon Feb 24 2020 Kalev Lember - 2.63.6-1 +- Update to 2.63.6 + +* Wed Feb 12 2020 Kalev Lember - 2.63.5-3 +- Backport a patch to work around SELinux policies not allowing + SYS_sched_setattr (#1795524) + +* Fri Feb 07 2020 Michael Catanzaro - 2.63.5-2 +- Add patch for CVE-2020-6750 and related issues. + +* Mon Feb 03 2020 Kalev Lember - 2.63.5-1 +- Update to 2.63.5 + +* Wed Jan 29 2020 Stephen Gallagher - 2.63.4-3 +- Fix GThreadPool initialization that is breaking createrepo_c (BZ #1795052) + +* Tue Jan 28 2020 Fedora Release Engineering - 2.63.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Fri Jan 24 2020 Kalev Lember - 2.63.4-1 +- Update to 2.63.4 + +* Mon Dec 16 2019 Kalev Lember - 2.63.3-1 +- Update to 2.63.3 + +* Mon Dec 02 2019 Kalev Lember - 2.63.2-1 +- Update to 2.63.2 + +* Fri Oct 04 2019 Kalev Lember - 2.63.0-1 +- Update to 2.63.0 + +* Fri Oct 04 2019 Kalev Lember - 2.62.1-1 +- Update to 2.62.1 + +* Fri Sep 06 2019 Kalev Lember - 2.62.0-1 +- Update to 2.62.0 + +* Tue Sep 03 2019 Kalev Lember - 2.61.3-1 +- Update to 2.61.3 + +* Mon Aug 12 2019 Kalev Lember - 2.61.2-1 +- Update to 2.61.2 + +* Thu Jul 25 2019 Fedora Release Engineering - 2.61.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jun 11 2019 David King - 2.61.1-2 +- Fix CVE-2019-12450 (#1719142) +- Consistently use buildroot macro + +* Fri May 24 2019 Kalev Lember - 2.61.1-1 +- Update to 2.61.1 + +* Tue Apr 16 2019 Adam Williamson - 2.61.0-2 +- Rebuild with Meson fix for #1699099 + +* Mon Apr 15 2019 Kalev Lember - 2.61.0-1 +- Update to 2.61.0 + +* Mon Apr 15 2019 Kalev Lember - 2.60.1-1 +- Update to 2.60.1 + +* Wed Mar 13 2019 Zbigniew Jędrzejewski-Szmek - 2.60.0-3 +- Switch back to timestamp-based pyc invalidation mode + +* Wed Mar 6 2019 Zbigniew Jędrzejewski-Szmek - 2.60.0-2 +- Make sure all .py files have fixed timestamps (fixes issue with + parallel installability of i686 and amd64 -devel packages) +- Switch to explicit byte compilation to override invalidation mode + +* Mon Mar 04 2019 Kalev Lember - 2.60.0-1 +- Update to 2.60.0 + +* Mon Feb 18 2019 Kalev Lember - 2.59.3-1 +- Update to 2.59.3 + +* Mon Feb 04 2019 Kalev Lember - 2.59.2-1 +- Update to 2.59.2 + +* Thu Jan 31 2019 Fedora Release Engineering - 2.59.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Sat Jan 26 2019 Kalev Lember - 2.59.1-1 +- Update to 2.59.1 + +* Thu Jan 03 2019 Kalev Lember - 2.59.0-1 +- Update to 2.59.0 +- Switch to the meson build system + +* Tue Dec 18 2018 Kalev Lember - 2.58.2-1 +- Update to 2.58.2 + +* Fri Oct 05 2018 Kalev Lember - 2.58.1-2 +- Fix multilib -devel installs (#1634778) + +* Fri Sep 21 2018 Kalev Lember - 2.58.1-1 +- Update to 2.58.1 + +* Wed Sep 05 2018 Kalev Lember - 2.58.0-1 +- Update to 2.58.0 + +* Thu Aug 2 2018 Ondrej Holy - 2.57.2-1 +- Update to 2.57.2 + +* Fri Jul 20 2018 Ondrej Holy - 2.57.1-1 +- Update to 2.57.1 + +* Fri Jul 13 2018 Fedora Release Engineering - 2.56.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jun 19 2018 Miro Hrončok - 2.56.1-5 +- Rebuilt for Python 3.7 + +* Thu Jun 14 2018 Debarshi Ray - 2.56.1-4 +- Backport patch to fix possible invalid pointer in dbus callback in the FD.o + notification backend (RH #1584916) + +* Sun May 27 2018 Kalev Lember - 2.56.1-3 +- Fix multilib -devel installs (#1581067) + +* Sun May 13 2018 Fabio Valentini - 2.56.1-2 +- Include upstream patch to fix gdbus-codegen with meson 0.46. + +* Sun Apr 08 2018 Kalev Lember - 2.56.1-1 +- Update to 2.56.1 + +* Mon Mar 12 2018 Kalev Lember - 2.56.0-1 +- Update to 2.56.0 + +* Wed Feb 07 2018 Igor Gnatenko - 2.55.2-3 +- Undo disabling mangling + +* Wed Feb 07 2018 Kalev Lember - 2.55.2-2 +- Disable brp-mangle-shebangs shebangs + +* Wed Feb 07 2018 Kalev Lember - 2.55.2-1 +- Update to 2.55.2 +- Drop ldconfig scriptlets + +* Wed Jan 31 2018 Igor Gnatenko - 2.55.1-3 +- Switch to %%ldconfig_scriptlets + +* Thu Jan 18 2018 Kalev Lember - 2.55.1-2 +- gmain: Partial revert of recent wakeup changes + +* Mon Jan 08 2018 Kalev Lember - 2.55.1-1 +- Update to 2.55.1 +- Drop upstreamed systemtap multilib fix + +* Tue Dec 19 2017 Kalev Lember - 2.55.0-1 +- Update to 2.55.0 + +* Wed Nov 01 2017 Kalev Lember - 2.54.2-1 +- Update to 2.54.2 + +* Fri Oct 06 2017 Kalev Lember - 2.54.1-1 +- Update to 2.54.1 + +* Mon Sep 11 2017 Kalev Lember - 2.54.0-1 +- Update to 2.54.0 + +* Tue Sep 05 2017 Kalev Lember - 2.53.7-1 +- Update to 2.53.7 + +* Sat Aug 19 2017 Kalev Lember - 2.53.6-1 +- Update to 2.53.6 + +* Mon Aug 07 2017 Igor Gnatenko - 2.53.5-1 +- Update to 2.53.5 + +* Tue Aug 01 2017 Kalev Lember - 2.53.4-4 +- Backport glib-mkenums flags annotation parsing fixes + +* Wed Jul 26 2017 Fedora Release Engineering - 2.53.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 21 2017 Kalev Lember - 2.53.4-2 +- Revert a GKeyFile introspection ABI change + +* Tue Jul 18 2017 Kalev Lember - 2.53.4-1 +- Update to 2.53.4 + +* Thu Jun 22 2017 Kalev Lember - 2.53.3-1 +- Update to 2.53.3 + +* Thu Jun 8 2017 Owen Taylor - 2.53.2-2 +- Make triggers also compile schemas in /app/share/glib-2.0/schemas + +* Wed May 24 2017 Florian Müllner - 2.53.2-1 +- Update to 2.53.2 + +* Mon May 15 2017 Kalev Lember - 2.52.2-2 +- Backport a gmain GWakeup patch to fix timedatex high CPU usage (#1450628) + +* Tue May 09 2017 Kalev Lember - 2.52.2-1 +- Update to 2.52.2 + +* Tue Apr 11 2017 Colin Walters - 2.52.1-3 +- Backport patches for gmain wakeup for qemu + See: https://bugzilla.gnome.org/show_bug.cgi?id=761102 + +* Tue Apr 11 2017 Colin Walters - 2.52.1-2 +- Explictly remove PCRE sources +- Related: https://bugzilla.redhat.com/show_bug.cgi?id=1324770 + +* Tue Apr 11 2017 Kalev Lember - 2.52.1-1 +- Update to 2.52.1 + +* Mon Mar 20 2017 Kalev Lember - 2.52.0-1 +- Update to 2.52.0 + +* Thu Mar 16 2017 Kalev Lember - 2.51.5-1 +- Update to 2.51.5 + +* Thu Mar 02 2017 Kalev Lember - 2.51.4-2 +- Remove the dependency on dbus-launch again (#927212) + +* Wed Mar 01 2017 David King - 2.51.4-1 +- Update to 2.51.4 +- Add a Requires on dbus-launch (#927212) +- Use pkgconfig for BuildRequires + +* Tue Feb 14 2017 Richard Hughes - 2.51.2-1 +- Update to 2.51.2 + +* Mon Feb 13 2017 Richard Hughes - 2.51.1-1 +- Update to 2.51.1 + +* Fri Feb 10 2017 Fedora Release Engineering - 2.51.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Dec 19 2016 Miro Hrončok - 2.51.0-2 +- Rebuild for Python 3.6 + +* Sun Oct 30 2016 Kalev Lember - 2.51.0-1 +- Update to 2.51.0 + +* Wed Oct 12 2016 Kalev Lember - 2.50.1-1 +- Update to 2.50.1 + +* Mon Sep 19 2016 Kalev Lember - 2.50.0-1 +- Update to 2.50.0 + +* Tue Sep 13 2016 Kalev Lember - 2.49.7-1 +- Update to 2.49.7 +- Don't set group tags + +* Sun Aug 28 2016 Kalev Lember - 2.49.6-1 +- Update to 2.49.6 + +* Thu Aug 18 2016 Kalev Lember - 2.49.5-1 +- Update to 2.49.5 +- Own /usr/share/gdb and /usr/share/systemtap directories + +* Tue Aug 16 2016 Miro Hrončok - 2.49.4-3 +- Use Python 3 for the RPM Python byte compilation + +* Wed Jul 27 2016 Ville Skyttä - 2.49.4-2 +- Switch to Python 3 (#1286284) + +* Thu Jul 21 2016 Kalev Lember - 2.49.4-1 +- Update to 2.49.4 + +* Sun Jul 17 2016 Kalev Lember - 2.49.3-1 +- Update to 2.49.3 + +* Wed Jun 22 2016 Richard Hughes - 2.49.2-1 +- Update to 2.49.2 + +* Wed Jun 01 2016 Yaakov Selkowitz - 2.49.1-2 +- Soften shared-mime-info dependency (#1266118) + +* Fri May 27 2016 Florian Müllner - 2.49.1-1 +- Update to 2.49.1 + +* Tue May 10 2016 Kalev Lember - 2.48.1-1 +- Update to 2.48.1 + +* Wed Apr 06 2016 Colin Walters - 2.48.0-2 +- Explicitly require system pcre, though we happened to default to this now + anyways due to something else pulling PCRE into the buildroot + Closes rhbz#1287266 + +* Tue Mar 22 2016 Kalev Lember - 2.48.0-1 +- Update to 2.48.0 + +* Thu Mar 17 2016 Richard Hughes - 2.47.92-1 +- Update to 2.47.92 + +* Wed Feb 24 2016 Colin Walters - 2.47.6.19.gad2092b-2 +- git snapshot to work around https://bugzilla.gnome.org/show_bug.cgi?id=762637 +- Add --with-python=/usr/bin/python explicitly to hopefully fix a weird + issue I am seeing where librepo fails to build in epel7 with this due to + us requiring /bin/python. + +* Wed Feb 17 2016 Richard Hughes - 2.47.6-1 +- Update to 2.47.6 + +* Wed Feb 03 2016 Fedora Release Engineering - 2.47.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Jan 19 2016 David King - 2.47.5-1 +- Update to 2.47.5 + +* Wed Dec 16 2015 Kalev Lember - 2.47.4-1 +- Update to 2.47.4 + +* Wed Nov 25 2015 Kalev Lember - 2.47.3-1 +- Update to 2.47.3 + +* Wed Nov 25 2015 Kalev Lember - 2.47.2-1 +- Update to 2.47.2 + +* Mon Nov 09 2015 Kevin Fenzi - 2.47.1-2 +- Add full path redirect output to null and || : to triggers. + +* Wed Oct 28 2015 Kalev Lember - 2.47.1-1 +- Update to 2.47.1 + +* Mon Oct 19 2015 Kalev Lember - 2.46.1-2 +- Backport an upstream fix for app launching under wayland (#1273146) + +* Wed Oct 14 2015 Kalev Lember - 2.46.1-1 +- Update to 2.46.1 + +* Mon Sep 21 2015 Kalev Lember - 2.46.0-1 +- Update to 2.46.0 + +* Mon Sep 14 2015 Kalev Lember - 2.45.8-1 +- Update to 2.45.8 + +* Tue Sep 01 2015 Kalev Lember - 2.45.7-1 +- Update to 2.45.7 + +* Wed Aug 19 2015 Kalev Lember - 2.45.6-1 +- Update to 2.45.6 + +* Wed Aug 19 2015 Kalev Lember - 2.45.5-1 +- Update to 2.45.5 + +* Fri Aug 14 2015 Matthias Clasen - 2.45.4-2 +- Add file triggers for gio modules and gsettings schemas + +* Tue Jul 21 2015 David King - 2.45.4-1 +- Update to 2.45.4 + +* Wed Jun 24 2015 Kalev Lember - 2.45.3-2 +- Backport a patch to fix notification withdrawing in gnome-software + +* Wed Jun 24 2015 David King - 2.45.3-1 +- Update to 2.45.3 + +* Wed Jun 17 2015 Fedora Release Engineering - 2.45.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Tue May 26 2015 David King - 2.45.2-1 +- Update to 2.45.2 + +* Thu Apr 30 2015 Kalev Lember - 2.45.1-1 +- Update to 2.45.1 + +* Mon Mar 23 2015 Kalev Lember - 2.44.0-1 +- Update to 2.44.0 + +* Tue Mar 17 2015 Kalev Lember - 2.43.92-1 +- Update to 2.43.92 + +* Mon Mar 02 2015 Kalev Lember - 2.43.91-1 +- Update to 2.43.91 + +* Sat Feb 21 2015 Till Maas - 2.43.90-2 +- Rebuilt for Fedora 23 Change + https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code + +* Wed Feb 18 2015 David King - 2.43.90-1 +- Update to 2.43.90 +- Update man pages glob in files section + +* Tue Feb 10 2015 Matthias Clasen - 2.43.4-1 +- Update to 2.43.4 + +* Tue Jan 20 2015 David King - 2.43.3-1 +- Update to 2.43.3 + +* Wed Dec 17 2014 Kalev Lember - 2.43.2-1 +- Update to 2.43.2 + +* Tue Nov 25 2014 Kalev Lember - 2.43.1-1 +- Update to 2.43.1 + +* Thu Oct 30 2014 Florian Müllner - 2.43.0-1 +- Update to 2.43.0 + +* Mon Sep 22 2014 Kalev Lember - 2.42.0-1 +- Update to 2.42.0 + +* Tue Sep 16 2014 Kalev Lember - 2.41.5-1 +- Update to 2.41.5 + +* Thu Sep 4 2014 Matthias Clasen 2.41.4-3 +- Don't remove rpath from gdbus-peer test - it doesn't work without it + +* Thu Sep 04 2014 Bastien Nocera 2.41.4-2 +- Fix banshee getting selected as the default movie player + +* Tue Sep 02 2014 Kalev Lember - 2.41.4-1 +- Update to 2.41.4 + +* Sat Aug 16 2014 Kalev Lember - 2.41.3-1 +- Update to 2.41.3 + +* Sat Aug 16 2014 Fedora Release Engineering - 2.41.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Jul 23 2014 Stef Walter - 2.41.2-2 +- Fix regression with GDBus array encoding rhbz#1122128 + +* Mon Jul 14 2014 Kalev Lember - 2.41.2-1 +- Update to 2.41.2 + +* Sat Jul 12 2014 Tom Callaway - 2.41.1-2 +- fix license handling + +* Tue Jun 24 2014 Richard Hughes - 2.41.1-1 +- Update to 2.41.1 + +* Sat Jun 07 2014 Fedora Release Engineering - 2.41.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue May 27 2014 Kalev Lember - 2.41.0-1 +- Update to 2.41.0 + +* Mon Mar 24 2014 Richard Hughes - 2.40.0-1 +- Update to 2.40.0 + +* Tue Mar 18 2014 Richard Hughes - 2.39.92-1 +- Update to 2.39.92 + +* Tue Mar 04 2014 Richard Hughes - 2.39.91-1 +- Update to 2.39.91 + +* Tue Feb 18 2014 Richard Hughes - 2.39.90-1 +- Update to 2.39.90 + +* Tue Feb 04 2014 Richard Hughes - 2.39.4-1 +- Update to 2.39.4 + +* Tue Jan 14 2014 Richard Hughes - 2.39.3-1 +- Update to 2.39.3 + +* Sun Dec 22 2013 Richard W.M. Jones - 2.39.2-2 +- Re-add static subpackage so that we can build static qemu as + an AArch64 binfmt. + +* Tue Dec 17 2013 Richard Hughes - 2.39.2-1 +- Update to 2.39.2 + +* Mon Dec 09 2013 Richard Hughes - 2.39.1-2 +- Backport a patch from master to stop gnome-settings-daemon crashing. + +* Thu Nov 14 2013 Richard Hughes - 2.39.1-1 +- Update to 2.39.1 + +* Mon Oct 28 2013 Richard Hughes - 2.39.0-1 +- Update to 2.39.0 + +* Tue Sep 24 2013 Kalev Lember - 2.38.0-1 +- Update to 2.38.0 + +* Tue Sep 17 2013 Kalev Lember - 2.37.93-1 +- Update to 2.37.93 + +* Mon Sep 02 2013 Kalev Lember - 2.37.7-1 +- Update to 2.37.7 + +* Wed Aug 21 2013 Debarshi Ray - 2.37.6-1 +- Update to 2.37.6 + +* Sat Aug 03 2013 Petr Pisar - 2.37.5-2 +- Perl 5.18 rebuild + +* Thu Aug 1 2013 Debarshi Ray - 2.37.5-1 +- Update to 2.37.5 + +* Wed Jul 17 2013 Petr Pisar - 2.37.4-2 +- Perl 5.18 rebuild + +* Tue Jul 9 2013 Matthias Clasen - 2.37.4-1 +- Update to 2.37.4 + +* Thu Jun 20 2013 Debarshi Ray - 2.37.2-1 +- Update to 2.37.2 + +* Tue May 28 2013 Matthias Clasen - 2.37.1-1 +- Update to 2.37.1 +- Add a tests subpackage + +* Sat May 04 2013 Kalev Lember - 2.37.0-1 +- Update to 2.37.0 + +* Sat Apr 27 2013 Thorsten Leemhuis - 2.36.1-2 +- Fix pidgin freezes by applying patch from master (#956872) + +* Mon Apr 15 2013 Kalev Lember - 2.36.1-1 +- Update to 2.36.1 + +* Mon Mar 25 2013 Kalev Lember - 2.36.0-1 +- Update to 2.36.0 + +* Tue Mar 19 2013 Matthias Clasen - 2.35.9-1 +- Update to 2.35.9 + +* Thu Feb 21 2013 Kalev Lember - 2.35.8-1 +- Update to 2.35.8 + +* Tue Feb 05 2013 Kalev Lember - 2.35.7-1 +- Update to 2.35.7 + +* Tue Jan 15 2013 Matthias Clasen - 2.35.4-1 +- Update to 2.35.4 + +* Thu Dec 20 2012 Kalev Lember - 2.35.3-1 +- Update to 2.35.3 + +* Sat Nov 24 2012 Kalev Lember - 2.35.2-1 +- Update to 2.35.2 + +* Thu Nov 08 2012 Kalev Lember - 2.35.1-1 +- Update to 2.35.1 +- Drop upstreamed codegen-in-datadir.patch diff --git a/base/rx/rx-glib2/gnutls-hmac.patch b/base/rx/rx-glib2/gnutls-hmac.patch new file mode 100644 index 0000000..4b1ba0e --- /dev/null +++ b/base/rx/rx-glib2/gnutls-hmac.patch @@ -0,0 +1,1086 @@ +From ff90bb8474b1e724727f4014b446e7c851e609bd Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 18:44:43 +0000 +Subject: [PATCH 1/4] ghmac: Split off wrapper functions into ghmac-utils.c + +Prep for adding a GnuTLS HMAC implementation; these are just +utility functions that call the "core" API. +--- + glib/ghmac-utils.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ + glib/ghmac.c | 112 ---------------------------------- + glib/meson.build | 1 + + 3 files changed, 146 insertions(+), 112 deletions(-) + create mode 100644 glib/ghmac-utils.c + +diff --git a/glib/ghmac-utils.c b/glib/ghmac-utils.c +new file mode 100644 +index 000000000..a17359ff1 +--- /dev/null ++++ b/glib/ghmac-utils.c +@@ -0,0 +1,145 @@ ++/* ghmac.h - data hashing functions ++ * ++ * Copyright (C) 2011 Collabora Ltd. ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include ++ ++#include "ghmac.h" ++ ++#include "glib/galloca.h" ++#include "gatomic.h" ++#include "gslice.h" ++#include "gmem.h" ++#include "gstrfuncs.h" ++#include "gtestutils.h" ++#include "gtypes.h" ++#include "glibintl.h" ++ ++/** ++ * g_compute_hmac_for_data: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: (array length=key_len): the key to use in the HMAC ++ * @key_len: the length of the key ++ * @data: (array length=length): binary blob to compute the HMAC of ++ * @length: length of @data ++ * ++ * Computes the HMAC for a binary @data of @length. This is a ++ * convenience wrapper for g_hmac_new(), g_hmac_get_string() ++ * and g_hmac_unref(). ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC of the binary data as a string in hexadecimal. ++ * The returned string should be freed with g_free() when done using it. ++ * ++ * Since: 2.30 ++ */ ++gchar * ++g_compute_hmac_for_data (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len, ++ const guchar *data, ++ gsize length) ++{ ++ GHmac *hmac; ++ gchar *retval; ++ ++ g_return_val_if_fail (length == 0 || data != NULL, NULL); ++ ++ hmac = g_hmac_new (digest_type, key, key_len); ++ if (!hmac) ++ return NULL; ++ ++ g_hmac_update (hmac, data, length); ++ retval = g_strdup (g_hmac_get_string (hmac)); ++ g_hmac_unref (hmac); ++ ++ return retval; ++} ++ ++/** ++ * g_compute_hmac_for_bytes: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: the key to use in the HMAC ++ * @data: binary blob to compute the HMAC of ++ * ++ * Computes the HMAC for a binary @data. This is a ++ * convenience wrapper for g_hmac_new(), g_hmac_get_string() ++ * and g_hmac_unref(). ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC of the binary data as a string in hexadecimal. ++ * The returned string should be freed with g_free() when done using it. ++ * ++ * Since: 2.50 ++ */ ++gchar * ++g_compute_hmac_for_bytes (GChecksumType digest_type, ++ GBytes *key, ++ GBytes *data) ++{ ++ gconstpointer byte_data; ++ gsize length; ++ gconstpointer key_data; ++ gsize key_len; ++ ++ g_return_val_if_fail (data != NULL, NULL); ++ g_return_val_if_fail (key != NULL, NULL); ++ ++ byte_data = g_bytes_get_data (data, &length); ++ key_data = g_bytes_get_data (key, &key_len); ++ return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); ++} ++ ++ ++/** ++ * g_compute_hmac_for_string: ++ * @digest_type: a #GChecksumType to use for the HMAC ++ * @key: (array length=key_len): the key to use in the HMAC ++ * @key_len: the length of the key ++ * @str: the string to compute the HMAC for ++ * @length: the length of the string, or -1 if the string is nul-terminated ++ * ++ * Computes the HMAC for a string. ++ * ++ * The hexadecimal string returned will be in lower case. ++ * ++ * Returns: the HMAC as a hexadecimal string. ++ * The returned string should be freed with g_free() ++ * when done using it. ++ * ++ * Since: 2.30 ++ */ ++gchar * ++g_compute_hmac_for_string (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len, ++ const gchar *str, ++ gssize length) ++{ ++ g_return_val_if_fail (length == 0 || str != NULL, NULL); ++ ++ if (length < 0) ++ length = strlen (str); ++ ++ return g_compute_hmac_for_data (digest_type, key, key_len, ++ (const guchar *) str, length); ++} +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 49fd272f0..4f181f21f 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -329,115 +329,3 @@ g_hmac_get_digest (GHmac *hmac, + g_checksum_update (hmac->digesto, buffer, len); + g_checksum_get_digest (hmac->digesto, buffer, digest_len); + } +- +-/** +- * g_compute_hmac_for_data: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: (array length=key_len): the key to use in the HMAC +- * @key_len: the length of the key +- * @data: (array length=length): binary blob to compute the HMAC of +- * @length: length of @data +- * +- * Computes the HMAC for a binary @data of @length. This is a +- * convenience wrapper for g_hmac_new(), g_hmac_get_string() +- * and g_hmac_unref(). +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC of the binary data as a string in hexadecimal. +- * The returned string should be freed with g_free() when done using it. +- * +- * Since: 2.30 +- */ +-gchar * +-g_compute_hmac_for_data (GChecksumType digest_type, +- const guchar *key, +- gsize key_len, +- const guchar *data, +- gsize length) +-{ +- GHmac *hmac; +- gchar *retval; +- +- g_return_val_if_fail (length == 0 || data != NULL, NULL); +- +- hmac = g_hmac_new (digest_type, key, key_len); +- if (!hmac) +- return NULL; +- +- g_hmac_update (hmac, data, length); +- retval = g_strdup (g_hmac_get_string (hmac)); +- g_hmac_unref (hmac); +- +- return retval; +-} +- +-/** +- * g_compute_hmac_for_bytes: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: the key to use in the HMAC +- * @data: binary blob to compute the HMAC of +- * +- * Computes the HMAC for a binary @data. This is a +- * convenience wrapper for g_hmac_new(), g_hmac_get_string() +- * and g_hmac_unref(). +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC of the binary data as a string in hexadecimal. +- * The returned string should be freed with g_free() when done using it. +- * +- * Since: 2.50 +- */ +-gchar * +-g_compute_hmac_for_bytes (GChecksumType digest_type, +- GBytes *key, +- GBytes *data) +-{ +- gconstpointer byte_data; +- gsize length; +- gconstpointer key_data; +- gsize key_len; +- +- g_return_val_if_fail (data != NULL, NULL); +- g_return_val_if_fail (key != NULL, NULL); +- +- byte_data = g_bytes_get_data (data, &length); +- key_data = g_bytes_get_data (key, &key_len); +- return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); +-} +- +- +-/** +- * g_compute_hmac_for_string: +- * @digest_type: a #GChecksumType to use for the HMAC +- * @key: (array length=key_len): the key to use in the HMAC +- * @key_len: the length of the key +- * @str: the string to compute the HMAC for +- * @length: the length of the string, or -1 if the string is nul-terminated +- * +- * Computes the HMAC for a string. +- * +- * The hexadecimal string returned will be in lower case. +- * +- * Returns: the HMAC as a hexadecimal string. +- * The returned string should be freed with g_free() +- * when done using it. +- * +- * Since: 2.30 +- */ +-gchar * +-g_compute_hmac_for_string (GChecksumType digest_type, +- const guchar *key, +- gsize key_len, +- const gchar *str, +- gssize length) +-{ +- g_return_val_if_fail (length == 0 || str != NULL, NULL); +- +- if (length < 0) +- length = strlen (str); +- +- return g_compute_hmac_for_data (digest_type, key, key_len, +- (const guchar *) str, length); +-} +diff --git a/glib/meson.build b/glib/meson.build +index 8c18e6de4..329b8d197 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -253,6 +253,7 @@ glib_sources = files( + 'ggettext.c', + 'ghash.c', + 'ghmac.c', ++ 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', + 'giochannel.c', +-- +2.31.1 + +From 5395d36e6685e0b7377794c59c5820970bb472ef Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 19:36:54 +0000 +Subject: [PATCH 2/4] Add a gnutls backend for GHmac + +For RHEL we want apps to use FIPS-certified crypto libraries, +and HMAC apparently counts as "keyed" and hence needs to +be validated. + +Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1630260 +Replaces: https://gitlab.gnome.org/GNOME/glib/merge_requests/897 + +This is a build-time option that backs the GHmac API with GnuTLS. +Most distributors ship glib-networking built with GnuTLS, and +most apps use glib-networking, so this isn't a net-new library +in most cases. + +======================================================================= + +mcatanzaro note: + +I've updated Colin's original patch with several enhancements: + +Implement g_hmac_copy() using gnutls_hmac_copy(), which didn't exist +when Colin developed this patch. + +Removed use of GSlice + +Better error checking in g_hmac_new(). It is possible for +gnutls_hmac_init() to fail if running in FIPS mode and an MD5 digest is +requested. In this case, we should return NULL rather than returning a +broken GHmac with a NULL gnutls_hmac_hd_t. This was leading to a later +null pointer dereference inside gnutls_hmac_update(). Applications are +responsible for checking to ensure the return value of g_hmac_new() is +not NULL since it is annotated as nullable. Added documentation to +indicate this possibility. + +Properly handle length -1 in g_hmac_update(). This means we've been +given a NUL-terminated string and should use strlen(). GnuTLS doesn't +accept -1, so let's call strlen() ourselves. + +Crash the application with g_error() if gnutls_hmac() fails for any +reason. This is necessary because g_hmac_update() is not fallible, so we +have no way to indicate error. Crashing seems better than returning the +wrong result later when g_hmac_get_string() or g_hmac_get_digest() is +later called. (Those functions are also not fallible.) Fortunately, I +don't think this error should actually be hit in practice. + +https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 +--- + glib/gchecksum.c | 9 +- + glib/gchecksumprivate.h | 32 +++++++ + glib/ghmac-gnutls.c | 187 ++++++++++++++++++++++++++++++++++++++++ + glib/ghmac.c | 15 ++++ + glib/meson.build | 10 ++- + meson.build | 7 ++ + meson_options.txt | 7 +- + 7 files changed, 260 insertions(+), 7 deletions(-) + create mode 100644 glib/gchecksumprivate.h + create mode 100644 glib/ghmac-gnutls.c + +diff --git a/glib/gchecksum.c b/glib/gchecksum.c +index 29b479bc6..929958c3a 100644 +--- a/glib/gchecksum.c ++++ b/glib/gchecksum.c +@@ -20,7 +20,7 @@ + + #include + +-#include "gchecksum.h" ++#include "gchecksumprivate.h" + + #include "gslice.h" + #include "gmem.h" +@@ -173,9 +173,9 @@ sha_byte_reverse (guint32 *buffer, + } + #endif /* G_BYTE_ORDER == G_BIG_ENDIAN */ + +-static gchar * +-digest_to_string (guint8 *digest, +- gsize digest_len) ++gchar * ++gchecksum_digest_to_string (guint8 *digest, ++ gsize digest_len) + { + gsize i, len = digest_len * 2; + gchar *retval; +@@ -194,6 +194,7 @@ digest_to_string (guint8 *digest, + + return retval; + } ++#define digest_to_string gchecksum_digest_to_string + + /* + * MD5 Checksum +diff --git a/glib/gchecksumprivate.h b/glib/gchecksumprivate.h +new file mode 100644 +index 000000000..86c7a3b61 +--- /dev/null ++++ b/glib/gchecksumprivate.h +@@ -0,0 +1,32 @@ ++/* gstdioprivate.h - Private GLib stdio functions ++ * ++ * Copyright 2017 Руслан Ижбулатов ++ * ++ * 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 . ++ */ ++ ++#ifndef __G_CHECKSUMPRIVATE_H__ ++#define __G_CHECKSUMPRIVATE_H__ ++ ++#include "gchecksum.h" ++ ++G_BEGIN_DECLS ++ ++gchar * ++gchecksum_digest_to_string (guint8 *digest, ++ gsize digest_len); ++ ++G_END_DECLS ++ ++#endif +\ No newline at end of file +diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c +new file mode 100644 +index 000000000..9fb775f89 +--- /dev/null ++++ b/glib/ghmac-gnutls.c +@@ -0,0 +1,187 @@ ++/* ghmac.h - data hashing functions ++ * ++ * Copyright (C) 2011 Collabora Ltd. ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "ghmac.h" ++ ++#include "glib/galloca.h" ++#include "gatomic.h" ++#include "gslice.h" ++#include "gmem.h" ++#include "gstrfuncs.h" ++#include "gchecksumprivate.h" ++#include "gtestutils.h" ++#include "gtypes.h" ++#include "glibintl.h" ++ ++#ifndef HAVE_GNUTLS ++#error "build configuration error" ++#endif ++ ++struct _GHmac ++{ ++ int ref_count; ++ GChecksumType digest_type; ++ gnutls_hmac_hd_t hmac; ++ gchar *digest_str; ++}; ++ ++GHmac * ++g_hmac_new (GChecksumType digest_type, ++ const guchar *key, ++ gsize key_len) ++{ ++ gnutls_mac_algorithm_t algo; ++ GHmac *hmac = g_new0 (GHmac, 1); ++ int ret; ++ ++ hmac->ref_count = 1; ++ hmac->digest_type = digest_type; ++ ++ switch (digest_type) ++ { ++ case G_CHECKSUM_MD5: ++ algo = GNUTLS_MAC_MD5; ++ break; ++ case G_CHECKSUM_SHA1: ++ algo = GNUTLS_MAC_SHA1; ++ break; ++ case G_CHECKSUM_SHA256: ++ algo = GNUTLS_MAC_SHA256; ++ break; ++ case G_CHECKSUM_SHA384: ++ algo = GNUTLS_MAC_SHA384; ++ break; ++ case G_CHECKSUM_SHA512: ++ algo = GNUTLS_MAC_SHA512; ++ break; ++ default: ++ g_free (hmac); ++ g_return_val_if_reached (NULL); ++ } ++ ++ ret = gnutls_hmac_init (&hmac->hmac, algo, key, key_len); ++ if (ret != 0) ++ { ++ /* There is no way to report an error here, but one possible cause of ++ * failure is that the requested digest may be disabled by FIPS mode. ++ */ ++ g_free (hmac); ++ return NULL; ++ } ++ ++ return hmac; ++} ++ ++GHmac * ++g_hmac_copy (const GHmac *hmac) ++{ ++ GHmac *copy; ++ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ copy = g_new0 (GHmac, 1); ++ copy->ref_count = 1; ++ copy->digest_type = hmac->digest_type; ++ copy->hmac = gnutls_hmac_copy (hmac->hmac); ++ ++ /* g_hmac_copy is not allowed to fail, so we'll have to crash on error. */ ++ if (!copy->hmac) ++ g_error ("gnutls_hmac_copy failed"); ++ ++ return copy; ++} ++ ++GHmac * ++g_hmac_ref (GHmac *hmac) ++{ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ g_atomic_int_inc (&hmac->ref_count); ++ ++ return hmac; ++} ++ ++void ++g_hmac_unref (GHmac *hmac) ++{ ++ g_return_if_fail (hmac != NULL); ++ ++ if (g_atomic_int_dec_and_test (&hmac->ref_count)) ++ { ++ gnutls_hmac_deinit (hmac->hmac, NULL); ++ g_free (hmac->digest_str); ++ g_free (hmac); ++ } ++} ++ ++ ++void ++g_hmac_update (GHmac *hmac, ++ const guchar *data, ++ gssize length) ++{ ++ int ret; ++ ++ g_return_if_fail (hmac != NULL); ++ g_return_if_fail (length == 0 || data != NULL); ++ ++ if (length == -1) ++ length = strlen ((const char *)data); ++ ++ /* g_hmac_update is not allowed to fail, so we'll have to crash on error. */ ++ ret = gnutls_hmac (hmac->hmac, data, length); ++ if (ret != 0) ++ g_error ("gnutls_hmac failed: %s", gnutls_strerror (ret)); ++} ++ ++const gchar * ++g_hmac_get_string (GHmac *hmac) ++{ ++ guint8 *buffer; ++ gsize digest_len; ++ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ if (hmac->digest_str) ++ return hmac->digest_str; ++ ++ digest_len = g_checksum_type_get_length (hmac->digest_type); ++ buffer = g_alloca (digest_len); ++ ++ gnutls_hmac_output (hmac->hmac, buffer); ++ hmac->digest_str = gchecksum_digest_to_string (buffer, digest_len); ++ return hmac->digest_str; ++} ++ ++ ++void ++g_hmac_get_digest (GHmac *hmac, ++ guint8 *buffer, ++ gsize *digest_len) ++{ ++ g_return_if_fail (hmac != NULL); ++ ++ gnutls_hmac_output (hmac->hmac, buffer); ++ *digest_len = g_checksum_type_get_length (hmac->digest_type); ++} +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 4f181f21f..0e39ea40a 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -33,6 +33,9 @@ + #include "gtypes.h" + #include "glibintl.h" + ++#ifdef HAVE_GNUTLS ++#error "build configuration error" ++#endif + + /** + * SECTION:hmac +@@ -84,6 +87,18 @@ struct _GHmac + * Support for digests of type %G_CHECKSUM_SHA512 has been added in GLib 2.42. + * Support for %G_CHECKSUM_SHA384 was added in GLib 2.52. + * ++ * Note that #GHmac creation may fail, in which case this function will ++ * return %NULL. Since there is no error parameter, it is not possible ++ * to indicate why. ++ * ++ * In Fedora, CentOS Stream, and Red Hat Enterprise Linux, GLib is ++ * configured to use GnuTLS to implement #GHmac in order to support FIPS ++ * compliance. This introduces additional failure possibilities that are ++ * not present in upstream GLib. For example, the creation of a #GHmac ++ * will fail if @digest_type is %G_CHECKSUM_MD5 and the system is ++ * running in FIPS mode. #GHmac creation may also fail if GLib is unable ++ * to load GnuTLS. ++ * + * Returns: the newly created #GHmac, or %NULL. + * Use g_hmac_unref() to free the memory allocated by it. + * +diff --git a/glib/meson.build b/glib/meson.build +index 329b8d197..2417de53d 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -252,7 +252,6 @@ glib_sources = files( + 'gfileutils.c', + 'ggettext.c', + 'ghash.c', +- 'ghmac.c', + 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', +@@ -308,6 +307,7 @@ glib_sources = files( + 'guriprivate.h', + 'gutils.c', + 'gutilsprivate.h', ++ 'gchecksumprivate.h', + 'guuid.c', + 'gvariant.c', + 'gvariant-core.c', +@@ -352,6 +352,12 @@ else + glib_dtrace_hdr = [] + endif + ++if get_option('gnutls') ++ glib_sources += files('ghmac-gnutls.c') ++else ++ glib_sources += files('ghmac.c') ++endif ++ + pcre_static_args = [] + + if use_pcre_static_flag +@@ -378,7 +384,7 @@ libglib = library('glib-2.0', + # intl.lib is not compatible with SAFESEH + link_args : [noseh_link_args, glib_link_flags, win32_ldflags], + include_directories : configinc, +- dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], ++ dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], + c_args : glib_c_args, + objc_args : glib_c_args, + ) +diff --git a/meson.build b/meson.build +index e2eba1871..cca15f653 100644 +--- a/meson.build ++++ b/meson.build +@@ -2090,6 +2090,13 @@ if host_system == 'linux' + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) + endif + ++# gnutls is used optionally by ghmac ++libgnutls_dep = [] ++if get_option('gnutls') ++ libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] ++ glib_conf.set('HAVE_GNUTLS', 1) ++endif ++ + if host_system == 'windows' + winsock2 = cc.find_library('ws2_32') + endif +diff --git a/meson_options.txt b/meson_options.txt +index 072765361..c8f26ac02 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -39,6 +39,11 @@ option('internal_pcre', + value : false, + description : 'whether to use internal PCRE') + ++option('gnutls', ++ type : 'boolean', ++ value : false, ++ description : 'build with gnutls support') ++ + option('man', + type : 'boolean', + value : false, +-- +2.31.1 + +From 61c175277acb8d1e080305acd444201c5ad1fb81 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 16 Jun 2021 20:35:00 -0500 +Subject: [PATCH 3/4] dlopen GnuTLS instead of linking directly + +I'd like to enable our GnuTLS GHmac patchset in Fedora in order to +ensure it is receiving sufficient real-world testing, since we've +discovered several bugs thus far. Problem is Fedora has one requirement +that RHEL does not: it needs to build glib as a static lib. This is +needed by QEMU in Fedora for complicated technical reasons that I don't +understand. However, nothing in RHEL needs it. This means we failed to +notice that glib2-static is broken in RHEL, because there is no +gnutls-static! We could fix this by adding a gnutls-static package, but +that seems like overkill, and adding more static libraries where they're +not truly necessary is not the direction we want to move in anyway. So +instead, let's just dlopen GnuTLS to sidestep this problem entirely. + +This would not be a good solution for upstream, but upstream has made +clear that this patchset is already non-upstreamable, so it will be fine +for our purposes. +--- + glib/ghmac-gnutls.c | 101 ++++++++++++++++++++++++++++++++++++++++++-- + glib/ghmac.c | 2 +- + glib/meson.build | 2 +- + meson.build | 6 +-- + 4 files changed, 102 insertions(+), 9 deletions(-) + +diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c +index 9fb775f89..1800fc2e0 100644 +--- a/glib/ghmac-gnutls.c ++++ b/glib/ghmac-gnutls.c +@@ -19,8 +19,8 @@ + + #include "config.h" + ++#include + #include +-#include + + #include "ghmac.h" + +@@ -31,13 +31,16 @@ + #include "gstrfuncs.h" + #include "gchecksumprivate.h" + #include "gtestutils.h" ++#include "gthread.h" + #include "gtypes.h" + #include "glibintl.h" + +-#ifndef HAVE_GNUTLS ++#ifndef USE_GNUTLS + #error "build configuration error" + #endif + ++typedef gpointer gnutls_hmac_hd_t; ++ + struct _GHmac + { + int ref_count; +@@ -46,15 +49,107 @@ struct _GHmac + gchar *digest_str; + }; + ++typedef enum ++{ ++ GNUTLS_MAC_MD5 = 2, ++ GNUTLS_MAC_SHA1 = 3, ++ GNUTLS_MAC_SHA256 = 6, ++ GNUTLS_MAC_SHA384 = 7, ++ GNUTLS_MAC_SHA512 = 8, ++} gnutls_mac_algorithm_t; ++ ++/* Why are we dlopening GnuTLS instead of linking to it directly? Because we ++ * want to be able to build GLib as a static library without depending on a ++ * static build of GnuTLS. QEMU depends on static linking with GLib, but Fedora ++ * does not ship a static build of GnuTLS, and this allows us to avoid changing ++ * that. ++ */ ++static int (*gnutls_hmac_init) (gnutls_hmac_hd_t *dig, gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen); ++static gnutls_hmac_hd_t (*gnutls_hmac_copy) (gnutls_hmac_hd_t handle); ++static void (*gnutls_hmac_deinit) (gnutls_hmac_hd_t handle, void *digest); ++static int (*gnutls_hmac) (gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len); ++static void (*gnutls_hmac_output) (gnutls_hmac_hd_t handle, void *digest); ++static const char * (*gnutls_strerror) (int error); ++ ++static gsize gnutls_initialize_attempted = 0; ++static gboolean gnutls_initialize_successful = FALSE; ++ ++static void ++initialize_gnutls (void) ++{ ++ gpointer libgnutls; ++ ++ libgnutls = dlopen ("libgnutls.so.30", RTLD_LAZY | RTLD_GLOBAL); ++ if (!libgnutls) ++ { ++ g_warning ("Cannot use GHmac: failed to load libgnutls.so.30: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_init = dlsym (libgnutls, "gnutls_hmac_init"); ++ if (!gnutls_hmac_init) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_init: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_copy = dlsym (libgnutls, "gnutls_hmac_copy"); ++ if (!gnutls_hmac_copy) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_copy: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_deinit = dlsym (libgnutls, "gnutls_hmac_deinit"); ++ if (!gnutls_hmac_deinit) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_deinit: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac = dlsym (libgnutls, "gnutls_hmac"); ++ if (!gnutls_hmac) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_hmac_output = dlsym (libgnutls, "gnutls_hmac_output"); ++ if (!gnutls_hmac_output) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_hmac_output: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_strerror = dlsym (libgnutls, "gnutls_strerror"); ++ if (!gnutls_strerror) ++ { ++ g_warning ("Cannot use GHmac: failed to load gnutls_strerror: %s", dlerror ()); ++ return; ++ } ++ ++ gnutls_initialize_successful = TRUE; ++} ++ + GHmac * + g_hmac_new (GChecksumType digest_type, + const guchar *key, + gsize key_len) + { + gnutls_mac_algorithm_t algo; +- GHmac *hmac = g_new0 (GHmac, 1); ++ GHmac *hmac; + int ret; + ++ if (g_once_init_enter (&gnutls_initialize_attempted)) ++ { ++ initialize_gnutls (); ++ g_once_init_leave (&gnutls_initialize_attempted, 1); ++ } ++ ++ if (!gnutls_initialize_successful) ++ return NULL; ++ ++ hmac = g_new0 (GHmac, 1); + hmac->ref_count = 1; + hmac->digest_type = digest_type; + +diff --git a/glib/ghmac.c b/glib/ghmac.c +index 0e39ea40a..2d9be91b8 100644 +--- a/glib/ghmac.c ++++ b/glib/ghmac.c +@@ -33,7 +33,7 @@ + #include "gtypes.h" + #include "glibintl.h" + +-#ifdef HAVE_GNUTLS ++#ifdef USE_GNUTLS + #error "build configuration error" + #endif + +diff --git a/glib/meson.build b/glib/meson.build +index 2417de53d..ba42951aa 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -384,7 +384,7 @@ libglib = library('glib-2.0', + # intl.lib is not compatible with SAFESEH + link_args : [noseh_link_args, glib_link_flags, win32_ldflags], + include_directories : configinc, +- dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], ++ dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep] + [libdl_dep], + c_args : glib_c_args, + objc_args : glib_c_args, + ) +diff --git a/meson.build b/meson.build +index cca15f653..404ef1790 100644 +--- a/meson.build ++++ b/meson.build +@@ -2090,11 +2090,9 @@ if host_system == 'linux' + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) + endif + +-# gnutls is used optionally by ghmac +-libgnutls_dep = [] ++# gnutls is used optionally by GHmac + if get_option('gnutls') +- libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] +- glib_conf.set('HAVE_GNUTLS', 1) ++ glib_conf.set('USE_GNUTLS', 1) + endif + + if host_system == 'windows' +-- +2.31.1 + +From 7d1d96311b6ecd4f90ebbdd6fc58d28e06a86887 Mon Sep 17 00:00:00 2001 +From: Michael Catanzaro +Date: Wed, 16 Jun 2021 20:46:24 -0500 +Subject: [PATCH 4/4] Add test for GHmac in FIPS mode + +This will test a few problems that we hit recently: + +g_hmac_copy() is broken, https://bugzilla.redhat.com/show_bug.cgi?id=1786538 + +Crash in g_hmac_update() in FIPS mode, https://bugzilla.redhat.com/show_bug.cgi?id=1971533 + +Crash when passing -1 length to g_hmac_update() (discovered in #1971533) + +We'll also test to ensure MD5 fails, and stop compiling the other MD5 +tests. +--- + glib/tests/hmac.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 46 insertions(+) + +diff --git a/glib/tests/hmac.c b/glib/tests/hmac.c +index 3ac3206df..2fa447984 100644 +--- a/glib/tests/hmac.c ++++ b/glib/tests/hmac.c +@@ -1,7 +1,10 @@ ++#include "config.h" ++ + #include + #include + #include + ++#ifndef USE_GNUTLS + /* HMAC-MD5 test vectors as per RFC 2202 */ + + /* Test 1 */ +@@ -81,6 +84,7 @@ guint8 key_md5_test7[] = { + guint8 result_md5_test7[] = { + 0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee, 0x1f, 0xb1, + 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e }; ++#endif + + /* HMAC-SHA1, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512 test vectors + * as per RFCs 2202 and 4868. +@@ -299,6 +303,7 @@ typedef struct { + gconstpointer result; + } HmacCase; + ++#ifndef USE_GNUTLS + HmacCase hmac_md5_tests[] = { + { G_CHECKSUM_MD5, key_md5_test1, 16, "Hi There", 8, result_md5_test1 }, + { G_CHECKSUM_MD5, "Jefe", 4, "what do ya want for nothing?", 28, +@@ -317,6 +322,7 @@ HmacCase hmac_md5_tests[] = { + 73, result_md5_test7 }, + { -1, NULL, 0, NULL, 0, NULL }, + }; ++#endif + + HmacCase hmac_sha1_tests[] = { + { G_CHECKSUM_SHA1, key_sha_test1, 20, "Hi There", 8, result_sha1_test1 }, +@@ -493,11 +499,45 @@ test_hmac_for_bytes (void) + g_bytes_unref (data); + } + ++#ifdef USE_GNUTLS ++static void ++test_gnutls_fips_mode (void) ++{ ++ GHmac *hmac; ++ GHmac *copy; ++ ++ /* No MD5 in FIPS mode. */ ++ hmac = g_hmac_new (G_CHECKSUM_MD5, "abc123", sizeof ("abc123")); ++ g_assert_null (hmac); ++ ++ /* SHA-256 should be good. */ ++ hmac = g_hmac_new (G_CHECKSUM_SHA256, "abc123", sizeof ("abc123")); ++ g_assert_nonnull (hmac); ++ ++ /* Ensure g_hmac_update() does not crash when called with -1. */ ++ g_hmac_update (hmac, "You win again, gravity!", -1); ++ ++ /* Ensure g_hmac_copy() does not crash. */ ++ copy = g_hmac_copy (hmac); ++ g_assert_nonnull (hmac); ++ g_hmac_unref (hmac); ++ ++ g_assert_cmpstr (g_hmac_get_string (copy), ==, "795ba6900bcb22e8ce65c2ec02db4e85697da921deb960ee3143bf88a4a60f83"); ++ g_hmac_unref (copy); ++} ++#endif ++ + int + main (int argc, + char **argv) + { + int i; ++ ++#ifdef USE_GNUTLS ++ /* This has to happen before GnuTLS is dlopened. */ ++ g_setenv ("GNUTLS_FORCE_FIPS_MODE", "1", FALSE); ++#endif ++ + g_test_init (&argc, &argv, NULL); + + for (i = 0 ; hmac_sha1_tests[i].key_len > 0 ; i++) +@@ -532,6 +572,7 @@ main (int argc, + g_free (name); + } + ++#ifndef USE_GNUTLS + for (i = 0 ; hmac_md5_tests[i].key_len > 0 ; i++) + { + gchar *name = g_strdup_printf ("/hmac/md5-%d", i + 1); +@@ -539,6 +580,7 @@ main (int argc, + (void (*)(const void *)) test_hmac); + g_free (name); + } ++#endif + + g_test_add_func ("/hmac/ref-unref", test_hmac_ref_unref); + g_test_add_func ("/hmac/copy", test_hmac_copy); +@@ -546,5 +588,9 @@ main (int argc, + g_test_add_func ("/hmac/for-string", test_hmac_for_string); + g_test_add_func ("/hmac/for-bytes", test_hmac_for_bytes); + ++#ifdef USE_GNUTLS ++ g_test_add_func ("/hmac/gnutls-fips-mode", test_gnutls_fips_mode); ++#endif ++ + return g_test_run (); + } +-- +2.31.1 diff --git a/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch b/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch new file mode 100644 index 0000000..39298db --- /dev/null +++ b/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch @@ -0,0 +1,47 @@ +From 07a8ac908cbadb22c344895ebf9cc00c6a8fd3f7 Mon Sep 17 00:00:00 2001 +From: Igor Gnatenko +Date: Wed, 29 Mar 2017 07:05:41 +0200 +Subject: [PATCH] don't add extra libraries for linking + +Signed-off-by: Igor Gnatenko +--- + lang/cpp/src/GpgmeppConfig.cmake.in.in | 2 +- + src/gpgme-config.in | 6 +++--- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in +index 73f5eaad..3104d715 100644 +--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in ++++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in +@@ -63,7 +63,7 @@ add_library(Gpgmepp SHARED IMPORTED) + + set_target_properties(Gpgmepp PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@" +- INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@;@LIBASSUAN_LIBS@" ++ INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@" + IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp@libsuffix@" + ) + +diff --git a/src/gpgme-config.in b/src/gpgme-config.in +index a4d152e1..6a854e4a 100644 +--- a/src/gpgme-config.in ++++ b/src/gpgme-config.in +@@ -22,12 +22,12 @@ cflags="-I@includedir@" + libs="-L@libdir@" + + # Network libraries. +-assuan_cflags="@LIBASSUAN_CFLAGS@" +-assuan_libs="@LIBASSUAN_LIBS@" ++#assuan_cflags="@LIBASSUAN_CFLAGS@" ++#assuan_libs="@LIBASSUAN_LIBS@" + + # Configure libgpg-error. + gpg_error_cflags="@GPG_ERROR_CFLAGS@" +-gpg_error_libs="@GPG_ERROR_LIBS@" ++#gpg_error_libs="@GPG_ERROR_LIBS@" + + # Configure thread packages. + thread_modules="" +-- +2.15.1 + diff --git a/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch b/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch new file mode 100644 index 0000000..c4f87c5 --- /dev/null +++ b/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch @@ -0,0 +1,112 @@ +From b0eabea4b1232ee7f45d13b8add928d463f37444 Mon Sep 17 00:00:00 2001 +From: Igor Gnatenko +Date: Wed, 29 Mar 2017 07:13:35 +0200 +Subject: [PATCH] fix stupid ax_python_devel + +References: https://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=commit;h=883a2abd5af5c96be894d5ef7ee6e9a2b8e64307 +Signed-off-by: Igor Gnatenko +--- + m4/ax_python_devel.m4 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) +diff -up gpgme-1.22.0/configure.stupid gpgme-1.22.0/configure +--- gpgme-1.22.0/configure.stupid 2023-08-21 09:46:33.000000000 +0200 ++++ gpgme-1.22.0/configure 2023-10-10 12:31:08.317796779 +0200 +@@ -23374,7 +23374,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -24146,7 +24146,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -24918,7 +24918,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -25690,7 +25690,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -26462,7 +26462,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -27234,7 +27234,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -28006,7 +28006,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -28778,7 +28778,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -29550,7 +29550,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -30322,7 +30322,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else +@@ -31094,7 +31094,7 @@ variable to configure. See \`\`configure + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 + printf %s "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` +- if test -z "$ac_distutils_result"; then ++ if test $? -eq 0; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + printf "%s\n" "yes" >&6; } + else diff --git a/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch b/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch new file mode 100644 index 0000000..dbcd4a7 --- /dev/null +++ b/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch @@ -0,0 +1,12 @@ +diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in +index c0fc091..d567ecb 100644 +--- a/lang/python/Makefile.in ++++ b/lang/python/Makefile.in +@@ -802,6 +802,7 @@ install-exec-local: + --build-base="$$(basename "$${PYTHON}")-gpg" \ + install \ + --prefix "$(DESTDIR)$(prefix)" \ ++ $${SETUP_PY_EXTRA_OPTS:-} \ + --verbose ; \ + done + diff --git a/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch b/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch new file mode 100644 index 0000000..5588205 --- /dev/null +++ b/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch @@ -0,0 +1,58 @@ +diff --git a/lang/qt/tests/t-remarks.cpp b/lang/qt/tests/t-remarks.cpp +index f9a901a..4b2f2ab 100644 +--- a/lang/qt/tests/t-remarks.cpp ++++ b/lang/qt/tests/t-remarks.cpp +@@ -47,6 +47,12 @@ + + #include "t-support.h" + ++#define SKIP_ON_2_4() do { \ ++ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.4.0") { \ ++ QSKIP("The test does not work well with gnupg 2.4+."); \ ++ } \ ++} while (false) ++ + using namespace QGpgME; + using namespace GpgME; + +@@ -105,6 +111,7 @@ private Q_SLOTS: + + void testRemarkReplaceSingleUIDExportable() + { ++ SKIP_ON_2_4(); + if (!loopbackSupported()) { + return; + } +@@ -187,6 +194,7 @@ private Q_SLOTS: + + void testMultipleRemarks() + { ++ SKIP_ON_2_4(); + if (!loopbackSupported()) { + return; + } +@@ -269,6 +277,7 @@ private Q_SLOTS: + + void testRemarkReplaceSingleUID() + { ++ SKIP_ON_2_4(); + if (!loopbackSupported()) { + return; + } +@@ -350,6 +359,7 @@ private Q_SLOTS: + + void testRemarkReplaceMultiUID() + { ++ SKIP_ON_2_4(); + if (!loopbackSupported()) { + return; + } +@@ -466,6 +476,8 @@ private: + QTemporaryDir mDir; + }; + ++#undef SKIP_ON_2_4 ++ + QTEST_MAIN(TestRemarks) + + #include "t-remarks.moc" diff --git a/base/rx/rx-gpgme/changelog b/base/rx/rx-gpgme/changelog new file mode 100644 index 0000000..5ab404d --- /dev/null +++ b/base/rx/rx-gpgme/changelog @@ -0,0 +1,380 @@ +* Fri Jun 16 2023 Marie Loise Nolden - 1.20.0-1 +- Update to 1.20.0 and enable/add qt6 build for qgpgme. The qt5 and qt6 + rpms have been split out into separate libs and -devel rpms for compatibility. + +* Tue Nov 02 2021 Frantisek Sumsal - 1.15.1-6 +- Fix build with glibc >=2.34 (RHBZ#1984691, RHBZ#1987561) + +* Thu Jul 22 2021 Fedora Release Engineering - 1.15.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Thu Jun 03 2021 Python Maint - 1.15.1-4 +- Rebuilt for Python 3.10 + +* Wed Jun 02 2021 Miro Hrončok - 1.15.1-3 +- Also remove RPATH from /usr/bin/gpgme-json + +* Tue Jan 26 2021 Fedora Release Engineering - 1.15.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Sun Jan 24 2021 Igor Raits - 1.15.1-1 +- Update to 1.15.1 + +* Tue Jul 28 2020 Fedora Release Engineering - 1.14.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Sat Jul 18 2020 Igor Raits - 1.14.0-1 +- Update to 1.14.0 + +* Fri May 22 2020 Miro Hrončok - 1.13.1-8 +- Rebuilt for Python 3.9 + +* Thu Apr 30 2020 Tomáš Mráz - 1.13.1-7 +- Fix FTBFS with gnupg-2.2.19 and above + +* Wed Jan 29 2020 Fedora Release Engineering - 1.13.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Oct 03 2019 Miro Hrončok - 1.13.1-5 +- Rebuilt for Python 3.8.0rc1 (#1748018) + +* Thu Aug 15 2019 Miro Hrončok - 1.13.1-4 +- Rebuilt for Python 3.8 + +* Sat Aug 10 2019 Igor Gnatenko - 1.13.1-3 +- Set real VERSION + +* Sat Aug 3 2019 Peter Robinson 1.13.1-2 +- Move .pc files to devel so the base library doesn't pull in devel packages + +* Mon Jul 29 18:46:42 CEST 2019 Igor Gnatenko - 1.13.1-1 +- Update to 1.13.1 + +* Thu Jul 25 2019 Fedora Release Engineering - 1.12.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Wed Jun 05 2019 Miro Hrončok - 1.12.0-2 +- Subpackage python2-gpg has been removed + See https://fedoraproject.org/wiki/Changes/Mass_Python_2_Package_Removal + +* Sat Feb 16 2019 Igor Gnatenko - 1.12.0-1 +- Update to 1.12.0 + +* Fri Feb 01 2019 Fedora Release Engineering - 1.11.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Jul 13 2018 Fedora Release Engineering - 1.11.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Fri Jun 15 2018 Miro Hrončok - 1.11.1-2 +- Rebuilt for Python 3.7 + +* Fri Apr 20 2018 Igor Gnatenko - 1.11.1-1 +- Update to 1.11.1 + +* Thu Apr 19 2018 Igor Gnatenko - 1.11.0-1 +- Update to 1.11.0 + +* Tue Apr 17 2018 Jonathan Lebon - 1.10.0-4 +- Backport patch to tweak STATUS_FAILURE handling + +* Wed Feb 07 2018 Fedora Release Engineering - 1.10.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Tue Jan 30 2018 Igor Gnatenko - 1.10.0-2 +- Switch to %%ldconfig_scriptlets + +* Wed Dec 13 2017 Igor Gnatenko - 1.10.0-1 +- Update to 1.10.0 + +* Tue Nov 07 2017 Igor Gnatenko - 1.9.0-8 +- Use better Obsoletes for platform-python + +* Fri Nov 03 2017 Igor Gnatenko - 1.9.0-7 +- Remove platform-python subpackages + +* Thu Aug 10 2017 Petr Viktorin - 1.9.0-6 +- Add subpackage for platform-python (https://fedoraproject.org/wiki/Changes/Platform_Python_Stack) + +* Mon Aug 07 2017 Igor Gnatenko - 1.9.0-5 +- Remove BuildRequires: pth-devel, it is not needed for long time + +* Mon Aug 07 2017 Björn Esser - 1.9.0-4 +- Rebuilt for AutoReq cmake-filesystem + +* Wed Aug 02 2017 Fedora Release Engineering - 1.9.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1.9.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Mar 29 2017 Igor Gnatenko - 1.9.0-1 +- Update to 1.9.0 + +* Sat Feb 11 2017 Igor Gnatenko - 1.8.0-12 +- Fix FTBFS + +* Fri Feb 10 2017 Fedora Release Engineering - 1.8.0-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Jan 18 2017 Rex Dieter - 1.8.0-10 +- patch out LIBASSUAN_LIBRARIES in cmake too + +* Wed Jan 18 2017 Rex Dieter - 1.8.0-9 +- gpgmepp-devel: Requires: libassuan-devel + +* Mon Jan 16 2017 Rex Dieter - 1.8.0-8 +- qgpgme-devel: Conflicts: kdepimlibs-devel < 4.14.10-17 + +* Sun Jan 01 2017 Rex Dieter - 1.8.0-7 +- rename gpgme-pp to gpgmepp, simplify -devel deps + +* Sun Jan 01 2017 Rex Dieter - 1.8.0-6 +- backport upstream cmake-related fix + +* Thu Dec 22 2016 Miro Hrončok - 1.8.0-5 +- Rebuild for Python 3.6 + +* Sun Dec 11 2016 Igor Gnatenko - 1.8.0-4 +- Rename pythonX-gpgme into pythonX-gpg + +* Sun Dec 11 2016 Igor Gnatenko - 1.8.0-3 +- Add Qt and C++ subpackages + +* Sat Dec 10 2016 Igor Gnatenko - 1.8.0-2 +- Enable tests + +* Sat Dec 10 2016 Igor Gnatenko - 1.8.0-1 +- Update to 1.8.0 + +* Wed Sep 21 2016 Igor Gnatenko - 1.7.0-1 +- Update to 1.7.0 + +* Mon Jul 25 2016 Igor Gnatenko - 1.6.0-3 +- Set min ver for libgpg-error + +* Mon Jul 25 2016 Igor Gnatenko - 1.6.0-2 +- Backport patch for STATUS_KEY_CONSIDERED (RHBZ #1359521) + +* Wed Jul 13 2016 Igor Gnatenko - 1.6.0-1 +- Update to 1.6.0 (RHBZ #1167656) + +* Wed Feb 03 2016 Fedora Release Engineering - 1.4.3-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jun 17 2015 Fedora Release Engineering - 1.4.3-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Sat Dec 06 2014 Frantisek Kluknavsky - 1.4.3-5 +- CVE-2014-3564, rhbz#1125170, gpgme-1.3.2-bufferoverflow.patch + +* Sat Aug 16 2014 Fedora Release Engineering - 1.4.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jul 12 2014 Tom Callaway - 1.4.3-3 +- fix license handling + +* Sat Jun 07 2014 Fedora Release Engineering - 1.4.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed Oct 09 2013 Rex Dieter - 1.4.3-1 +- gpgme-1.4.3 +- cleanup .spec, trim changelog + +* Sat Aug 03 2013 Fedora Release Engineering - 1.3.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 09 2013 Karsten Hopp 1.3.2-3 +- rebuild to fix some f20 dependency issues on PPC + +* Thu Feb 14 2013 Fedora Release Engineering - 1.3.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Tue Nov 20 2012 Frantisek Kluknavsky - 1.3.2-2 +- minor spec cleanup + +* Wed Sep 26 2012 Tomas Mraz - 1.3.2-1 +- new upstream version +- re-enable gpg tests (original patch by John Morris ) +- quiet configure warning 'could not find g13' +- there is no libgpgme-pth anymore + +* Thu Jul 19 2012 Fedora Release Engineering - 1.3.0-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sun Apr 22 2012 Rex Dieter 1.3.0-8 +- -devel: make Requires: libgpg-error-devel arch'd +- ensure gpgme-config wrapper is executable + +* Sun Apr 22 2012 Rex Dieter 1.3.0-7 +- gpgme.h: fatal error: gpgme-i386.h: No such file or directory compilation terminated (#815116) + +* Wed Feb 15 2012 Simon Lukasik - 1.3.0-6 +- Resolve multilib conflict of gpgme-config (#341351) +- Resolve multilib conflict of gpgme.h (#341351) + +* Fri Jan 13 2012 Fedora Release Engineering - 1.3.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Mar 17 2011 Rex Dieter - 1.3.0-4 +- gpgme-config: remove libassuan-related flags as threatened (#676954) +\ +* Sun Feb 13 2011 Rex Dieter - 1.3.0-3 +- -devel: fix typo (broken dep) + +* Sat Feb 12 2011 Rex Dieter - 1.3.0-2 +- BR: libassuan2-devel +- gpgme-config outputs -lassuan (#676954) + +* Fri Feb 11 2011 Tomas Mraz - 1.3.0-1 +- new upstream version + +* Tue Feb 08 2011 Fedora Release Engineering - 1.2.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Aug 18 2010 Tomas Mraz - 1.2.0-3 +- fix the condition for adding the -D_FILE_OFFSET_BITS... + +* Wed Aug 11 2010 Tomas Mraz - 1.2.0-2 +- add -D_FILE_OFFSET_BITS... to gpgme-config as appropriate (#621698) + +* Fri Jul 02 2010 Rex Dieter - 1.2.0-1 +- gpgme-1.2.0 (#610984) + +* Sun Feb 14 2010 Rex Dieter - 1.1.8-4 +- FTBFS gpgme-1.1.8-3.fc13: ImplicitDSOLinking (#564605) + +* Thu Nov 19 2009 Tomas Mraz - 1.1.8-3 +- Add buildrequires gnupg2-smime for the gpgsm + +* Fri Jul 24 2009 Fedora Release Engineering - 1.1.8-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Sat Jun 20 2009 Rex Dieter - 1.1.8-1 +- gpgme-1.1.8 +- -devel: s/postun/preun/ info scriptlet + +* Wed Mar 11 2009 Rex Dieter - 1.1.7-3 +- track shlib sonames closer, to highlight future abi/soname changes +- _with_gpg macro, to potentially conditionalize gnupg vs gnupg2 defaults + for various os/releases (ie, fedora vs rhel) + +* Tue Feb 24 2009 Fedora Release Engineering - 1.1.7-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Oct 18 2008 Rex Dieter 1.1.7-1 +- gpgme-1.1.7 + +* Sun Feb 17 2008 Rex Dieter 1.1.6-3 +- --with-gpg=%%_bindir/gpg2 (#432445) +- drop Requires: gnupg (#432445) + +* Fri Feb 08 2008 Rex Dieter 1.1.6-2 +- respin (gcc43) + +* Fri Jan 04 2008 Rex Dieter 1.1.6-1 +- gpgme-1.1.6 +- multiarch conflicts in gpgme (#341351) + +* Sat Aug 25 2007 Rex Dieter 1.1.5-4 +- BR: gawk + +* Sat Aug 25 2007 Rex Dieter 1.1.5-3 +- respin (BuildID) + +* Thu Aug 09 2007 Rex Dieter 1.1.5-2 +- License: LGPLv2+ + +* Mon Jul 09 2007 Rex Dieter 1.1.5-1 +- gpgme-1.1.5 + +* Mon Mar 05 2007 Rex Dieter 1.1.4-1 +- gpgme-1.1.4 + +* Sat Feb 03 2007 Rex Dieter 1.1.3-1 +- gpgme-1.1.3 + +* Tue Oct 03 2006 Rex Dieter +- respin + +* Mon Sep 18 2006 Rex Dieter 1.1.2-6 +- fix gpgme-config --thread=pthread --cflags + +* Tue Aug 29 2006 Rex Dieter 1.1.2-5 +- fc6 respin + +* Mon Mar 6 2006 Rex Dieter 1.1.2-4 +- add back support for gpgme-config --thread=pthread + +* Mon Mar 6 2006 Rex Dieter 1.1.2-2 +- drop extraneous libs from gpgme-config + +* Fri Mar 3 2006 Rex Dieter 1.1.2-1 +- 1.1.2 +- drop upstreamed gpgme-1.1.0-tests.patch + +* Wed Mar 1 2006 Rex Dieter +- fc5: gcc/glibc respin + +* Wed Nov 30 2005 Rex Dieter - 1.1.0-3 +- (re)build against (newer) libksba/gnupg2 + +* Thu Oct 06 2005 Rex Dieter - 1.1.0-2 +- 1.1.0 + +* Mon Aug 8 2005 Rex Dieter - 1.0.3-1 +- 1.0.3 +- --disable-static + +* Thu May 12 2005 Michael Schwendt - 1.0.2-3 +- rebuilt + +* Fri Mar 18 2005 Ville Skyttä - 1.0.2-2 +- Fix FC4 build. + +* Tue Feb 1 2005 Michael Schwendt - 0:1.0.2-1 +- LGPL used here, and made summary more explicit. +- Remove dirmngr dependency (gpgsm interfaces with it). +- Obsolete cryptplug as gpgme >= 0.4.5 provides what we used cryptplug for. + +* Thu Jan 06 2005 Rex Dieter 0:1.0.2-0.fdr.1 +- 1.0.2 + +* Thu Oct 21 2004 Rex Dieter 0:1.0.0-0.fdr.1 +- 1.0.0 +- Requires: dirmngr + +* Tue Oct 19 2004 Rex Dieter 0:0.4.7-0.fdr.1 +- 0.4.7 + +* Sun May 2 2004 Ville Skyttä - 0:0.4.3-0.fdr.3 +- Require %%{_bindir}/gpgsm instead of newpg. +- Cosmetic spec file improvements. + +* Thu Oct 23 2003 Ville Skyttä - 0:0.4.3-0.fdr.2 +- Update description. + +* Tue Oct 7 2003 Ville Skyttä - 0:0.4.3-0.fdr.1 +- Update to 0.4.3. + +* Fri Aug 15 2003 Ville Skyttä - 0:0.4.2-0.fdr.1 +- Update to 0.4.2. +- make check in the %%check section. + +* Thu Jul 10 2003 Ville Skyttä - 0:0.4.1-0.fdr.1 +- Update to 0.4.1. +- Make -devel cooperate with --excludedocs. + +* Sat Apr 19 2003 Ville Skyttä - 0:0.4.0-0.fdr.2 +- BuildRequire pth-devel, fix missing epoch in -devel Requires (#169). +- Save .spec in UTF-8. + +* Sat Mar 22 2003 Ville Skyttä - 0:0.4.0-0.fdr.1 +- Update to current Fedora guidelines. +- Exclude %%{_libdir}/*.la. + +* Tue Feb 12 2003 Warren Togami 0.4.0-1.fedora.3 +- info/dir temporary workaround + +* Sat Feb 8 2003 Ville Skyttä - 0.4.0-1.fedora.1 +- First Fedora release. diff --git a/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch b/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch new file mode 100644 index 0000000..bbf88de --- /dev/null +++ b/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch @@ -0,0 +1,24 @@ +diff -up gpgme-1.3.2/src/gpgme-config.in.largefile gpgme-1.3.2/src/gpgme-config.in +--- gpgme-1.3.2/src/gpgme-config.in.largefile 2012-09-26 10:10:37.882744198 +0200 ++++ gpgme-1.3.2/src/gpgme-config.in 2012-09-26 10:16:02.558762827 +0200 +@@ -41,6 +41,10 @@ cflags_pthread="" + cflags_glib="@GLIB_CFLAGS@" + with_glib= + ++if test "0@NEED__FILE_OFFSET_BITS@" -gt "0" ; then ++ cflags_lfs="-D_FILE_OFFSET_BITS=@NEED__FILE_OFFSET_BITS@" ++fi ++ + output="" + + usage() +@@ -105,6 +109,9 @@ while test $# -gt 0; do + exit 0 + ;; + --cflags) ++ if test "x$cflags_lfs" != "x"; then ++ output="$output $cflags_lfs" ++ fi + result= + tmp_c= + tmp_g= diff --git a/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch b/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch new file mode 100644 index 0000000..6188066 --- /dev/null +++ b/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch @@ -0,0 +1,24 @@ +diff -Naur a/configure b/configure +--- a/configure 2025-02-10 16:01:00.000000000 +0600 ++++ b/configure 2025-02-18 10:34:48.662943905 +0600 +@@ -3570,7 +3570,7 @@ + GPGME_CONFIG_API_VERSION=1 + ############################################## + +-NEED_GPG_ERROR_VERSION=1.47 ++NEED_GPG_ERROR_VERSION=1.31 + NEED_LIBASSUAN_API=2 + NEED_LIBASSUAN_VERSION=2.4.2 + +diff -Naur a/configure.ac b/configure.ac +--- a/configure.ac 2025-02-10 15:57:10.000000000 +0600 ++++ b/configure.ac 2025-02-18 10:34:59.988883253 +0600 +@@ -87,7 +87,7 @@ + GPGME_CONFIG_API_VERSION=1 + ############################################## + +-NEED_GPG_ERROR_VERSION=1.47 ++NEED_GPG_ERROR_VERSION=1.31 + NEED_LIBASSUAN_API=2 + NEED_LIBASSUAN_VERSION=2.4.2 + diff --git a/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch b/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch new file mode 100644 index 0000000..0b7cacb --- /dev/null +++ b/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch @@ -0,0 +1,31 @@ +diff -Naur a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in +--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in 2025-02-10 15:52:23.000000000 +0600 ++++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in 2025-02-18 10:36:13.788488048 +0600 +@@ -63,7 +63,7 @@ + + set_target_properties(Gpgmepp PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@" +- INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@;@LIBASSUAN_LIBS@" ++ INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@" + IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp@libsuffix@" + ) + +diff -Naur a/src/gpgme-config.in b/src/gpgme-config.in +--- a/src/gpgme-config.in 2018-12-03 15:37:25.000000000 +0600 ++++ b/src/gpgme-config.in 2025-02-18 10:36:59.981240681 +0600 +@@ -25,12 +25,12 @@ + libs="-L@libdir@" + + # Network libraries. +-assuan_cflags="@LIBASSUAN_CFLAGS@" +-assuan_libs="@LIBASSUAN_LIBS@" ++#assuan_cflags="@LIBASSUAN_CFLAGS@" ++#assuan_libs="@LIBASSUAN_LIBS@" + + # Configure libgpg-error. + gpg_error_cflags="@GPG_ERROR_CFLAGS@" +-gpg_error_libs="@GPG_ERROR_LIBS@" ++#gpg_error_libs="@GPG_ERROR_LIBS@" + + # Configure thread packages. + thread_modules="" diff --git a/base/rx/rx-gpgme/gpgme-multilib.h b/base/rx/rx-gpgme/gpgme-multilib.h new file mode 100644 index 0000000..574f09d --- /dev/null +++ b/base/rx/rx-gpgme/gpgme-multilib.h @@ -0,0 +1,20 @@ +/* gpgme-multilib.h */ +/* This file is here to prevent a file conflict on multiarch systems. A + * conflict will occur because gpgme.h has arch-specific definitions. + * + * DO NOT INCLUDE THE NEW FILE DIRECTLY -- ALWAYS INCLUDE THIS ONE INSTEAD. */ + +#ifndef GPGME_MULTILIB_H +#define GPGME_MULTILIB_H +#include + +#if __WORDSIZE == 32 +#include "gpgme-32.h" +#elif __WORDSIZE == 64 +#include "gpgme-64.h" +#else +#error "unexpected value for __WORDSIZE macro" +#endif + +#endif + diff --git a/base/rx/rx-gpgme/gpgme.spec b/base/rx/rx-gpgme/gpgme.spec new file mode 100644 index 0000000..3bb8fa8 --- /dev/null +++ b/base/rx/rx-gpgme/gpgme.spec @@ -0,0 +1,412 @@ +%bcond_with check +# No Qt5 on RHEL 10 and higher +%bcond_without qt5 +%bcond_without qt6 +%bcond_with python + +%global base_name gpgme +%global gnupg2_min_ver 2.2.24 +%global libgpg_error_min_ver 1.47 + +# we are doing out of source build +%global _configure ../configure + +# brpldconfig creates links that conflict with original gpgme +%undefine __brp_ldconfig + +# We are using python3.11, make sure sitearch is correct +%global python3_pkgversion 3.11 + +%global _syslibdir %{_libdir} +%global _sysincludedir %{_includedir} + +%global _prefix /opt/rx +%global _sysconfdir %{_prefix}/etc +%global _includedir %{_prefix}/include +%global _libdir %{_prefix}/%{_lib} +%global _sbindir %{_prefix}/sbin +%global _datadir %{_prefix}/share +%global _mandir %{_datadir}/man +%global _docdir %{_datadir}/doc +%global _infodir %{_datadir}/info + +Name: rx-gpgme +Summary: GnuPG Made Easy - high level crypto API - version 1.23 +Version: 1.24.2 +Release: 1%{?dist} + +# MIT: src/cJSON.{c,h} (used by gpgme-json) +License: LGPL-2.1-or-later AND MIT +URL: https://gnupg.org/related_software/gpgme/ +Source0: https://gnupg.org/ftp/gcrypt/gpgme/gpgme-%{version}.tar.bz2 +Source2: gpgme-multilib.h + +## downstream patches +# Don't add extra libs/cflags in gpgme-config/cmake equivalent +Patch1001: 0001-don-t-add-extra-libraries-for-linking.patch +# add -D_FILE_OFFSET_BITS... to gpgme-config, upstreamable +Patch1002: gpgme-1.3.2-largefile.patch +# Let's fix stupid AX_PYTHON_DEVEL +#Patch1003: 0001-fix-stupid-ax_python_devel.patch +# Allow extra options to be passed to setup.py during installation +Patch1004: 0002-setup_py_extra_opts.patch + +%if 0%{?rhel} < 9 +# Adjust libgpg-error version +Patch1100: gpgme-downgrade-libgpg-error-version.patch +%endif + +## temporary downstream fixes +# Skip lang/qt/tests/t-remarks on gnupg 2.4+ +Patch3001: 1001-qt-skip-test-remarks-for-gnupg2-2.4.patch + +BuildRequires: make +BuildRequires: cmake +#BuildRequires: gcc +BuildRequires: clang +BuildRequires: gawk +BuildRequires: texinfo +BuildRequires: rx-gnupg2 >= %{gnupg2_min_ver} +BuildRequires: rx-gnupg2-smime +BuildRequires: pkgconfig(gpg-error) >= %{libgpg_error_min_ver} +BuildRequires: libassuan-devel >= 2.4.2 + + +# For python bindings +BuildRequires: swig + +# to remove RPATH +BuildRequires: chrpath + +# For AutoReq cmake-filesystem +BuildRequires: cmake + +Requires: rx-gnupg2 >= %{gnupg2_min_ver} + +Provides: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +# On the following architectures workaround multiarch conflict of -devel packages: +%define multilib_arches %{ix86} x86_64 ia64 ppc ppc64 s390 s390x %{sparc} + +%description +GnuPG Made Easy (GPGME) is a library designed to make access to GnuPG +easier for applications. It provides a high-level crypto API for +encryption, decryption, signing, signature verification and key +management. + +%package devel +Summary: Development headers and libraries for %{name} +Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires: pkgconfig(gpg-error) >= %{libgpg_error_min_ver} + +# Conflicts with files in gpgme devel packages +Conflicts: gpgme-devel +Obsoletes: gpgme1.22-devel < %{?epoch:%{epoch}:}%{version}-%{release} + + +%description devel +%{summary}. + + +%package -n %{name}pp +Summary: C++ bindings/wrapper for GPGME +Provides: %{name}-pp = %{?epoch:%{epoch}:}%{version}-%{release} +Provides: %{name}-pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Provides: %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n %{name}pp +%{summary}. + +%package -n %{name}pp-devel +Summary: Development libraries and header files for %{name}-pp +Provides: %{name}-pp-devel = %{?epoch:%{epoch}:}%{version}-%{release} +Provides: %{name}-pp-devel%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires: %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires: %{name}-devel%{?_isa} + +# Conflicts with files in gpgme devel packages +Conflicts: gpgmepp-devel <= 1.16 +Obsoletes: gpgme1.22pp-devel < %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n %{name}pp-devel +%{summary} + +%if %{with qt5} +%package -n rx-q%{base_name}-qt5 +Summary: Qt5 API bindings/wrapper for GPGME +Requires: %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5Test) +Obsoletes: rx-q%{base_name} < %{?epoch:%{epoch}:}%{version}-%{release} +Provides: rx-q%{base_name} = %{?epoch:%{epoch}:}%{version}-%{release} + + +%description -n rx-q%{base_name}-qt5 +%{summary}. +%endif + +%if %{with qt6} +%package -n rx-q%{base_name}-qt6 +Summary: Qt6 API bindings/wrapper for GPGME +Requires: %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +BuildRequires: pkgconfig(Qt6Core) +BuildRequires: pkgconfig(Qt6Test) + +%description -n rx-q%{base_name}-qt6 +%{summary}. +%endif + + +%if %{with qt5} +%package -n rx-q%{base_name}-qt5-devel +Summary: Development libraries and header files for %{name}-qt5 +# before libqgpgme.so symlink was moved to avoid conflict +Conflicts: kdepimlibs-devel < 4.14.10-17 +Requires: rx-q%{base_name}-qt5%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} +Provides: rx-q%{base_name}-devel = %{?epoch:%{epoch}:}%{version}-%{release} +Provides: rx-q%{base_name}-devel%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +# Conflicts with files in gpgme devel packages +Conflicts: qgpgme-devel +Obsoletes: qgpgme1.22-qt5-devel < %{?epoch:%{epoch}:}%{version}-%{release} + +Obsoletes: rx-q%{base_name}-common-devel < %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: q%{base_name}1.22-common-devel < %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n rx-q%{base_name}-qt5-devel +%{summary}. +%endif + +%if %{with qt6} +%package -n rx-q%{base_name}-qt6-devel +Summary: Development libraries and header files for %{name}-qt6 +Requires: rx-q%{base_name}-qt6%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +Obsoletes: qgpgme1.22-qt6-devel < %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: rx-q%{base_name}-common-devel < %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: q%{base_name}1.22-common-devel < %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n rx-q%{base_name}-qt6-devel +%{summary}. +%endif + +%if %{with python} +%package -n python%{python3_pkgversion}-gpg +Summary: %{name} bindings for Python 3 +BuildRequires: python%{python3_pkgversion}-devel +# Needed since Python 3.12+ drops distutils +BuildRequires: python%{python3_pkgversion}-setuptools +Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n python%{python3_pkgversion}-gpg +%{summary}. +%endif + +%prep +%autosetup -p1 -n gpgme-%{version} + +## HACK ALERT +# The config script already suppresses the -L if it's /usr/lib, so cheat and +# set it to a value which we know will be suppressed. +sed -i -e 's|^libdir=@libdir@$|libdir=@exec_prefix@/lib|g' src/gpgme-config.in + +# The build machinery does not support Python 3.9+ yet +# https://github.com/gpg/gpgme/pull/4 +sed -i 's/3.8/%{python3_pkgversion}/g' configure + +%build +export CC=clang +export CXX=clang++ +# People neeed to learn that you can't run autogen.sh anymore +#./autogen.sh + +# Since 1.16.0, we need to explicitly pass -D_LARGEFILE_SOURCE and +# -D_FILE_OFFSET_BITS=64 for the QT binding to build successfully on 32-bit +# platforms. +export CFLAGS='-I%{_sysincludedir} -I%{_sysincludedir}/Qt6 %{optflags} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Qunused-arguments' +export CXXFLAGS='-I%{_sysincludedir} -I%{_sysincludedir}/Qt6 %{optflags} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Qunused-arguments' +# Explicit new lines in C(XX)FLAGS can break naive build scripts +export CFLAGS="$(echo ${CFLAGS} | tr '\n\\' ' ')" +export CXXFLAGS="$(echo ${CXXFLAGS} | tr '\n\\' ' ')" +export SETUPTOOLS_USE_DISTUTILS=local + +export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} +# Link statically +export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} -lgpg-error $LDFLAGS" +export PATH="%{_bindir}:$PATH" + +# Force to use rx-gnupg2 +sed 's@GPG_DEFAULT=no@GPG_DEFAULT="/opt/rx/bin/gpg"@g' -i configure +sed 's@GPGSM_DEFAULT=no@GPGSM_DEFAULT="/opt/rx/bin/gpgsm"@g' -i configure +sed 's@GPGCONF_DEFAULT=no@GPGCONF_DEFAULT="/opt/rx/bin/gpgconf"@g' -i configure +sed 's@G13_DEFAULT=no@G13_DEFAULT="/opt/rx/bin/g13"@g' -i configure + +# Also build either qt5 or qt6 +mkdir build +pushd build +%configure --disable-static --disable-silent-rules --enable-languages=cpp,%{?with_python:python,}%{?with_qt5:qt,}%{!?with_qt5:%{?with_qt6:qt6,}} +%make_build +popd + +# Build qt6 in extra step if qt5 has been build +%if %{with qt5} && %{with qt6} +mkdir build-qt6 +pushd build-qt6 +%configure --disable-static --disable-silent-rules --enable-languages=cpp,qt6%{?with_python:,python} +%make_build +popd +%endif + +%install +# When using distutils from setuptools 60+, ./setup.py install use +# the .egg format. This forces setuptools to use .egg-info format. +# SETUP_PY_EXTRA_OPTS is introduced by the Patch1004 above. +export SETUPTOOLS_USE_DISTUTILS=local +export SETUP_PY_EXTRA_OPTS="--single-version-externally-managed --root=/" +# Aliso install either qt5 or qt6 +pushd build +%make_install +popd +# Install qt6 in extra step if qt5 has been installed +%if %{with qt5} && %{with qt6} +pushd build-qt6 +%make_install +popd +%endif + +# unpackaged files +rm -fv %{buildroot}%{_infodir}/dir +rm -fv %{buildroot}%{_libdir}/lib*.la + +# Hack to resolve multiarch conflict (#341351) +%ifarch %{multilib_arches} +mv %{buildroot}%{_bindir}/gpgme-config{,.%{_target_cpu}} +cat > gpgme-config-multilib.sh <<__END__ +#!/bin/sh +exec %{_bindir}/gpgme-config.\$(arch) \$@ +__END__ +install -D -p gpgme-config-multilib.sh %{buildroot}%{_bindir}/gpgme-config +mv %{buildroot}%{_includedir}/gpgme.h \ + %{buildroot}%{_includedir}/gpgme-%{__isa_bits}.h +install -m644 -p -D %{SOURCE2} %{buildroot}%{_includedir}/gpgme.h +%endif +chrpath -d %{buildroot}%{_bindir}/%{base_name}-tool +chrpath -d %{buildroot}%{_bindir}/%{base_name}-json +chrpath -d %{buildroot}%{_libdir}/lib%{base_name}pp.so* +# qt5 +%if %{with qt5} +chrpath -d %{buildroot}%{_libdir}/libq%{base_name}.so* +%endif +# qt6 +%if %{with qt6} +chrpath -d %{buildroot}%{_libdir}/libq%{base_name}qt6.so* +%endif + +# autofoo installs useless stuff for uninstall +rm -vf %{buildroot}%{python2_sitelib}/gpg/install_files.txt +rm -vf %{buildroot}%{python3_sitelib}/gpg/install_files.txt + +# Remove duplicate stuff +rm -rf %{buildroot}%{_bindir}/%{base_name}-json +rm -rf %{buildroot}%{_datadir}/man/man1/gpgme-json* +#rm -fv %{buildroot}%{_libdir}/lib%{base_name}.so.11 +#rm -fv %{buildroot}%{_libdir}/lib%{base_name}pp.so.6 + +mkdir -p %{buildroot}%{_syslibdir}/cmake +ln -sf %{_libdir}/cmake/Gpgmepp %{buildroot}%{_syslibdir}/cmake/Gpgmepp +%if %{with qt5} +ln -sf %{_libdir}/cmake/QGpgme %{buildroot}%{_syslibdir}/cmake/QGpgme +%endif +%if %{with qt6} +ln -sf %{_libdir}/cmake/QGpgmeQt6 %{buildroot}%{_syslibdir}/cmake/QGpgmeQt6 +%endif + +%if %{with check} +%check +pushd build +make check +popd +%endif + +%files +%license COPYING* LICENSES +%doc AUTHORS NEWS README* +%{_libdir}/lib%{base_name}.so.11* + +%files devel +%{_bindir}/%{base_name}-config +%{_bindir}/%{base_name}-tool +%ifarch %{multilib_arches} +%{_bindir}/%{base_name}-config.%{_target_cpu} +%{_includedir}/%{base_name}-%{__isa_bits}.h +%endif +%{_includedir}/%{base_name}.h +%{_libdir}/lib%{base_name}.so +%{_datadir}/aclocal/%{base_name}.m4 +%{_infodir}/%{base_name}.info* +%{_libdir}/pkgconfig/%{base_name}*.pc + +%files -n %{name}pp +%doc lang/cpp/README +%{_libdir}/lib%{base_name}pp.so.6* + +%files -n %{name}pp-devel +%{_includedir}/%{base_name}++/ +%{_libdir}/lib%{base_name}pp.so +%{_libdir}/cmake/Gpgmepp/ +%{_syslibdir}/cmake/Gpgmepp + +%if %{with qt5} +%files -n rx-q%{base_name}-qt5 +%doc lang/qt/README +%{_libdir}/libq%{base_name}.so.15* +%endif + +%if %{with qt6} +%files -n rx-q%{base_name}-qt6 +%{_libdir}/libq%{base_name}qt6.so.15* +%endif + +%if %{with qt5} +%files -n rx-q%{base_name}-qt5-devel +%{_libdir}/libq%{base_name}.so +%{_libdir}/cmake/QGpgme/ +%{_syslibdir}/cmake/QGpgme +%{_includedir}/q%{base_name}-qt5/ +%endif + +%if %{with qt6} +%files -n rx-q%{base_name}-qt6-devel +%{_libdir}/libq%{base_name}qt6.so +%{_libdir}/cmake/QGpgmeQt6/ +%{_syslibdir}/cmake/QGpgmeQt6 +%{_includedir}/q%{base_name}-qt6/ +%endif + +%if %{with python} +%files -n python%{python3_pkgversion}-gpg +%doc lang/python/README +%{python3_sitearch}/gpg-*.egg-info/ +%{python3_sitearch}/gpg/ +%endif + +%changelog +* Tue Feb 18 2025 Raven - 1.24.2-1 +- update to 1.24.2 + +* Tue Feb 18 2025 Raven - 1.23.2-4 +- rebuild (qt6) + +* Tue Oct 1 2024 Raven - 1.23.2-3 +- downgrade gpg-error version +- obsolete *1.22-devel packages from epel + +* Mon Jun 3 2024 Raven - 1.23.2-2 +- rebuild (qt6) + +* Fri Mar 15 2024 Raven - 1.23.2-1 +- update to 1.23.2 + +* Fri Mar 15 2024 Raven - 1.22.0-1 +- converted from el9 epel package diff --git a/base/rx/rx-harfbuzz/harfbuzz.spec b/base/rx/rx-harfbuzz/harfbuzz.spec index c21a5a9..4818c32 100644 --- a/base/rx/rx-harfbuzz/harfbuzz.spec +++ b/base/rx/rx-harfbuzz/harfbuzz.spec @@ -2,7 +2,7 @@ Name: rx-harfbuzz Version: 7.0.0 -Release: 2%{?dist}%{?_trivial}%{?_buildid} +Release: 4%{?dist}%{?_trivial}%{?_buildid} Summary: Text shaping library License: MIT @@ -101,6 +101,9 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.la %{_libdir}/libharfbuzz-icu.so.* %changelog +* Tue Oct 29 2024 Raven - 7.0.0-4 +- rebuilt with new rx-freetype + * Fri Feb 17 2023 Lucas Meneghel Rodrigues 7.0.0-2.amzn2023.0.1 - Update to upstream 7.0.0 diff --git a/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch b/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch new file mode 100644 index 0000000..2ad09bc --- /dev/null +++ b/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch @@ -0,0 +1,122 @@ +diff -up libgpg-error-1.29/configure.ac.multilib libgpg-error-1.29/configure.ac +--- libgpg-error-1.29/configure.ac.multilib 2018-04-11 14:41:10.479019981 +0200 ++++ libgpg-error-1.29/configure.ac 2018-04-11 14:43:31.288394113 +0200 +@@ -215,13 +215,13 @@ GNUPG_FUNC_MKDIR_TAKES_ONE_ARG + + + # Find a 64 bit integer type to be used instead of off_t. We prefer +-# the standard integer types over int64_t and finally try long long. +-if test "$ac_cv_sizeof_int" = "8"; then ++# int64_t and finally try long long. ++if test "$ac_cv_header_stdint_h" = yes; then ++ replacement_for_off_t="int64_t" ++elif test "$ac_cv_sizeof_int" = "8"; then + replacement_for_off_t="int" + elif test "$ac_cv_sizeof_long" = "8"; then + replacement_for_off_t="long" +-elif test "$ac_cv_header_stdint_h" = yes; then +- replacement_for_off_t="int64_t" + elif test "$ac_cv_sizeof_long_long" = "8"; then + replacement_for_off_t="long long" + else +diff -up libgpg-error-1.29/configure.multilib libgpg-error-1.29/configure +--- libgpg-error-1.29/configure.multilib 2018-04-11 09:34:30.000000000 +0200 ++++ libgpg-error-1.29/configure 2018-04-11 14:41:10.481020028 +0200 +@@ -11301,7 +11301,7 @@ shlibpath_var= + shlibpath_overrides_runpath=unknown + version_type=none + dynamic_linker="$host_os ld.so" +-sys_lib_dlsearch_path_spec="/lib /usr/lib" ++sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/lib64 /usr/lib64" + need_lib_prefix=unknown + hardcode_into_libs=no + +@@ -11775,7 +11775,7 @@ fi + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` +- sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" ++ sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/lib64 /usr/lib64 $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on +@@ -15879,12 +15879,12 @@ fi + + # Find a 64 bit integer type to be used instead of off_t. We prefer + # the standard integer types over int64_t and finally try long long. +-if test "$ac_cv_sizeof_int" = "8"; then ++if test "$ac_cv_header_stdint_h" = yes; then ++ replacement_for_off_t="int64_t" ++elif test "$ac_cv_sizeof_int" = "8"; then + replacement_for_off_t="int" + elif test "$ac_cv_sizeof_long" = "8"; then + replacement_for_off_t="long" +-elif test "$ac_cv_header_stdint_h" = yes; then +- replacement_for_off_t="int64_t" + elif test "$ac_cv_sizeof_long_long" = "8"; then + replacement_for_off_t="long long" + else +diff -up libgpg-error-1.29/src/gen-posix-lock-obj.c.multilib libgpg-error-1.29/src/gen-posix-lock-obj.c +--- libgpg-error-1.29/src/gen-posix-lock-obj.c.multilib 2016-11-16 13:22:03.000000000 +0100 ++++ libgpg-error-1.29/src/gen-posix-lock-obj.c 2018-04-11 14:41:10.481020028 +0200 +@@ -72,6 +72,7 @@ main (void) + #ifdef USE_POSIX_THREADS + unsigned char *p; + int i; ++ int initidx = 0; + #endif + struct { + long vers; +@@ -111,11 +112,12 @@ main (void) + + /* To force a probably suitable alignment of the structure we use a + union and include a long and a pointer to a long. */ +- printf ("typedef struct\n" ++ printf ("#include \n" ++ "typedef struct\n" + "{\n" + " long _vers;\n" + " union {\n" +- " volatile char _priv[%d];\n" ++ " volatile char _priv[sizeof(pthread_mutex_t)];\n" + "%s" + " long _x_align;\n" + " long *_xp_align;\n" +@@ -123,7 +125,6 @@ main (void) + "} gpgrt_lock_t;\n" + "\n" + "#define GPGRT_LOCK_INITIALIZER {%d,{{", +- SIZEOF_PTHREAD_MUTEX_T, + # if USE_16BYTE_ALIGNMENT + " int _x16_align __attribute__ ((aligned (16)));\n", + # elif USE_DOUBLE_FOR_ALIGNMENT +@@ -137,10 +138,16 @@ main (void) + p = (unsigned char *)&mtx; + for (i=0; i < sizeof mtx; i++) + { ++ if (p[i] != 0) ++ initidx = i; ++ } ++ ++ for (i=0; i <= initidx; i++) ++ { + if (i && !(i % 8)) + printf (" \\\n%*s", 36, ""); + printf ("%u", p[i]); +- if (i < sizeof mtx - 1) ++ if (i < initidx) + putchar (','); + } + fputs ("}}}\n", stdout); +diff -up libgpg-error-1.29/src/gpg-error.h.in.multilib libgpg-error-1.29/src/gpg-error.h.in +--- libgpg-error-1.29/src/gpg-error.h.in.multilib 2018-04-11 14:41:10.481020028 +0200 ++++ libgpg-error-1.29/src/gpg-error.h.in 2018-04-11 14:45:28.184203566 +0200 +@@ -17,7 +17,7 @@ + * License along with this program; if not, see . + * SPDX-License-Identifier: LGPL-2.1-or-later + * +- * @configure_input@ ++ * Do not edit. Generated from gpg-error.h.in. + */ + + /* The GnuPG project consists of many components. Error codes are diff --git a/base/rx/rx-libgpg-error/libgpg-error.spec b/base/rx/rx-libgpg-error/libgpg-error.spec new file mode 100644 index 0000000..bb60db9 --- /dev/null +++ b/base/rx/rx-libgpg-error/libgpg-error.spec @@ -0,0 +1,361 @@ +%global realname libgpg-error +%global _syslibdir %{_libdir} +%global _prefix /opt/rx +%global _bindir %{_prefix}/bin +%global _libdir %{_prefix}/%{_lib} +%global _includedir %{_prefix}/include +%global _datadir %{_prefix}/share +%global _docdir %{_datadir}/doc +%global _mandir %{_datadir}/man +%global _infodir %{_datadir}/info + +Name: rx-libgpg-error +Version: 1.51 +Release: 1%{?dist} +Summary: Library for error values used by GnuPG components +URL: https://www.gnupg.org/related_software/libgpg-error/ +License: LGPLv2+ + +Source0: https://www.gnupg.org/ftp/gcrypt/libgpg-error/%{realname}-%{version}.tar.bz2 +Source1: https://www.gnupg.org/ftp/gcrypt/libgpg-error/%{realname}-%{version}.tar.bz2.sig +Patch1: libgpg-error-1.29-multilib.patch +Patch2: set-proper-version-suffix.patch + +BuildRequires: gcc +BuildRequires: gawk, gettext, autoconf, automake, gettext-devel, libtool +BuildRequires: texinfo +BuildRequires: gettext-autopoint +BuildRequires: make + +%description +This is a library that defines common error values for all GnuPG +components. Among these are GPG, GPGSM, GPGME, GPG-Agent, libgcrypt, +pinentry, SmartCard Daemon and possibly more in the future. + +%package devel +Summary: Development files for the %{name} package +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: pkgconfig + +%description devel +This is a library that defines common error values for all GnuPG +components. Among these are GPG, GPGSM, GPGME, GPG-Agent, libgcrypt, +pinentry, SmartCard Daemon and possibly more in the future. This package +contains files necessary to develop applications using libgpg-error. + +%prep +%autosetup -p1 -n %{realname}-%{version} + +autoreconf -f + +# The config script already suppresses the -L if it's /usr/lib, so cheat and +# set it to a value which we know will be suppressed. +sed -i -e 's|^libdir=@libdir@$|libdir=@exec_prefix@/lib64|g;s|@GPG_ERROR_CONFIG_HOST@|none|g' src/gpg-error-config.in +sed -i -e '/--variable=host/d' src/gpg-error-config-test.sh.in + +# Modify configure to drop rpath for /usr/lib64 +sed -i -e 's|sys_lib_dlsearch_path_spec="/lib /usr/lib|sys_lib_dlsearch_path_spec="/lib /usr/lib %{_libdir} %{_syslibdir}|g' configure + +%build +export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} $LDFLAGS" + +%configure \ + --disable-static\ + --disable-rpath \ + --disable-languages \ + --enable-install-gpg-error-config +%make_build + +%install +%make_install +rm -f $RPM_BUILD_ROOT%{_libdir}/*.la +rm -f $RPM_BUILD_ROOT%{_infodir}/dir + +%find_lang %{realname} + +%check +make check + +%ldconfig_scriptlets + +%files -f %{realname}.lang +%license COPYING COPYING.LIB +%doc AUTHORS README NEWS +%{_bindir}/gpg-error +%{_libdir}/libgpg-error.so.0* +%{_datadir}/libgpg-error + +%files devel +%{_bindir}/gpg-error-config +%{_bindir}/gpgrt-config +%{_bindir}/yat2m +%{_libdir}/libgpg-error.so +%{_libdir}/pkgconfig/gpg-error.pc +%{_includedir}/gpg-error.h +%{_includedir}/gpgrt.h +%{_datadir}/aclocal/gpg-error.m4 +%{_datadir}/aclocal/gpgrt.m4 +%{_infodir}/gpgrt.info* +%{_mandir}/man1/gpgrt-config.* +%{_mandir}/man1/gpg-error-config.* + +%changelog +* Tue Feb 18 2025 Raven - 1.51-1 +- New upstream release + +* Wed Aug 24 2022 Raven - 1.42-5 +- change installation root to /opt/rx + +* Mon Dec 06 2021 Jakub Jelen - 1.42-5 +- Avoid using bad function inet_addr + +* Mon Aug 09 2021 Mohan Boddu - 1.42-4 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Fri Apr 16 2021 Mohan Boddu - 1.42-3 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Mon Apr 12 2021 Jakub Jelen - 1.42-2 +- Address coverity reported issues + +* Mon Mar 22 2021 Jakub Jelen - 1.42-1 +- New upstream release (#1941582) + +* Tue Jan 26 2021 Fedora Release Engineering - 1.41-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Jan 04 2021 Jakub Jelen - 1.41-1 +- New upstream release (#1909749) + +* Tue Dec 01 2020 Jakub Jelen - 1.39-1 +- New upstream release (#1800640) + +* Tue Jul 28 2020 Fedora Release Engineering - 1.37-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Fri Feb 28 2020 Tomáš Mráz 1.37-1 +- new upstream release 1.37 + +* Wed Jan 29 2020 Fedora Release Engineering - 1.36-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Aug 14 2019 Tomáš Mráz 1.36-2 +- fix FTBFS in rawhide due to new gawk + +* Sat Aug 3 2019 Peter Robinson 1.36-1 +- new upstream release 1.36 + +* Thu Jul 25 2019 Fedora Release Engineering - 1.33-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Fri Feb 01 2019 Fedora Release Engineering - 1.33-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Tue Jan 08 2019 Tomáš Mráz 1.33-1 +- new upstream release 1.33 +- dropped obsolete install-info scriptlets + +* Fri Jul 13 2018 Fedora Release Engineering - 1.31-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Mon Jun 11 2018 Tomáš Mráz 1.31-1 +- new upstream release 1.31 + +* Wed Apr 11 2018 Tomáš Mráz 1.29-1 +- new upstream release 1.29 + +* Wed Feb 28 2018 Richard W.M. Jones - 1.27-6 +- Backport patch which adds riscv64 support. + +* Wed Feb 07 2018 Fedora Release Engineering - 1.27-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Feb 03 2018 Igor Gnatenko - 1.27-4 +- Switch to %%ldconfig_scriptlets + +* Thu Aug 03 2017 Fedora Release Engineering - 1.27-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1.27-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Mar 15 2017 Tomáš Mráz 1.27-1 +- new upstream release 1.27 + +* Fri Feb 10 2017 Fedora Release Engineering - 1.25-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Nov 15 2016 Tomáš Mráz 1.25-1 +- new upstream release 1.25 + +* Thu Jul 14 2016 Tomáš Mráz 1.24-1 +- new upstream release + +* Fri Mar 18 2016 Rex Dieter - 1.21-3 +- drop explicit /sbin/ldconfig scriptlet deps (#1319144) + +* Thu Feb 04 2016 Fedora Release Engineering - 1.21-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Dec 22 2015 Tomáš Mráz 1.21-1 +- new upstream release + +* Tue Sep 1 2015 Tomáš Mráz 1.20-1 +- new upstream release + +* Wed Jun 17 2015 Fedora Release Engineering - 1.19-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Mon Apr 13 2015 Tomáš Mráz 1.19-1 +- new upstream release + +* Sat Feb 21 2015 Till Maas - 1.17-3 +- Rebuilt for Fedora 23 Change + https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code + +* Fri Jan 30 2015 Tomáš Mráz 1.17-2 +- do not conflict on header file between architectures (#1180857) + +* Thu Jan 29 2015 Tomáš Mráz 1.17-1 +- new upstream release + +* Fri Sep 19 2014 Tomáš Mráz 1.16-1 +- new upstream release +- move from /lib to /usr/lib + +* Sun Aug 17 2014 Fedora Release Engineering - 1.13-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Fri Jul 18 2014 Tom Callaway 1.13-2 +- fix license handling + +* Wed Jun 25 2014 Tomáš Mráz 1.13-1 +- new upstream release + +* Sat Jun 07 2014 Fedora Release Engineering - 1.12-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Fri Aug 23 2013 Tomáš Mráz 1.12-1 +- new upstream release + +* Sat Aug 03 2013 Fedora Release Engineering - 1.11-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Fri Apr 5 2013 Tomáš Mráz 1.11-1 +- new upstream release + +* Thu Feb 14 2013 Fedora Release Engineering - 1.10-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Thu Jul 19 2012 Fedora Release Engineering - 1.10-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Fri Jan 13 2012 Fedora Release Engineering - 1.10-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Fri Jul 15 2011 Tomáš Mráz 1.10-1 +- new upstream release + +* Tue Feb 08 2011 Fedora Release Engineering - 1.9-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Fri Jul 23 2010 Rex Dieter 1.9-1 +- libgpg-error-1.9 + +* Thu Feb 25 2010 Nalin Dahyabhai - 1.7-3 +- turn off common lisp bindings the right way +- drop finger output +- recode the changelog into UTF-8 if it isn't UTF-8 (rpmlint) + +* Mon Jan 11 2010 Nalin Dahyabhai - 1.7-2 +- fix use of macro in changelog (rpmlint) +- build with --disable-rpath (rpmlint) +- build with %%{?_smp_mflags} + +* Thu Oct 15 2009 Nalin Dahyabhai - 1.7-1 +- long-overdue update to 1.7 +- add a disttag + +* Fri Jul 24 2009 Fedora Release Engineering - 1.6-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed Feb 25 2009 Fedora Release Engineering - 1.6-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Tue Feb 19 2008 Fedora Release Engineering - 1.6-2 +- Autorebuild for GCC 4.3 + +* Fri Dec 7 2007 Nalin Dahyabhai +- remove the generic install docs (#226021) + +* Fri Dec 7 2007 Nalin Dahyabhai - 1.6-1 +- update to 1.6 +- add suggested summary, buildrequires, and modify install call as suggested + by package review (#226021) + +* Mon Oct 15 2007 Nalin Dahyabhai - 1.5-6 +- use ldconfig to make the soname symlink so that it gets packaged (#331241) + +* Wed Aug 22 2007 Nalin Dahyabhai - 1.5-5 +- add missing gawk buildrequirement + +* Thu Aug 16 2007 Nalin Dahyabhai - 1.5-4 +- clarify license + +* Mon Jul 30 2007 Nalin Dahyabhai - 1.5-3 +- disable static libraries (part of #249815) + +* Fri Jul 27 2007 Nalin Dahyabhai - 1.5-2 +- move libgpg-error shared library to /%%{_lib} (#249816) + +* Thu Jul 19 2007 Nalin Dahyabhai - 1.5-1 +- update to 1.5 + +* Sun Oct 01 2006 Jesse Keating - 1.4-2 +- rebuilt for unwind info generation, broken in gcc-4.1.1-21 + +* Mon Sep 18 2006 Bill Nottngham - 1.4-1 +- update to 1.4 +- don't ship lisp bindings + +* Wed Jul 12 2006 Jesse Keating - 1.3-3.1 +- rebuild + +* Mon Jun 5 2006 Nalin Dahyabhai 1.3-3 +- give gpg-error-config libdir=@exec_prefix@/lib instead of @libdir@, so that + it agrees on 32- and 64-bit arches (it suppresses the -L argument if @libdir@ + is /usr/lib, so this should be cleaner than adding a non-standard .pc file + which upstream developers might inadvertently think they can depend to be on + every system which provides this library) + +* Mon May 15 2006 Karsten Hopp 1.3-2 +- switch to pkgconfig so that gpg-error-config can be the same on + 32bit and 64bit archs + +* Tue May 2 2006 Nalin Dahyabhai - 1.3-1 +- update to version 1.3 + +* Fri Feb 10 2006 Jesse Keating - 1.1-1.2.1 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 1.1-1.2 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Wed Nov 30 2005 Karsten Hopp 1.1-1 +- update + +* Wed Mar 2 2005 Bill Nottingham - 1.0-2 +- we can rebuild it. we have the technology. + +* Tue Aug 31 2004 Bill Nottingham - 1.0-1 +- update to 1.0 + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Fri Apr 16 2004 Bill Nottingham - 0.7-1 +- adapt upstream specfile diff --git a/base/rx/rx-libgpg-error/set-proper-version-suffix.patch b/base/rx/rx-libgpg-error/set-proper-version-suffix.patch new file mode 100644 index 0000000..fcbb55d --- /dev/null +++ b/base/rx/rx-libgpg-error/set-proper-version-suffix.patch @@ -0,0 +1,11 @@ +--- a/autogen.sh 2024-07-04 21:14:53.000000000 +0600 ++++ b/autogen.sh 2025-02-18 11:23:30.572654260 +0600 +@@ -271,7 +271,7 @@ + else + ingit=no + beta=yes +- tmp="-unknown" ++ tmp="-rx" + cid="0000000" + rev="0000000" + rvd="0" diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch new file mode 100644 index 0000000..75e0cea --- /dev/null +++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch @@ -0,0 +1,1248 @@ +From db1afe88c361ceea22c6ac8abdb9b0ff41a39aa2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Nikola=20Forr=C3=B3?= +Date: Mon, 15 Apr 2019 16:56:45 +0200 +Subject: [PATCH] x86 SIMD: Add endbr32/endbr64 instructions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Allow for indirect branch tracking with Intel CET (Control-Flow +Enforcement Technology) [1], by making all exported routines a possible +target for an indirect jump. + +Signed-off-by: Nikola Forró + +[1] https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf +--- + simd/i386/jccolext-avx2.asm | 1 + + simd/i386/jccolext-mmx.asm | 1 + + simd/i386/jccolext-sse2.asm | 1 + + simd/i386/jcgryext-avx2.asm | 1 + + simd/i386/jcgryext-mmx.asm | 1 + + simd/i386/jcgryext-sse2.asm | 1 + + simd/i386/jchuff-sse2.asm | 1 + + simd/i386/jcphuff-sse2.asm | 2 ++ + simd/i386/jcsample-avx2.asm | 2 ++ + simd/i386/jcsample-mmx.asm | 2 ++ + simd/i386/jcsample-sse2.asm | 2 ++ + simd/i386/jdcolext-avx2.asm | 1 + + simd/i386/jdcolext-mmx.asm | 1 + + simd/i386/jdcolext-sse2.asm | 1 + + simd/i386/jdmrgext-avx2.asm | 2 ++ + simd/i386/jdmrgext-mmx.asm | 2 ++ + simd/i386/jdmrgext-sse2.asm | 2 ++ + simd/i386/jdsample-avx2.asm | 4 ++++ + simd/i386/jdsample-mmx.asm | 4 ++++ + simd/i386/jdsample-sse2.asm | 4 ++++ + simd/i386/jfdctflt-3dn.asm | 1 + + simd/i386/jfdctflt-sse.asm | 1 + + simd/i386/jfdctfst-mmx.asm | 1 + + simd/i386/jfdctfst-sse2.asm | 1 + + simd/i386/jfdctint-avx2.asm | 1 + + simd/i386/jfdctint-mmx.asm | 1 + + simd/i386/jfdctint-sse2.asm | 1 + + simd/i386/jidctflt-3dn.asm | 1 + + simd/i386/jidctflt-sse.asm | 1 + + simd/i386/jidctflt-sse2.asm | 1 + + simd/i386/jidctfst-mmx.asm | 1 + + simd/i386/jidctfst-sse2.asm | 1 + + simd/i386/jidctint-avx2.asm | 1 + + simd/i386/jidctint-mmx.asm | 1 + + simd/i386/jidctint-sse2.asm | 1 + + simd/i386/jidctred-mmx.asm | 2 ++ + simd/i386/jidctred-sse2.asm | 2 ++ + simd/i386/jquant-3dn.asm | 2 ++ + simd/i386/jquant-mmx.asm | 2 ++ + simd/i386/jquant-sse.asm | 2 ++ + simd/i386/jquantf-sse2.asm | 2 ++ + simd/i386/jquanti-avx2.asm | 2 ++ + simd/i386/jquanti-sse2.asm | 2 ++ + simd/nasm/jsimdext.inc | 8 ++++++++ + simd/x86_64/jccolext-avx2.asm | 1 + + simd/x86_64/jccolext-sse2.asm | 1 + + simd/x86_64/jcgryext-avx2.asm | 1 + + simd/x86_64/jcgryext-sse2.asm | 1 + + simd/x86_64/jchuff-sse2.asm | 1 + + simd/x86_64/jcphuff-sse2.asm | 2 ++ + simd/x86_64/jcsample-avx2.asm | 2 ++ + simd/x86_64/jcsample-sse2.asm | 2 ++ + simd/x86_64/jdcolext-avx2.asm | 1 + + simd/x86_64/jdcolext-sse2.asm | 1 + + simd/x86_64/jdmrgext-avx2.asm | 2 ++ + simd/x86_64/jdmrgext-sse2.asm | 2 ++ + simd/x86_64/jdsample-avx2.asm | 4 ++++ + simd/x86_64/jdsample-sse2.asm | 4 ++++ + simd/x86_64/jfdctflt-sse.asm | 1 + + simd/x86_64/jfdctfst-sse2.asm | 1 + + simd/x86_64/jfdctint-avx2.asm | 1 + + simd/x86_64/jfdctint-sse2.asm | 1 + + simd/x86_64/jidctflt-sse2.asm | 1 + + simd/x86_64/jidctfst-sse2.asm | 1 + + simd/x86_64/jidctint-avx2.asm | 1 + + simd/x86_64/jidctint-sse2.asm | 1 + + simd/x86_64/jidctred-sse2.asm | 2 ++ + simd/x86_64/jquantf-sse2.asm | 2 ++ + simd/x86_64/jquanti-avx2.asm | 2 ++ + simd/x86_64/jquanti-sse2.asm | 2 ++ + 70 files changed, 116 insertions(+) + +diff --git a/simd/i386/jccolext-avx2.asm b/simd/i386/jccolext-avx2.asm +index c46d684..7dc6e08 100644 +--- a/simd/i386/jccolext-avx2.asm ++++ b/simd/i386/jccolext-avx2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_avx2) + + EXTN(jsimd_rgb_ycc_convert_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jccolext-mmx.asm b/simd/i386/jccolext-mmx.asm +index 6357a42..8048abb 100644 +--- a/simd/i386/jccolext-mmx.asm ++++ b/simd/i386/jccolext-mmx.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_mmx) + + EXTN(jsimd_rgb_ycc_convert_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jccolext-sse2.asm b/simd/i386/jccolext-sse2.asm +index c6c8085..5307ddc 100644 +--- a/simd/i386/jccolext-sse2.asm ++++ b/simd/i386/jccolext-sse2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_sse2) + + EXTN(jsimd_rgb_ycc_convert_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jcgryext-avx2.asm b/simd/i386/jcgryext-avx2.asm +index 3fa7973..27a0e11 100644 +--- a/simd/i386/jcgryext-avx2.asm ++++ b/simd/i386/jcgryext-avx2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_gray_convert_avx2) + + EXTN(jsimd_rgb_gray_convert_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jcgryext-mmx.asm b/simd/i386/jcgryext-mmx.asm +index 8af42e5..dda0e05 100644 +--- a/simd/i386/jcgryext-mmx.asm ++++ b/simd/i386/jcgryext-mmx.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_gray_convert_mmx) + + EXTN(jsimd_rgb_gray_convert_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jcgryext-sse2.asm b/simd/i386/jcgryext-sse2.asm +index c9d6ff1..f8835bb 100644 +--- a/simd/i386/jcgryext-sse2.asm ++++ b/simd/i386/jcgryext-sse2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_gray_convert_sse2) + + EXTN(jsimd_rgb_gray_convert_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jchuff-sse2.asm b/simd/i386/jchuff-sse2.asm +index 76cc85f..0217480 100644 +--- a/simd/i386/jchuff-sse2.asm ++++ b/simd/i386/jchuff-sse2.asm +@@ -350,6 +350,7 @@ times 1 << 14 db 15 + GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2) + + EXTN(jsimd_huff_encode_one_block_sse2): ++ _endbr32 + + %assign stack_offset 0 + %define arg_state 4 + stack_offset +diff --git a/simd/i386/jcphuff-sse2.asm b/simd/i386/jcphuff-sse2.asm +index c26b48a..7fb01e5 100644 +--- a/simd/i386/jcphuff-sse2.asm ++++ b/simd/i386/jcphuff-sse2.asm +@@ -281,6 +281,7 @@ + GLOBAL_FUNCTION(jsimd_encode_mcu_AC_first_prepare_sse2) + + EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -460,6 +461,7 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): + GLOBAL_FUNCTION(jsimd_encode_mcu_AC_refine_prepare_sse2) + + EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jcsample-avx2.asm b/simd/i386/jcsample-avx2.asm +index 0a20802..46eba8c 100644 +--- a/simd/i386/jcsample-avx2.asm ++++ b/simd/i386/jcsample-avx2.asm +@@ -43,6 +43,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_downsample_avx2) + + EXTN(jsimd_h2v1_downsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -216,6 +217,7 @@ EXTN(jsimd_h2v1_downsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_downsample_avx2) + + EXTN(jsimd_h2v2_downsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jcsample-mmx.asm b/simd/i386/jcsample-mmx.asm +index 2c223ee..b2b8ded 100644 +--- a/simd/i386/jcsample-mmx.asm ++++ b/simd/i386/jcsample-mmx.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_downsample_mmx) + + EXTN(jsimd_h2v1_downsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -185,6 +186,7 @@ EXTN(jsimd_h2v1_downsample_mmx): + GLOBAL_FUNCTION(jsimd_h2v2_downsample_mmx) + + EXTN(jsimd_h2v2_downsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jcsample-sse2.asm b/simd/i386/jcsample-sse2.asm +index 4fea60d..4c22b40 100644 +--- a/simd/i386/jcsample-sse2.asm ++++ b/simd/i386/jcsample-sse2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_downsample_sse2) + + EXTN(jsimd_h2v1_downsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -198,6 +199,7 @@ EXTN(jsimd_h2v1_downsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_downsample_sse2) + + EXTN(jsimd_h2v2_downsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jdcolext-avx2.asm b/simd/i386/jdcolext-avx2.asm +index 015be04..b076765 100644 +--- a/simd/i386/jdcolext-avx2.asm ++++ b/simd/i386/jdcolext-avx2.asm +@@ -43,6 +43,7 @@ + GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_avx2) + + EXTN(jsimd_ycc_rgb_convert_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jdcolext-mmx.asm b/simd/i386/jdcolext-mmx.asm +index 5813cfc..150f5b6 100644 +--- a/simd/i386/jdcolext-mmx.asm ++++ b/simd/i386/jdcolext-mmx.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_mmx) + + EXTN(jsimd_ycc_rgb_convert_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jdcolext-sse2.asm b/simd/i386/jdcolext-sse2.asm +index d5572b3..cd3ac70 100644 +--- a/simd/i386/jdcolext-sse2.asm ++++ b/simd/i386/jdcolext-sse2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_sse2) + + EXTN(jsimd_ycc_rgb_convert_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jdmrgext-avx2.asm b/simd/i386/jdmrgext-avx2.asm +index e35f728..0db0aa4 100644 +--- a/simd/i386/jdmrgext-avx2.asm ++++ b/simd/i386/jdmrgext-avx2.asm +@@ -43,6 +43,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_avx2) + + EXTN(jsimd_h2v1_merged_upsample_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -523,6 +524,7 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_avx2) + + EXTN(jsimd_h2v2_merged_upsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jdmrgext-mmx.asm b/simd/i386/jdmrgext-mmx.asm +index eb3e36b..6427a1a 100644 +--- a/simd/i386/jdmrgext-mmx.asm ++++ b/simd/i386/jdmrgext-mmx.asm +@@ -40,6 +40,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_mmx) + + EXTN(jsimd_h2v1_merged_upsample_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -408,6 +409,7 @@ EXTN(jsimd_h2v1_merged_upsample_mmx): + GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_mmx) + + EXTN(jsimd_h2v2_merged_upsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jdmrgext-sse2.asm b/simd/i386/jdmrgext-sse2.asm +index c113dc4..6897fa1 100644 +--- a/simd/i386/jdmrgext-sse2.asm ++++ b/simd/i386/jdmrgext-sse2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_sse2) + + EXTN(jsimd_h2v1_merged_upsample_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -465,6 +466,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_sse2) + + EXTN(jsimd_h2v2_merged_upsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jdsample-avx2.asm b/simd/i386/jdsample-avx2.asm +index a800c35..7d52708 100644 +--- a/simd/i386/jdsample-avx2.asm ++++ b/simd/i386/jdsample-avx2.asm +@@ -60,6 +60,7 @@ PW_EIGHT times 16 dw 8 + GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_avx2) + + EXTN(jsimd_h2v1_fancy_upsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + pushpic ebx +@@ -227,6 +228,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_avx2) + + EXTN(jsimd_h2v2_fancy_upsample_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -570,6 +572,7 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v1_upsample_avx2) + + EXTN(jsimd_h2v1_upsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -669,6 +672,7 @@ EXTN(jsimd_h2v1_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_upsample_avx2) + + EXTN(jsimd_h2v2_upsample_avx2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jdsample-mmx.asm b/simd/i386/jdsample-mmx.asm +index 12c49f0..7f2ab40 100644 +--- a/simd/i386/jdsample-mmx.asm ++++ b/simd/i386/jdsample-mmx.asm +@@ -59,6 +59,7 @@ PW_EIGHT times 4 dw 8 + GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_mmx) + + EXTN(jsimd_h2v1_fancy_upsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + pushpic ebx +@@ -217,6 +218,7 @@ EXTN(jsimd_h2v1_fancy_upsample_mmx): + GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_mmx) + + EXTN(jsimd_h2v2_fancy_upsample_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -541,6 +543,7 @@ EXTN(jsimd_h2v2_fancy_upsample_mmx): + GLOBAL_FUNCTION(jsimd_h2v1_upsample_mmx) + + EXTN(jsimd_h2v1_upsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -640,6 +643,7 @@ EXTN(jsimd_h2v1_upsample_mmx): + GLOBAL_FUNCTION(jsimd_h2v2_upsample_mmx) + + EXTN(jsimd_h2v2_upsample_mmx): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jdsample-sse2.asm b/simd/i386/jdsample-sse2.asm +index 4e28d2f..3311b25 100644 +--- a/simd/i386/jdsample-sse2.asm ++++ b/simd/i386/jdsample-sse2.asm +@@ -59,6 +59,7 @@ PW_EIGHT times 8 dw 8 + GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_sse2) + + EXTN(jsimd_h2v1_fancy_upsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + pushpic ebx +@@ -216,6 +217,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_sse2) + + EXTN(jsimd_h2v2_fancy_upsample_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -538,6 +540,7 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v1_upsample_sse2) + + EXTN(jsimd_h2v1_upsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +@@ -635,6 +638,7 @@ EXTN(jsimd_h2v1_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_upsample_sse2) + + EXTN(jsimd_h2v2_upsample_sse2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jfdctflt-3dn.asm b/simd/i386/jfdctflt-3dn.asm +index 322ab16..109e36e 100644 +--- a/simd/i386/jfdctflt-3dn.asm ++++ b/simd/i386/jfdctflt-3dn.asm +@@ -56,6 +56,7 @@ PD_1_306 times 2 dd 1.306562964876376527856643 + GLOBAL_FUNCTION(jsimd_fdct_float_3dnow) + + EXTN(jsimd_fdct_float_3dnow): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jfdctflt-sse.asm b/simd/i386/jfdctflt-sse.asm +index 86952c6..b1e0576 100644 +--- a/simd/i386/jfdctflt-sse.asm ++++ b/simd/i386/jfdctflt-sse.asm +@@ -67,6 +67,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643 + GLOBAL_FUNCTION(jsimd_fdct_float_sse) + + EXTN(jsimd_fdct_float_sse): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jfdctfst-mmx.asm b/simd/i386/jfdctfst-mmx.asm +index 80645a5..be84fdb 100644 +--- a/simd/i386/jfdctfst-mmx.asm ++++ b/simd/i386/jfdctfst-mmx.asm +@@ -81,6 +81,7 @@ PW_F1306 times 4 dw F_1_306 << CONST_SHIFT + GLOBAL_FUNCTION(jsimd_fdct_ifast_mmx) + + EXTN(jsimd_fdct_ifast_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jfdctfst-sse2.asm b/simd/i386/jfdctfst-sse2.asm +index 446fa7a..945f9cf 100644 +--- a/simd/i386/jfdctfst-sse2.asm ++++ b/simd/i386/jfdctfst-sse2.asm +@@ -82,6 +82,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT + GLOBAL_FUNCTION(jsimd_fdct_ifast_sse2) + + EXTN(jsimd_fdct_ifast_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jfdctint-avx2.asm b/simd/i386/jfdctint-avx2.asm +index 23cf733..56acb63 100644 +--- a/simd/i386/jfdctint-avx2.asm ++++ b/simd/i386/jfdctint-avx2.asm +@@ -260,6 +260,7 @@ PW_1_NEG1 times 8 dw 1 + GLOBAL_FUNCTION(jsimd_fdct_islow_avx2) + + EXTN(jsimd_fdct_islow_avx2): ++ _endbr32 + push ebp + mov ebp, esp + pushpic ebx +diff --git a/simd/i386/jfdctint-mmx.asm b/simd/i386/jfdctint-mmx.asm +index 34a43b9..4d1e773 100644 +--- a/simd/i386/jfdctint-mmx.asm ++++ b/simd/i386/jfdctint-mmx.asm +@@ -102,6 +102,7 @@ PW_DESCALE_P2X times 4 dw 1 << (PASS1_BITS - 1) + GLOBAL_FUNCTION(jsimd_fdct_islow_mmx) + + EXTN(jsimd_fdct_islow_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jfdctint-sse2.asm b/simd/i386/jfdctint-sse2.asm +index 6f8e18c..3954c8f 100644 +--- a/simd/i386/jfdctint-sse2.asm ++++ b/simd/i386/jfdctint-sse2.asm +@@ -103,6 +103,7 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) + GLOBAL_FUNCTION(jsimd_fdct_islow_sse2) + + EXTN(jsimd_fdct_islow_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctflt-3dn.asm b/simd/i386/jidctflt-3dn.asm +index 8795191..eb49902 100644 +--- a/simd/i386/jidctflt-3dn.asm ++++ b/simd/i386/jidctflt-3dn.asm +@@ -65,6 +65,7 @@ PB_CENTERJSAMP times 8 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_float_3dnow) + + EXTN(jsimd_idct_float_3dnow): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctflt-sse.asm b/simd/i386/jidctflt-sse.asm +index b27ecfd..ffe54f8 100644 +--- a/simd/i386/jidctflt-sse.asm ++++ b/simd/i386/jidctflt-sse.asm +@@ -75,6 +75,7 @@ PB_CENTERJSAMP times 8 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_float_sse) + + EXTN(jsimd_idct_float_sse): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctflt-sse2.asm b/simd/i386/jidctflt-sse2.asm +index c646eae..fd1fe35 100644 +--- a/simd/i386/jidctflt-sse2.asm ++++ b/simd/i386/jidctflt-sse2.asm +@@ -75,6 +75,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_float_sse2) + + EXTN(jsimd_idct_float_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctfst-mmx.asm b/simd/i386/jidctfst-mmx.asm +index 24622d4..00940b8 100644 +--- a/simd/i386/jidctfst-mmx.asm ++++ b/simd/i386/jidctfst-mmx.asm +@@ -96,6 +96,7 @@ PB_CENTERJSAMP times 8 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_ifast_mmx) + + EXTN(jsimd_idct_ifast_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctfst-sse2.asm b/simd/i386/jidctfst-sse2.asm +index 19704ff..1f4af33 100644 +--- a/simd/i386/jidctfst-sse2.asm ++++ b/simd/i386/jidctfst-sse2.asm +@@ -94,6 +94,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_ifast_sse2) + + EXTN(jsimd_idct_ifast_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctint-avx2.asm b/simd/i386/jidctint-avx2.asm +index 199c7df..2eb606a 100644 +--- a/simd/i386/jidctint-avx2.asm ++++ b/simd/i386/jidctint-avx2.asm +@@ -296,6 +296,7 @@ PW_1_NEG1 times 8 dw 1 + GLOBAL_FUNCTION(jsimd_idct_islow_avx2) + + EXTN(jsimd_idct_islow_avx2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctint-mmx.asm b/simd/i386/jidctint-mmx.asm +index f15c8d3..2d91b7e 100644 +--- a/simd/i386/jidctint-mmx.asm ++++ b/simd/i386/jidctint-mmx.asm +@@ -109,6 +109,7 @@ PB_CENTERJSAMP times 8 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_islow_mmx) + + EXTN(jsimd_idct_islow_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctint-sse2.asm b/simd/i386/jidctint-sse2.asm +index 43e3201..804be19 100644 +--- a/simd/i386/jidctint-sse2.asm ++++ b/simd/i386/jidctint-sse2.asm +@@ -107,6 +107,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_islow_sse2) + + EXTN(jsimd_idct_islow_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +diff --git a/simd/i386/jidctred-mmx.asm b/simd/i386/jidctred-mmx.asm +index e2307e1..cb43106 100644 +--- a/simd/i386/jidctred-mmx.asm ++++ b/simd/i386/jidctred-mmx.asm +@@ -117,6 +117,7 @@ PB_CENTERJSAMP times 8 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_4x4_mmx) + + EXTN(jsimd_idct_4x4_mmx): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -504,6 +505,7 @@ EXTN(jsimd_idct_4x4_mmx): + GLOBAL_FUNCTION(jsimd_idct_2x2_mmx) + + EXTN(jsimd_idct_2x2_mmx): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jidctred-sse2.asm b/simd/i386/jidctred-sse2.asm +index 6e56494..2a61b9e 100644 +--- a/simd/i386/jidctred-sse2.asm ++++ b/simd/i386/jidctred-sse2.asm +@@ -115,6 +115,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_4x4_sse2) + + EXTN(jsimd_idct_4x4_sse2): ++ _endbr32 + push ebp + mov eax, esp ; eax = original ebp + sub esp, byte 4 +@@ -425,6 +426,7 @@ EXTN(jsimd_idct_4x4_sse2): + GLOBAL_FUNCTION(jsimd_idct_2x2_sse2) + + EXTN(jsimd_idct_2x2_sse2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +diff --git a/simd/i386/jquant-3dn.asm b/simd/i386/jquant-3dn.asm +index 5cb60ca..a0599eb 100644 +--- a/simd/i386/jquant-3dn.asm ++++ b/simd/i386/jquant-3dn.asm +@@ -36,6 +36,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_float_3dnow) + + EXTN(jsimd_convsamp_float_3dnow): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -138,6 +139,7 @@ EXTN(jsimd_convsamp_float_3dnow): + GLOBAL_FUNCTION(jsimd_quantize_float_3dnow) + + EXTN(jsimd_quantize_float_3dnow): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jquant-mmx.asm b/simd/i386/jquant-mmx.asm +index 61305c6..080021b 100644 +--- a/simd/i386/jquant-mmx.asm ++++ b/simd/i386/jquant-mmx.asm +@@ -36,6 +36,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_mmx) + + EXTN(jsimd_convsamp_mmx): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -145,6 +146,7 @@ EXTN(jsimd_convsamp_mmx): + GLOBAL_FUNCTION(jsimd_quantize_mmx) + + EXTN(jsimd_quantize_mmx): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jquant-sse.asm b/simd/i386/jquant-sse.asm +index 218adc9..cacd2a9 100644 +--- a/simd/i386/jquant-sse.asm ++++ b/simd/i386/jquant-sse.asm +@@ -36,6 +36,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_float_sse) + + EXTN(jsimd_convsamp_float_sse): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -138,6 +139,7 @@ EXTN(jsimd_convsamp_float_sse): + GLOBAL_FUNCTION(jsimd_quantize_float_sse) + + EXTN(jsimd_quantize_float_sse): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jquantf-sse2.asm b/simd/i386/jquantf-sse2.asm +index a881ab5..6f4789c 100644 +--- a/simd/i386/jquantf-sse2.asm ++++ b/simd/i386/jquantf-sse2.asm +@@ -36,6 +36,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_float_sse2) + + EXTN(jsimd_convsamp_float_sse2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_float_sse2): + GLOBAL_FUNCTION(jsimd_quantize_float_sse2) + + EXTN(jsimd_quantize_float_sse2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jquanti-avx2.asm b/simd/i386/jquanti-avx2.asm +index 5ed6bec..efcddd2 100644 +--- a/simd/i386/jquanti-avx2.asm ++++ b/simd/i386/jquanti-avx2.asm +@@ -37,6 +37,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_avx2) + + EXTN(jsimd_convsamp_avx2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -130,6 +131,7 @@ EXTN(jsimd_convsamp_avx2): + GLOBAL_FUNCTION(jsimd_quantize_avx2) + + EXTN(jsimd_quantize_avx2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/i386/jquanti-sse2.asm b/simd/i386/jquanti-sse2.asm +index 0a50940..98d39e0 100644 +--- a/simd/i386/jquanti-sse2.asm ++++ b/simd/i386/jquanti-sse2.asm +@@ -36,6 +36,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_sse2) + + EXTN(jsimd_convsamp_sse2): ++ _endbr32 + push ebp + mov ebp, esp + push ebx +@@ -121,6 +122,7 @@ EXTN(jsimd_convsamp_sse2): + GLOBAL_FUNCTION(jsimd_quantize_sse2) + + EXTN(jsimd_quantize_sse2): ++ _endbr32 + push ebp + mov ebp, esp + ; push ebx ; unused +diff --git a/simd/nasm/jsimdext.inc b/simd/nasm/jsimdext.inc +index bebcb20..4b0cd69 100644 +--- a/simd/nasm/jsimdext.inc ++++ b/simd/nasm/jsimdext.inc +@@ -513,6 +513,14 @@ const_base: + + %endif + ++%imacro _endbr32 0 ++ dd 0xfb1e0ff3 ++%endmacro ++ ++%imacro _endbr64 0 ++ dd 0xfa1e0ff3 ++%endmacro ++ + ; -------------------------------------------------------------------------- + ; Defines picked up from the C headers + ; +diff --git a/simd/x86_64/jccolext-avx2.asm b/simd/x86_64/jccolext-avx2.asm +index dd7ea39..e6c8283 100644 +--- a/simd/x86_64/jccolext-avx2.asm ++++ b/simd/x86_64/jccolext-avx2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_avx2) + + EXTN(jsimd_rgb_ycc_convert_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jccolext-sse2.asm b/simd/x86_64/jccolext-sse2.asm +index bc1e817..4bb1af6 100644 +--- a/simd/x86_64/jccolext-sse2.asm ++++ b/simd/x86_64/jccolext-sse2.asm +@@ -40,6 +40,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_sse2) + + EXTN(jsimd_rgb_ycc_convert_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jcgryext-avx2.asm b/simd/x86_64/jcgryext-avx2.asm +index c8c8d12..12ecb7e 100644 +--- a/simd/x86_64/jcgryext-avx2.asm ++++ b/simd/x86_64/jcgryext-avx2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_gray_convert_avx2) + + EXTN(jsimd_rgb_gray_convert_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jcgryext-sse2.asm b/simd/x86_64/jcgryext-sse2.asm +index 7e5a0f2..e3a2413 100644 +--- a/simd/x86_64/jcgryext-sse2.asm ++++ b/simd/x86_64/jcgryext-sse2.asm +@@ -40,6 +40,7 @@ + GLOBAL_FUNCTION(jsimd_rgb_gray_convert_sse2) + + EXTN(jsimd_rgb_gray_convert_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jchuff-sse2.asm b/simd/x86_64/jchuff-sse2.asm +index 0c2cdd6..44ea81d 100644 +--- a/simd/x86_64/jchuff-sse2.asm ++++ b/simd/x86_64/jchuff-sse2.asm +@@ -261,6 +261,7 @@ times 1 << 15 db 16 + GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2) + + EXTN(jsimd_huff_encode_one_block_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + +diff --git a/simd/x86_64/jcphuff-sse2.asm b/simd/x86_64/jcphuff-sse2.asm +index 11db4b2..2157e97 100644 +--- a/simd/x86_64/jcphuff-sse2.asm ++++ b/simd/x86_64/jcphuff-sse2.asm +@@ -282,6 +282,7 @@ + GLOBAL_FUNCTION(jsimd_encode_mcu_AC_first_prepare_sse2) + + EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits +@@ -445,6 +446,7 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2): + GLOBAL_FUNCTION(jsimd_encode_mcu_AC_refine_prepare_sse2) + + EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + and rsp, byte (-SIZEOF_XMMWORD) ; align to 128 bits +diff --git a/simd/x86_64/jcsample-avx2.asm b/simd/x86_64/jcsample-avx2.asm +index 589c52b..7d8d4e0 100644 +--- a/simd/x86_64/jcsample-avx2.asm ++++ b/simd/x86_64/jcsample-avx2.asm +@@ -44,6 +44,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_downsample_avx2) + + EXTN(jsimd_h2v1_downsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 6 +@@ -205,6 +206,7 @@ EXTN(jsimd_h2v1_downsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_downsample_avx2) + + EXTN(jsimd_h2v2_downsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 6 +diff --git a/simd/x86_64/jcsample-sse2.asm b/simd/x86_64/jcsample-sse2.asm +index 7a4f1bc..8932b94 100644 +--- a/simd/x86_64/jcsample-sse2.asm ++++ b/simd/x86_64/jcsample-sse2.asm +@@ -43,6 +43,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_downsample_sse2) + + EXTN(jsimd_h2v1_downsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 6 +@@ -187,6 +188,7 @@ EXTN(jsimd_h2v1_downsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_downsample_sse2) + + EXTN(jsimd_h2v2_downsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 6 +diff --git a/simd/x86_64/jdcolext-avx2.asm b/simd/x86_64/jdcolext-avx2.asm +index 070436c..6c0a212 100644 +--- a/simd/x86_64/jdcolext-avx2.asm ++++ b/simd/x86_64/jdcolext-avx2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_avx2) + + EXTN(jsimd_ycc_rgb_convert_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jdcolext-sse2.asm b/simd/x86_64/jdcolext-sse2.asm +index bba3a30..28d05e4 100644 +--- a/simd/x86_64/jdcolext-sse2.asm ++++ b/simd/x86_64/jdcolext-sse2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_sse2) + + EXTN(jsimd_ycc_rgb_convert_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jdmrgext-avx2.asm b/simd/x86_64/jdmrgext-avx2.asm +index 1191645..f58384c 100644 +--- a/simd/x86_64/jdmrgext-avx2.asm ++++ b/simd/x86_64/jdmrgext-avx2.asm +@@ -42,6 +42,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_avx2) + + EXTN(jsimd_h2v1_merged_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +@@ -506,6 +507,7 @@ EXTN(jsimd_h2v1_merged_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_avx2) + + EXTN(jsimd_h2v2_merged_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +diff --git a/simd/x86_64/jdmrgext-sse2.asm b/simd/x86_64/jdmrgext-sse2.asm +index 8988dd0..8641aa3 100644 +--- a/simd/x86_64/jdmrgext-sse2.asm ++++ b/simd/x86_64/jdmrgext-sse2.asm +@@ -41,6 +41,7 @@ + GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_sse2) + + EXTN(jsimd_h2v1_merged_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +@@ -448,6 +449,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_sse2) + + EXTN(jsimd_h2v2_merged_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +diff --git a/simd/x86_64/jdsample-avx2.asm b/simd/x86_64/jdsample-avx2.asm +index c6ddbb5..c5594ed 100644 +--- a/simd/x86_64/jdsample-avx2.asm ++++ b/simd/x86_64/jdsample-avx2.asm +@@ -62,6 +62,7 @@ PW_EIGHT times 16 dw 8 + GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_avx2) + + EXTN(jsimd_h2v1_fancy_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push_xmm 3 +@@ -215,6 +216,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_avx2) + + EXTN(jsimd_h2v2_fancy_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +@@ -524,6 +526,7 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v1_upsample_avx2) + + EXTN(jsimd_h2v1_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +@@ -612,6 +615,7 @@ EXTN(jsimd_h2v1_upsample_avx2): + GLOBAL_FUNCTION(jsimd_h2v2_upsample_avx2) + + EXTN(jsimd_h2v2_upsample_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +diff --git a/simd/x86_64/jdsample-sse2.asm b/simd/x86_64/jdsample-sse2.asm +index 24cd389..27bf771 100644 +--- a/simd/x86_64/jdsample-sse2.asm ++++ b/simd/x86_64/jdsample-sse2.asm +@@ -61,6 +61,7 @@ PW_EIGHT times 8 dw 8 + GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_sse2) + + EXTN(jsimd_h2v1_fancy_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +@@ -202,6 +203,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_sse2) + + EXTN(jsimd_h2v2_fancy_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +@@ -497,6 +499,7 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v1_upsample_sse2) + + EXTN(jsimd_h2v1_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +@@ -583,6 +586,7 @@ EXTN(jsimd_h2v1_upsample_sse2): + GLOBAL_FUNCTION(jsimd_h2v2_upsample_sse2) + + EXTN(jsimd_h2v2_upsample_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +diff --git a/simd/x86_64/jfdctflt-sse.asm b/simd/x86_64/jfdctflt-sse.asm +index 3595496..b5abeee 100644 +--- a/simd/x86_64/jfdctflt-sse.asm ++++ b/simd/x86_64/jfdctflt-sse.asm +@@ -66,6 +66,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643 + GLOBAL_FUNCTION(jsimd_fdct_float_sse) + + EXTN(jsimd_fdct_float_sse): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jfdctfst-sse2.asm b/simd/x86_64/jfdctfst-sse2.asm +index d33c58a..78af5e4 100644 +--- a/simd/x86_64/jfdctfst-sse2.asm ++++ b/simd/x86_64/jfdctfst-sse2.asm +@@ -81,6 +81,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT + GLOBAL_FUNCTION(jsimd_fdct_ifast_sse2) + + EXTN(jsimd_fdct_ifast_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jfdctint-avx2.asm b/simd/x86_64/jfdctint-avx2.asm +index d0afe5e..b083fbb 100644 +--- a/simd/x86_64/jfdctint-avx2.asm ++++ b/simd/x86_64/jfdctint-avx2.asm +@@ -260,6 +260,7 @@ PW_1_NEG1 times 8 dw 1 + GLOBAL_FUNCTION(jsimd_fdct_islow_avx2) + + EXTN(jsimd_fdct_islow_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 1 +diff --git a/simd/x86_64/jfdctint-sse2.asm b/simd/x86_64/jfdctint-sse2.asm +index 024ce90..88ea491 100644 +--- a/simd/x86_64/jfdctint-sse2.asm ++++ b/simd/x86_64/jfdctint-sse2.asm +@@ -102,6 +102,7 @@ PW_DESCALE_P2X times 8 dw 1 << (PASS1_BITS - 1) + GLOBAL_FUNCTION(jsimd_fdct_islow_sse2) + + EXTN(jsimd_fdct_islow_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jidctflt-sse2.asm b/simd/x86_64/jidctflt-sse2.asm +index 952fbe3..8610710 100644 +--- a/simd/x86_64/jidctflt-sse2.asm ++++ b/simd/x86_64/jidctflt-sse2.asm +@@ -76,6 +76,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_float_sse2) + + EXTN(jsimd_idct_float_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jidctfst-sse2.asm b/simd/x86_64/jidctfst-sse2.asm +index a3da8d8..351ee06 100644 +--- a/simd/x86_64/jidctfst-sse2.asm ++++ b/simd/x86_64/jidctfst-sse2.asm +@@ -95,6 +95,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_ifast_sse2) + + EXTN(jsimd_idct_ifast_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jidctint-avx2.asm b/simd/x86_64/jidctint-avx2.asm +index 528da01..00087c7 100644 +--- a/simd/x86_64/jidctint-avx2.asm ++++ b/simd/x86_64/jidctint-avx2.asm +@@ -282,6 +282,7 @@ PW_1_NEG1 times 8 dw 1 + GLOBAL_FUNCTION(jsimd_idct_islow_avx2) + + EXTN(jsimd_idct_islow_avx2): ++ _endbr64 + push rbp + mov rbp, rsp ; rbp = aligned rbp + push_xmm 4 +diff --git a/simd/x86_64/jidctint-sse2.asm b/simd/x86_64/jidctint-sse2.asm +index 92f633e..9301e81 100644 +--- a/simd/x86_64/jidctint-sse2.asm ++++ b/simd/x86_64/jidctint-sse2.asm +@@ -108,6 +108,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_islow_sse2) + + EXTN(jsimd_idct_islow_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +diff --git a/simd/x86_64/jidctred-sse2.asm b/simd/x86_64/jidctred-sse2.asm +index 1ec500c..e74162d 100644 +--- a/simd/x86_64/jidctred-sse2.asm ++++ b/simd/x86_64/jidctred-sse2.asm +@@ -116,6 +116,7 @@ PB_CENTERJSAMP times 16 db CENTERJSAMPLE + GLOBAL_FUNCTION(jsimd_idct_4x4_sse2) + + EXTN(jsimd_idct_4x4_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + push r15 +@@ -413,6 +414,7 @@ EXTN(jsimd_idct_4x4_sse2): + GLOBAL_FUNCTION(jsimd_idct_2x2_sse2) + + EXTN(jsimd_idct_2x2_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 4 +diff --git a/simd/x86_64/jquantf-sse2.asm b/simd/x86_64/jquantf-sse2.asm +index 232bbb2..2d65986 100644 +--- a/simd/x86_64/jquantf-sse2.asm ++++ b/simd/x86_64/jquantf-sse2.asm +@@ -37,6 +37,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_float_sse2) + + EXTN(jsimd_convsamp_float_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +@@ -109,6 +110,7 @@ EXTN(jsimd_convsamp_float_sse2): + GLOBAL_FUNCTION(jsimd_quantize_float_sse2) + + EXTN(jsimd_quantize_float_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +diff --git a/simd/x86_64/jquanti-avx2.asm b/simd/x86_64/jquanti-avx2.asm +index 66104d7..a7ea496 100644 +--- a/simd/x86_64/jquanti-avx2.asm ++++ b/simd/x86_64/jquanti-avx2.asm +@@ -38,6 +38,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_avx2) + + EXTN(jsimd_convsamp_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_avx2): + GLOBAL_FUNCTION(jsimd_quantize_avx2) + + EXTN(jsimd_quantize_avx2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +diff --git a/simd/x86_64/jquanti-sse2.asm b/simd/x86_64/jquanti-sse2.asm +index 11e9f4c..24cb1cf 100644 +--- a/simd/x86_64/jquanti-sse2.asm ++++ b/simd/x86_64/jquanti-sse2.asm +@@ -37,6 +37,7 @@ + GLOBAL_FUNCTION(jsimd_convsamp_sse2) + + EXTN(jsimd_convsamp_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_sse2): + GLOBAL_FUNCTION(jsimd_quantize_sse2) + + EXTN(jsimd_quantize_sse2): ++ _endbr64 + push rbp + mov rbp, rsp + collect_args 3 +-- diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch new file mode 100644 index 0000000..ef4c14d --- /dev/null +++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch @@ -0,0 +1,33 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index adb0ca4..902c271 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1768,18 +1768,6 @@ endif() + + install(TARGETS rdjpgcom wrjpgcom RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.ijg +- ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.c +- ${CMAKE_CURRENT_SOURCE_DIR}/tjexample.c +- ${CMAKE_CURRENT_SOURCE_DIR}/libjpeg.txt +- ${CMAKE_CURRENT_SOURCE_DIR}/structure.txt +- ${CMAKE_CURRENT_SOURCE_DIR}/usage.txt ${CMAKE_CURRENT_SOURCE_DIR}/wizard.txt +- ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) +-if(WITH_JAVA) +- install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/java/TJExample.java +- DESTINATION ${CMAKE_INSTALL_DOCDIR}) +-endif() +- + if(UNIX OR MINGW) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cjpeg.1 + ${CMAKE_CURRENT_SOURCE_DIR}/djpeg.1 ${CMAKE_CURRENT_SOURCE_DIR}/jpegtran.1 +@@ -1803,7 +1791,7 @@ install(EXPORT ${CMAKE_PROJECT_NAME}Targets + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jconfig.h + ${CMAKE_CURRENT_SOURCE_DIR}/jerror.h ${CMAKE_CURRENT_SOURCE_DIR}/jmorecfg.h +- ${CMAKE_CURRENT_SOURCE_DIR}/jpeglib.h ++ ${CMAKE_CURRENT_SOURCE_DIR}/jpeglib.h ${CMAKE_CURRENT_SOURCE_DIR}/jpegint.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + include(cmakescripts/BuildPackages.cmake) +-- diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec b/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec new file mode 100644 index 0000000..6354a9a --- /dev/null +++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec @@ -0,0 +1,508 @@ +%global realname libjpeg-turbo +%global _sys_bindir %_bindir +%global _prefix /opt/rx +%global _bindir %{_prefix}/bin +%global _libdir %{_prefix}/%{_lib} +%global _datadir %{_prefix}/share +%global _includedir %{_prefix}/include +%global _mandir %{_datadir}/man + +Name: rx-libjpeg-turbo +Version: 3.0.2 +Release: 1%{?dist} +Summary: A MMX/SSE2/SIMD accelerated library for manipulating JPEG image files +License: IJG +URL: http://sourceforge.net/projects/libjpeg-turbo + +Source0: http://downloads.sourceforge.net/%{realname}/%{realname}-%{version}.tar.gz +Patch0: libjpeg-turbo-cmake.patch +Patch1: libjpeg-turbo-CET.patch + +BuildRequires: gcc +BuildRequires: cmake +BuildRequires: libtool +BuildRequires: nasm + + +%description +The libjpeg-turbo package contains a library of functions for manipulating JPEG +images. + +%package devel +Summary: Headers for the libjpeg-turbo library +Requires: rx-libjpeg-turbo%{?_isa} = %{version}-%{release} + + +%description devel +This package contains header files necessary for developing programs which will +manipulate JPEG files using the libjpeg-turbo library. + +%package utils +Summary: Utilities for manipulating JPEG images +Requires: rx-libjpeg-turbo%{?_isa} = %{version}-%{release} + +%description utils +The libjpeg-turbo-utils package contains simple client programs for accessing +the libjpeg functions. It contains cjpeg, djpeg, jpegtran, rdjpgcom and +wrjpgcom. Cjpeg compresses an image file into JPEG format. Djpeg decompresses a +JPEG file into a regular image file. Jpegtran can perform various useful +transformations on JPEG files. Rdjpgcom displays any text comments included in a +JPEG file. Wrjpgcom inserts text comments into a JPEG file. + +%package -n rx-turbojpeg +Summary: TurboJPEG library + +%description -n rx-turbojpeg +The turbojpeg package contains the TurboJPEG shared library. + +%package -n rx-turbojpeg-devel +Summary: Headers for the TurboJPEG library +Requires: rx-turbojpeg%{?_isa} = %{version}-%{release} + +%description -n rx-turbojpeg-devel +This package contains header files necessary for developing programs which will +manipulate JPEG files using the TurboJPEG library. + +%prep +%autosetup -p1 -n %{realname}-%{version} + +%build +export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} +export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} $LDFLAGS" +# NASM object files are missing GNU Property note for Intel CET, +# force it on the resulting library +%ifarch %{ix86} x86_64 +export LDFLAGS="$RPM_LD_FLAGS -Wl,-z,ibt -Wl,-z,shstk" +%endif + +%{cmake} -DCMAKE_SKIP_RPATH:BOOL=YES \ + -DCMAKE_SKIP_INSTALL_RPATH:BOOL=YES \ +%ifarch s390x + -DFLOATTEST:STRING="fp-contract" \ +%endif + -DENABLE_STATIC:BOOL=NO + +%cmake_build + +%install +%cmake_install +find %{buildroot} -name "*.la" -delete + +# Fix perms +chmod -x README.md + +# Remove tjbench +rm -f %{buildroot}/%{_bindir}/tjbench + +# multilib header hack +# we only apply this to known Red Hat multilib arches, per bug #1264675 +case `uname -i` in + i386 | ppc | s390 | sparc ) + wordsize="32" + ;; + x86_64 | ppc64 | s390x | sparc64 ) + wordsize="64" + ;; + *) + wordsize="" + ;; +esac + +if test -n "$wordsize" +then + mv $RPM_BUILD_ROOT%{_includedir}/jconfig.h \ + $RPM_BUILD_ROOT%{_includedir}/jconfig-$wordsize.h + + cat >$RPM_BUILD_ROOT%{_includedir}/jconfig.h < + +#if __WORDSIZE == 32 +# include "jconfig-32.h" +#elif __WORDSIZE == 64 +# include "jconfig-64.h" +#else +# error "unexpected value for __WORDSIZE macro" +#endif + +#endif +EOF + +fi + +mkdir -p %{buildroot}%{_sys_bindir} +for bin in cjpeg djpeg jpegtran rdjpgcom wrjpgcom +do + ln -sf %{_bindir}/${bin} %{buildroot}/%{_sys_bindir}/${bin}-3 +done + +%check +export LD_LIBRARY_PATH=%{buildroot}%{_libdir} +%ctest + +%ldconfig_scriptlets +%ldconfig_scriptlets -n rx-turbojpeg + +%files +%license LICENSE.md +%doc README.md README.ijg ChangeLog.md +%{_libdir}/libjpeg.so.62* + +%files devel +%doc coderules.txt jconfig.txt libjpeg.txt structure.txt +%{_includedir}/jconfig*.h +%{_includedir}/jerror.h +%{_includedir}/jmorecfg.h +%{_includedir}/jpegint.h +%{_includedir}/jpeglib.h +%{_libdir}/libjpeg.so +%{_libdir}/pkgconfig/libjpeg.pc +%{_libdir}/cmake/%{realname}/%{realname}*.cmake + +%files utils +%doc usage.txt wizard.txt +%{_sys_bindir}/*-3 +%{_bindir}/cjpeg +%{_bindir}/djpeg +%{_bindir}/jpegtran +%{_bindir}/rdjpgcom +%{_bindir}/wrjpgcom +%{_mandir}/man1/cjpeg.1* +%{_mandir}/man1/djpeg.1* +%{_mandir}/man1/jpegtran.1* +%{_mandir}/man1/rdjpgcom.1* +%{_mandir}/man1/wrjpgcom.1* + +%files -n rx-turbojpeg +%license LICENSE.md +%doc README.md README.ijg ChangeLog.md +%{_libdir}/libturbojpeg.so.0* + +%files -n rx-turbojpeg-devel +%doc tjexample.c +%{_includedir}/turbojpeg.h +%{_libdir}/libturbojpeg.so +%{_libdir}/pkgconfig/libturbojpeg.pc + +%changelog +* Tue May 7 2024 Raven - 3.0.2-1 +- New upstream release 3.0.2 + +* Mon Aug 15 2022 Nikola Forró - 2.1.4-1 +- New upstream release 2.1.4 (#2118023) + +* Thu Jul 21 2022 Fedora Release Engineering - 2.1.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Sun Feb 27 2022 Nikola Forró - 2.1.3-1 +- New upstream release 2.1.3 (#2058898) + +* Thu Jan 20 2022 Fedora Release Engineering - 2.1.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Mon Nov 22 2021 Matej Mužila - 2.1.2-1 +- New upstream release 2.1.2 (#2025141) + +* Wed Aug 11 2021 Nikola Forró - 2.1.1-1 +- New upstream release 2.1.1 (#1991844) + +* Thu Jul 22 2021 Fedora Release Engineering - 2.1.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Mon Apr 26 2021 Nikola Forró - 2.1.0-1 +- New upstream release 2.1.0 (#1953074) + +* Thu Mar 25 2021 Nikola Forró - 2.0.90-2 +- Fix CVE-2021-20205 (#1937387) + +* Thu Jan 28 2021 Nikola Forró - 2.0.90-1 +- New upstream release 2.0.90 (#1898427) + +* Tue Jan 26 2021 Fedora Release Engineering - 2.0.5-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Aug 04 2020 Nikola Forró - 2.0.5-5 +- Fix FTBFS (#1864007) + +* Sat Aug 01 2020 Fedora Release Engineering - 2.0.5-4 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jul 28 2020 Fedora Release Engineering - 2.0.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 13 2020 Tom Stellard - 2.0.5-2 +- Use make macros +- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro + +* Fri Jul 03 2020 Nikola Forró - 2.0.5-1 +- New upstream release 2.0.5 (#1850293) + +* Tue Jun 16 2020 Nikola Forró - 2.0.4-3 +- Fix CVE-2020-13790 (#1847159) + +* Wed Jan 29 2020 Fedora Release Engineering - 2.0.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Jan 08 2020 Nikola Forró - 2.0.4-1 +- New upstream release 2.0.4 (#1787793) + +* Thu Sep 05 2019 Nikola Forró - 2.0.3-1 +- New upstream release 2.0.3 (#1749130) + +* Thu Jul 25 2019 Fedora Release Engineering - 2.0.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jun 04 2019 Nikola Forró - 2.0.2-3 +- Fix LDFLAGS + +* Mon Apr 29 2019 Nikola Forró - 2.0.2-2 +- Support running with Intel CET + +* Wed Feb 27 2019 Nikola Forró - 2.0.2-1 +- New upstream release 2.0.2 + +* Fri Feb 01 2019 Fedora Release Engineering - 2.0.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Jan 11 2019 Nikola Forró - 2.0.0-3 +- Fix CVE-2018-19664 (#1656219) + +* Fri Jan 11 2019 Nikola Forró - 2.0.0-2 +- Fix CVE-2018-20330 (#1665224) + +* Mon Jul 30 2018 Nikola Forró - 2.0.0-1 +- New upstream release 2.0.0 (#1609439) + +* Fri Jul 13 2018 Fedora Release Engineering - 1.5.90-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Fri Jun 29 2018 Nikola Forró - 1.5.90-3 +- Fix CVE-2018-1152 (#1593555) + +* Fri Jun 15 2018 Nikola Forró - 1.5.90-2 +- Fix CVE-2018-11813 (#1588804) + +* Wed Mar 28 2018 Nikola Forró - 1.5.90-1 +- New upstream release 1.5.90 (#1560219) + +* Tue Feb 20 2018 Nikola Forró - 1.5.3-4 +- Add missing gcc build dependency + +* Wed Feb 07 2018 Fedora Release Engineering - 1.5.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Feb 03 2018 Igor Gnatenko - 1.5.3-2 +- Switch to %%ldconfig_scriptlets + +* Tue Dec 19 2017 Nikola Forró - 1.5.3-1 +- New upstream release 1.5.3 (#1468783) + +* Tue Dec 19 2017 Nikola Forró - 1.5.1-5 +- re-enable check on ppc64(le) + +* Thu Aug 03 2017 Fedora Release Engineering - 1.5.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1.5.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu May 18 2017 Karsten Hopp - 1.5.1-2 +- disable check on ppc64(le) + +* Fri Feb 10 2017 Fedora Release Engineering - 1.5.1-1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Oct 06 2016 Petr Hracek - 1.5.1-1 +- New upstream relelase 1.5.1 (#1377903) + +* Wed Sep 21 2016 Peter Robinson 1.5.0-4 +- Add upstream aarch64 NEON fix, re-enable SIMD on aarch64 + +* Mon Sep 19 2016 Peter Robinson 1.5.0-3 +- Temporarily disable SIMD on aarch64 until upstream #97 is fixed +- Add NEON fix for ARMv7 + +* Tue Sep 13 2016 Peter Robinson 1.5.0-2 +- Add upstream fix to fix SIMD crash on aarch64 (rhbz #1368569) + +* Tue Jun 21 2016 Petr Hracek - 1.5.0-1 +- New upstream release 1.5.0 (#1343786) + +* Thu Mar 10 2016 Petr Hracek - 1.4.90-1 +- New upstream release 1.4.90 (#1313111) + +* Thu Feb 04 2016 Fedora Release Engineering - 1.4.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Oct 07 2015 Petr Hracek - 1.4.2-2 +- Fix problem with multilibs like jconfig.h (#1264675) + +* Wed Oct 07 2015 Petr Hracek - 1.4.2-1 +- New upstream release 1.4.2 (#1265034) + +* Tue Jun 16 2015 Peter Robinson 1.4.1-1 +- new upstream version 1.4.1 +- nasm available on all arches +- run tests with SMP + +* Tue Jan 20 2015 Petr Hracek - 1.4.0-1 +- new upstream version 1.4.0 (#1180442) + +* Wed Nov 26 2014 Petr Hracek - 1.3.90-3 +- libjpeg-turbo no longer defined macros like JPP (#1164815) + +* Wed Nov 19 2014 Petr Hracek - 1.3.90-2 +- Resolves #1161585 Add suport for secondary arches + +* Mon Oct 27 2014 Petr Hracek - 1.3.90-1 +- new upstream version 1.3.90 +Resolves #1135375 + +* Sun Aug 17 2014 Fedora Release Engineering - 1.3.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 1.3.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu Apr 17 2014 Simone Caronni - 1.3.1-2 +- Re-add libjpeg-devel requirements for broken packages since Fedora 13. + +* Wed Apr 16 2014 Petr Hracek - 1.3.1-1 +- New upstream version +- Remove upstreamed patches, add missing jpegint.h +- Clean up SPEC file +- Disable --static subpackage +- Remove libjpeg obsolency, removed in Fedora 13 + +* Thu Dec 19 2013 Petr Hracek - 1.3.0-2 +- Apply fixes CVE-2013-6629, CVE-2013-6630 (#20131737) + +* Thu Jul 25 2013 Petr Hracek - 1.3.0-1 +- new upstream version +- no soname bump change + +* Tue Mar 26 2013 Adam Tkac - 1.2.90-2 +- rebuild for ARM64 support + +* Fri Feb 08 2013 Adam Tkac 1.2.90-1 +- update to 1.2.90 + +* Mon Feb 04 2013 Adam Tkac 1.2.90-0.1.20130204svn922 +- update to 1.2.80 snapshot (#854695) +- run `make test` during build + +* Fri Jan 18 2013 Adam Tkac 1.2.1-6 +- build with jpeg6 API/ABI (jpeg8-ABI feature was dropped) + +* Tue Dec 04 2012 Adam Tkac 1.2.1-5 +- change license to IJG (#877517) + +* Wed Oct 24 2012 Adam Tkac 1.2.1-4 +- build with jpeg8 API/ABI (#854695) + +* Thu Oct 18 2012 Adam Tkac 1.2.1-3 +- minor provides tuning (#863231) + +* Thu Jul 19 2012 Fedora Release Engineering - 1.2.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon Jul 16 2012 Adam Tkac 1.2.1-1 +- update to 1.2.1 + +* Thu Mar 08 2012 Adam Tkac 1.2.0-1 +- update to 1.2.0 + +* Fri Jan 13 2012 Fedora Release Engineering - 1.1.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Mon Nov 21 2011 Orion Poplawski 1.1.1-3 +- Make turobojpeg-devel depend on turbojpeg + +* Fri Oct 7 2011 Orion Poplawski 1.1.1-2 +- Ship the turbojpeg library (#744258) + +* Mon Jul 11 2011 Adam Tkac 1.1.1-1 +- update to 1.1.1 + - ljt11-rh688712.patch merged + +* Tue Mar 22 2011 Adam Tkac 1.1.0-2 +- handle broken JPEGs better (#688712) + +* Tue Mar 01 2011 Adam Tkac 1.1.0-1 +- update to 1.1.0 + +* Tue Feb 08 2011 Fedora Release Engineering - 1.0.90-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Mon Jan 17 2011 Adam Tkac 1.0.90-1 +- update to 1.0.90 +- libjpeg-turbo10-rh639672.patch merged + +* Fri Oct 29 2010 Adam Tkac 1.0.1-3 +- add support for arithmetic coded files into decoder (#639672) + +* Wed Sep 29 2010 jkeating - 1.0.1-2 +- Rebuilt for gcc bug 634757 + +* Mon Sep 13 2010 Adam Tkac 1.0.1-1 +- update to 1.0.1 + - libjpeg-turbo10-rh617469.patch merged +- add -static subpkg (#632859) + +* Wed Aug 04 2010 Adam Tkac 1.0.0-3 +- fix huffman decoder to handle broken JPEGs well (#617469) + +* Fri Jul 02 2010 Adam Tkac 1.0.0-2 +- add libjpeg-devel%%{_isa} provides to -devel subpkg to satisfy imlib-devel + deps + +* Fri Jul 02 2010 Adam Tkac 1.0.0-1 +- update to 1.0.0 +- patches merged + - libjpeg-turbo-programs.patch + - libjpeg-turbo-nosimd.patch +- add libjpeg provides to the main package to workaround problems with broken + java-1.6.0-openjdk package + +* Fri Jul 02 2010 Adam Tkac 0.0.93-13 +- remove libjpeg provides from -utils subpkg + +* Wed Jun 30 2010 Rex Dieter 0.0.93-12 +- move Obsoletes: libjpeg to main pkg + +* Wed Jun 30 2010 Rex Dieter 0.0.93-11 +- -utils: Requires: %%name ... + +* Wed Jun 30 2010 Adam Tkac 0.0.93-10 +- add Provides = libjpeg to -utils subpackage + +* Mon Jun 28 2010 Adam Tkac 0.0.93-9 +- merge review related fixes (#600243) + +* Wed Jun 16 2010 Adam Tkac 0.0.93-8 +- merge review related fixes (#600243) + +* Mon Jun 14 2010 Adam Tkac 0.0.93-7 +- obsolete -static libjpeg subpackage (#600243) + +* Mon Jun 14 2010 Adam Tkac 0.0.93-6 +- improve package description a little (#600243) +- include example.c as %%doc in the -devel subpackage + +* Fri Jun 11 2010 Adam Tkac 0.0.93-5 +- don't use "fc12" disttag in obsoletes/provides (#600243) + +* Thu Jun 10 2010 Adam Tkac 0.0.93-4 +- fix compilation on platforms without MMX/SSE (#600243) + +* Thu Jun 10 2010 Adam Tkac 0.0.93-3 +- package review related fixes (#600243) + +* Wed Jun 09 2010 Adam Tkac 0.0.93-2 +- package review related fixes (#600243) + +* Fri Jun 04 2010 Adam Tkac 0.0.93-1 +- initial package diff --git a/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch b/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch new file mode 100644 index 0000000..01ca0dd --- /dev/null +++ b/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch @@ -0,0 +1,26 @@ +diff -rupN --no-dereference libwebp-1.3.2/src/dec/vp8l_dec.c libwebp-1.3.2-new/src/dec/vp8l_dec.c +--- libwebp-1.3.2/src/dec/vp8l_dec.c 2023-09-14 00:11:07.000000000 +0200 ++++ libwebp-1.3.2-new/src/dec/vp8l_dec.c 2023-09-28 20:47:39.648154201 +0200 +@@ -1241,9 +1241,20 @@ static int DecodeImageData(VP8LDecoder* + } + + br->eos_ = VP8LIsEndOfStream(br); +- if (dec->incremental_ && br->eos_ && src < src_end) { ++ // In incremental decoding: ++ // br->eos_ && src < src_last: if 'br' reached the end of the buffer and ++ // 'src_last' has not been reached yet, there is not enough data. 'dec' has to ++ // be reset until there is more data. ++ // !br->eos_ && src < src_last: this cannot happen as either the buffer is ++ // fully read, either enough has been read to reach 'src_last'. ++ // src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go ++ // beyond 'src_last' in case the image is cropped and an LZ77 goes further. ++ // The buffer might have been enough or there is some left. 'br->eos_' does ++ // not matter. ++ assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last); ++ if (dec->incremental_ && br->eos_ && src < src_last) { + RestoreState(dec); +- } else if (!br->eos_) { ++ } else if ((dec->incremental_ && src >= src_last) || !br->eos_) { + // Process the remaining rows corresponding to last row-block. + if (process_func != NULL) { + process_func(dec, row > last_row ? last_row : row); diff --git a/base/rx/rx-libwebp/fix-cmake-files-location.patch b/base/rx/rx-libwebp/fix-cmake-files-location.patch new file mode 100644 index 0000000..d9fe5c5 --- /dev/null +++ b/base/rx/rx-libwebp/fix-cmake-files-location.patch @@ -0,0 +1,11 @@ +--- a/CMakeLists.txt 2024-04-13 02:48:48.000000000 +0600 ++++ b/CMakeLists.txt 2024-05-13 10:06:57.159443901 +0600 +@@ -781,7 +781,7 @@ + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +-set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/) ++set(ConfigPackageLocation ${CMAKE_CONFIG_PREFIX}/cmake/${PROJECT_NAME}/) + install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${ConfigPackageLocation}) + diff --git a/base/rx/rx-libwebp/libwebp-cmakedir.patch b/base/rx/rx-libwebp/libwebp-cmakedir.patch new file mode 100644 index 0000000..811794a --- /dev/null +++ b/base/rx/rx-libwebp/libwebp-cmakedir.patch @@ -0,0 +1,12 @@ +diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt +--- libwebp-1.4.0/CMakeLists.txt 2024-04-14 15:30:20.053730120 +0200 ++++ libwebp-1.4.0-new/CMakeLists.txt 2024-04-14 15:30:20.056730140 +0200 +@@ -784,7 +784,7 @@ install( + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +-set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/) ++set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/) + install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${ConfigPackageLocation}) + diff --git a/base/rx/rx-libwebp/libwebp-freeglut.patch b/base/rx/rx-libwebp/libwebp-freeglut.patch new file mode 100644 index 0000000..676e71d --- /dev/null +++ b/base/rx/rx-libwebp/libwebp-freeglut.patch @@ -0,0 +1,35 @@ +diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt +--- libwebp-1.4.0/CMakeLists.txt 2024-04-12 22:48:48.000000000 +0200 ++++ libwebp-1.4.0-new/CMakeLists.txt 2024-04-14 15:30:19.981729641 +0200 +@@ -582,8 +582,8 @@ endif() + + if(WEBP_BUILD_VWEBP) + # vwebp +- find_package(GLUT) +- if(GLUT_FOUND) ++ find_package(FreeGLUT) ++ if(FreeGLUT_FOUND) + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp") + add_executable(vwebp ${VWEBP_SRCS}) +@@ -591,7 +591,7 @@ if(WEBP_BUILD_VWEBP) + vwebp + ${OPENGL_LIBRARIES} + exampleutil +- GLUT::GLUT ++ glut + imageioutil + webp + webpdemux) +diff -rupN --no-dereference libwebp-1.4.0/examples/vwebp.c libwebp-1.4.0-new/examples/vwebp.c +--- libwebp-1.4.0/examples/vwebp.c 2024-04-12 22:48:48.000000000 +0200 ++++ libwebp-1.4.0-new/examples/vwebp.c 2024-04-14 15:30:19.981729641 +0200 +@@ -28,7 +28,7 @@ + #if defined(HAVE_GLUT_GLUT_H) + #include + #else +-#include ++#include + #ifdef FREEGLUT + #include + #endif diff --git a/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch b/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch new file mode 100644 index 0000000..9cda88c --- /dev/null +++ b/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch @@ -0,0 +1,13 @@ +diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt +--- libwebp-1.4.0/CMakeLists.txt 2024-04-14 15:30:20.015729868 +0200 ++++ libwebp-1.4.0-new/CMakeLists.txt 2024-04-14 15:30:20.020729901 +0200 +@@ -301,6 +301,9 @@ macro(set_version FILE TARGET_NAME NAME_ + MACHO_CURRENT_VERSION + ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}.${LT_REVISION}) + endif() ++ if(WIN32) ++ set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX "-${LT_CURRENT_MINUS_AGE}${CMAKE_SHARED_LIBRARY_SUFFIX}") ++ endif(WIN32) + endmacro() + + # ############################################################################## diff --git a/base/rx/rx-libwebp/libwebp-rpath.patch b/base/rx/rx-libwebp/libwebp-rpath.patch new file mode 100644 index 0000000..e066270 --- /dev/null +++ b/base/rx/rx-libwebp/libwebp-rpath.patch @@ -0,0 +1,20 @@ +diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt +--- libwebp-1.4.0/CMakeLists.txt 2024-04-14 15:30:20.089730360 +0200 ++++ libwebp-1.4.0-new/CMakeLists.txt 2024-04-14 15:30:20.093730386 +0200 +@@ -114,11 +114,11 @@ endif() + include(cmake/deps.cmake) + include(GNUInstallDirs) + +-if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH) +- # Set the rpath to match autoconf/libtool behavior. Note this must be set +- # before target creation. +- set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +-endif() ++# if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH) ++# # Set the rpath to match autoconf/libtool behavior. Note this must be set ++# # before target creation. ++# set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") ++# endif() + + # ############################################################################## + # Options. diff --git a/base/rx/rx-libwebp/libwebp.spec b/base/rx/rx-libwebp/libwebp.spec new file mode 100644 index 0000000..b84282f --- /dev/null +++ b/base/rx/rx-libwebp/libwebp.spec @@ -0,0 +1,365 @@ +%global _hardened_build 1 +%global realname libwebp + +%global _root_prefix %{_prefix} +%global _root_libdir %{_root_prefix}/%{_lib} +%global _root_bindir %{_root_prefix}/bin + +%global _prefix /opt/rx +%global _libdir %{_prefix}/%{_lib} +%global _bindir %{_prefix}/bin +%global _datadir %{_prefix}/share +%global _mandir %{_prefix}/share/man +%global _includedir %{_prefix}/include + + +Name: rx-libwebp +Version: 1.4.0 +Release: 1%{?dist} +URL: http://webmproject.org/ +Summary: Library and tools for the WebP graphics format +# Additional IPR is licensed as well. See PATENTS file for details +License: BSD +Source0: http://downloads.webmproject.org/releases/webp/%{realname}-%{version}.tar.gz +Source1: libwebp_jni_example.java + +# Fix build with freeglut +Patch0: libwebp-freeglut.patch +# Fix cmake module install location +Patch2: fix-cmake-files-location.patch +# Kill rpath +#Patch3: libwebp-rpath.patch + +#BuildRequires: rx-libjpeg-turbo-devel +BuildRequires: libjpeg-devel +BuildRequires: libpng-devel +BuildRequires: giflib-devel +BuildRequires: libtiff-devel +BuildRequires: java-devel +BuildRequires: jpackage-utils +BuildRequires: swig +BuildRequires: ninja-build +BuildRequires: freeglut-devel +BuildRequires: cmake +BuildRequires: clang + +#Requires: rx-libjpeg-turbo + +%description +WebP is an image format that does lossy compression of digital +photographic images. WebP consists of a codec based on VP8, and a +container based on RIFF. Webmasters, web developers and browser +developers can use WebP to compress, archive and distribute digital +images more efficiently. + + +%package tools +Summary: The WebP command line tools + +%description tools +WebP is an image format that does lossy compression of digital +photographic images. WebP consists of a codec based on VP8, and a +container based on RIFF. Webmasters, web developers and browser +developers can use WebP to compress, archive and distribute digital +images more efficiently. + + +%package devel +Summary: Development files for libwebp, a library for the WebP format +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: pkgconfig(libsharpyuv) = %{version}-%{release} +Provides: pkgconfig(libwebp) = %{version}-%{release} +Provides: pkgconfig(libwebpdecoder) = %{version}-%{release} +Provides: pkgconfig(libwebpdemux) = %{version}-%{release} +Provides: pkgconfig(libwebpmux) = %{version}-%{release} + + +%description devel +WebP is an image format that does lossy compression of digital +photographic images. WebP consists of a codec based on VP8, and a +container based on RIFF. Webmasters, web developers and browser +developers can use WebP to compress, archive and distribute digital +images more efficiently. + + +%package java +Summary: Java bindings for libwebp, a library for the WebP format +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: java-headless +Requires: jpackage-utils + +%description java +Java bindings for libwebp. + + +%prep +%autosetup -p1 -n %{realname}-%{version} + + +%build +%ifarch aarch64 +export CFLAGS="%{optflags} -frename-registers" +%endif +export LDFLAGS="%{build_ldflags} -L%{_libdir} -Wl,-rpath=%{_libdir}" + +export CC=clang +export CXX=clang++ + +# Native build +%cmake -DCMAKE_CONFIG_PREFIX=%{_libdir} +%cmake_build + +# swig generated Java bindings +cp %{SOURCE1} . +cd swig +rm -rf libwebp.jar libwebp_java_wrap.c +mkdir -p java/com/google/webp +swig -ignoremissing -I../src -java \ + -package com.google.webp \ + -outdir java/com/google/webp \ + -o libwebp_java_wrap.c libwebp.swig + +clang %{__global_ldflags} %(echo %{optflags} | sed 's/-fcf-protection//')-shared -fPIC \ + -I/usr/lib/jvm/java/include \ + -I/usr/lib/jvm/java/include/linux \ + -I../src \ + -L.. \ + -L../src/.libs -lwebp libwebp_java_wrap.c \ + -o libwebp_jni.so + +cd java +javac com/google/webp/libwebp.java +jar cvf ../libwebp.jar com/google/webp/*.class + + +%install +%cmake_install + +find "%{buildroot}/%{_libdir}" -type f -name "*.la" -delete + +# swig generated Java bindings +mkdir -p %{buildroot}/%{_libdir}/%{realname}-java +cp swig/*.jar swig/*.so %{buildroot}/%{_libdir}/%{realname}-java/ + +#mkdir -p %{buildroot}%{_root_libdir}/ +#mv %{buildroot}%{_libdir}/pkgconfig %{buildroot}%{_root_libdir}/ + +mkdir -p %{buildroot}%{_root_bindir} + +for b in cwebp dwebp gif2webp img2webp webpinfo webpmux +do + ln -sf %{_bindir}/$b %{buildroot}%{_root_bindir}/${b}-1.3 +done + + +%ldconfig_scriptlets + + +%files tools +%{_bindir}/cwebp +%{_bindir}/dwebp +%{_bindir}/gif2webp +%{_bindir}/img2webp +%{_bindir}/webpinfo +%{_bindir}/webpmux +%{_root_bindir}/*-1.3 +%{_mandir}/man*/* + +%files -n %{name} +%doc README.md PATENTS NEWS AUTHORS +%license COPYING +%{_libdir}/%{realname}.so.7* +%{_libdir}/%{realname}decoder.so.3* +%{_libdir}/%{realname}demux.so.2* +%{_libdir}/%{realname}mux.so.3* +%{_libdir}/libsharpyuv.so.0* + +%files devel +%{_libdir}/%{realname}*.so +%{_libdir}/libsharpyuv.so +%{_includedir}/* +%{_libdir}/pkgconfig/*.pc +%{_libdir}/cmake/WebP/ + + +%files java +%doc libwebp_jni_example.java +%{_libdir}/%{realname}-java/ + + +%changelog +* Tue May 7 2024 Raven - 1.4.0-1 +- Update to 1.4.0 + +* Wed Dec 20 2023 Raven - 1.3.2-1 +- Update to 1.3.2 + +* Fri Apr 16 2021 Mohan Boddu - 1.2.0-2 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Mon Feb 01 2021 Sandro Mani - 1.2.0-1 +- Update to 1.2.0 + +* Tue Jan 26 2021 Fedora Release Engineering - 1.1.0-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Jul 28 2020 Fedora Release Engineering - 1.1.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Sat Jul 11 2020 Jiri Vanek - 1.1.0-4 +- Rebuilt for JDK-11, see https://fedoraproject.org/wiki/Changes/Java11 + +* Mon May 18 2020 Sandro Mani - 1.1.0-3 +- Don't manually and incorrectly install vwebp, Makefile already does it correctly (#1836640) + +* Wed Jan 29 2020 Fedora Release Engineering - 1.1.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Tue Jan 07 2020 Sandro Mani - 1.1.0-1 +- Update to 1.1.0 + +* Tue Sep 17 2019 Gwyn Ciesla - 1.0.3-3 +- Rebuilt for new freeglut + +* Thu Jul 25 2019 Fedora Release Engineering - 1.0.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Mon Jul 15 2019 Sandro Mani - 1.0.3-1 +- Update to 1.0.3 + +* Fri Feb 01 2019 Fedora Release Engineering - 1.0.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Tue Jan 22 2019 Sandro Mani - 1.0.2-1 +- Update to 1.0.2 + +* Fri Jul 13 2018 Fedora Release Engineering - 1.0.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Apr 26 2018 Sandro Mani - 1.0.0-1 +- Update to 1.0.0 + +* Tue Feb 27 2018 Sandro Mani - 0.6.1-8 +- Fix LDFLAGS not passed when building libwebp_jni.so (#1548718) + +* Mon Feb 26 2018 Sandro Mani - 0.6.1-7 +- More big-endian fixes + +* Fri Feb 16 2018 Sandro Mani - 0.6.1-6 +- Backport another big-endian fix + +* Fri Feb 16 2018 Sandro Mani - 0.6.1-5 +- Backport upstream big-endian fix + +* Tue Feb 13 2018 Sandro Mani - 0.6.1-4 +- Rebuild (giflib) + +* Wed Feb 07 2018 Fedora Release Engineering - 0.6.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Feb 03 2018 Igor Gnatenko - 0.6.1-2 +- Switch to %%ldconfig_scriptlets + +* Thu Nov 30 2017 Sandro Mani - 0.6.1-1 +- Update to 0.6.1 + +* Thu Aug 03 2017 Fedora Release Engineering - 0.6.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.6.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 0.6.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Feb 01 2017 Sandro Mani - 0.6.0-1 +- Update to 0.6.0 + +* Thu Dec 22 2016 Sandro Mani - 0.5.2-1 +- Update to 0.5.2 + +* Sat Oct 29 2016 Sandro Mani - 0.5.1-2 +- Backport e2affacc35f1df6cc3b1a9fa0ceff5ce2d0cce83 (CVE-2016-9085, rhbz#1389338) + +* Fri Aug 12 2016 Sandro Mani - 0.5.1-1 +- upstream release 0.5.1 + +* Thu Feb 04 2016 Fedora Release Engineering - 0.5.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Mon Dec 28 2015 Sandro Mani - 0.5.0-1 +- upstream release 0.5.0 + +* Fri Oct 30 2015 Sandro Mani - 0.4.4-1 +- upstream release 0.4.4 + +* Wed Jun 17 2015 Fedora Release Engineering - 0.4.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Mar 27 2015 Sandro Mani - 0.4.3-2 +- Add BuildRequires: freeglut-devel to build vwebp + +* Thu Mar 12 2015 Sandro Mani - 0.4.3-1 +- upstream release 0.4.3 + +* Fri Oct 17 2014 Sandro Mani - 0.4.2-1 +- upstream release 0.4.2 + +* Sun Aug 17 2014 Fedora Release Engineering - 0.4.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Aug 13 2014 Peter Robinson 0.4.1-2 +- Use frename-registers cflag to fix FTBFS on aarch64 + +* Tue Aug 05 2014 Sandro Mani - 0.4.1-1 +- upstream release 0.4.1 + +* Sat Jun 07 2014 Fedora Release Engineering - 0.4.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Apr 08 2014 Jaromir Capik - 0.4.0-3 +- Fixing endian checks (#962091) +- Fixing FTPBS caused by rpath presence + +* Fri Mar 28 2014 Michael Simacek - 0.4.0-2 +- Use Requires: java-headless rebuild (#1067528) + +* Thu Jan 02 2014 Sandro Mani - 0.4.0-1 +- upstream release 0.4.0 + +* Wed Oct 02 2013 Sandro Mani - 0.3.1-2 +- enable webpdemux + +* Sun Aug 04 2013 Sandro Mani - 0.3.1-1 +- upstream release 0.3.1 + +* Sat Aug 03 2013 Fedora Release Engineering - 0.3.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Mon May 13 2013 Rahul Sundaram - 0.3.0-1 +- upstream release 0.3.0 +- enable gif2webp +- add build requires on giflib-devel and libtiff-devel +- use make_install and hardened macros +- list binaries explicitly + +* Thu Feb 14 2013 Fedora Release Engineering - 0.2.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Fri Jan 18 2013 Adam Tkac - 0.2.1-2 +- rebuild due to "jpeg8-ABI" feature drop + +* Thu Dec 27 2012 Rahul Sundaram - 0.2.1-1 +- new upstream release 0.2.1 + +* Fri Dec 21 2012 Adam Tkac - 0.1.3-3 +- rebuild against new libjpeg + +* Thu Jul 19 2012 Fedora Release Engineering - 0.1.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Feb 02 2012 Rahul Sundaram - 0.1.3-1 +- Several spec improvements by Scott Tsai + +* Wed May 25 2011 Rahul Sundaram - 0.1.2-1 +- Initial spec. Based on openSUSE one diff --git a/base/rx/rx-libwebp/libwebp_jni_example.java b/base/rx/rx-libwebp/libwebp_jni_example.java new file mode 100644 index 0000000..d28d9b8 --- /dev/null +++ b/base/rx/rx-libwebp/libwebp_jni_example.java @@ -0,0 +1,27 @@ +import com.google.webp.libwebp; + +import java.lang.reflect.Method; + +public class libwebp_jni_example { + static { + try { + System.load("/usr/lib64/libwebp-java/libwebp_jni.so"); + } catch (UnsatisfiedLinkError e) { + System.load("/usr/lib/libwebp-java/libwebp_jni.so"); + } + } + + /** + * usage: java -cp libwebp.jar:. libwebp_jni_example + */ + public static void main(String argv[]) { + final int version = libwebp.WebPGetDecoderVersion(); + System.out.println("libwebp version: " + Integer.toHexString(version)); + + System.out.println("libwebp methods:"); + final Method[] libwebpMethods = libwebp.class.getDeclaredMethods(); + for (int i = 0; i < libwebpMethods.length; i++) { + System.out.println(libwebpMethods[i]); + } + } +} diff --git a/base/rx/rx-poppler/poppler.spec b/base/rx/rx-poppler/poppler.spec index 8e1b86e..703b8bd 100644 --- a/base/rx/rx-poppler/poppler.spec +++ b/base/rx/rx-poppler/poppler.spec @@ -19,7 +19,7 @@ Summary: PDF rendering library Name: rx-poppler Version: 23.02.0 -Release: 2%{?dist} +Release: 5%{?dist} License: (GPLv2 or GPLv3) and GPLv2+ and LGPLv2+ and MIT URL: http://poppler.freedesktop.org/ Source0: http://poppler.freedesktop.org/poppler-%{version}.tar.xz @@ -32,7 +32,7 @@ Patch3: poppler-21.01.0-glib-introspection.patch BuildRequires: make BuildRequires: cmake -BuildRequires: gcc-c++ +BuildRequires: gcc-toolset-14-gcc-c++ BuildRequires: gettext-devel BuildRequires: pkgconfig(cairo) BuildRequires: pkgconfig(cairo-ft) @@ -171,6 +171,7 @@ Requires: %{name}-devel%{?_isa} = %{version}-%{release} chmod -x poppler/CairoFontEngine.cc %build +%enable_devtoolset14 export PATH=%{_bindir}${PATH:+:${PATH}} export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} -L%{_prefix}/glib2/%{_lib} -Wl,-rpath=%{_prefix}/glib2/%{_lib} ${LDFLAGS}" export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} @@ -288,6 +289,9 @@ test "$(pkg-config --modversion poppler-qt6)" = "%{version}" %{_includedir}/poppler/cpp %changelog +* Tue Oct 29 2024 Raven - 23.02.0-4 +- rebuilt with new rx-freetype + * Fri Feb 3 2023 Marek Kasik - 23.02.0-1 - Update to 23.02.0 - Resolves: #2123190