diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3702777..dfe9c39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,18 @@ Tools team member. Once you have obtained review, you may merge. Note that for the `c10s` branch, the pipeline is currently not green. On the `c8s` and `c9s` branches, it is green and should remain so. +# Building locally + +It is recommend to use the `mock` tool to build in a clean build +environment. Use `centpkg srpm` to create the source RPM. + +If you want to build locally, be aware that the patch-git tooling +picks up additional patches in the `$HOME/rpmbuild` directory +hierarchy. You can avoid this by building directly out of dist-git, +without installing the source RPM using `rpm -i` first: + + rpmbuild -bb -D "_sourcedir $PWD" glibc.spec + # Trailers in Git commit messages The patch management tooling (in `patch-git.lua`) assumes that all Git @@ -89,6 +101,9 @@ The following `Key: value` pairs are recognized. - Fix memory leak in fdopen (bug 31840) - libio: Test for fdopen memory leak without SEEK_END ``` + If there are no `- ` lines, the changelog entry is automatically + word-wrapped. If `- ` lines are used, the line breaks found in the + trailer are preserved in the RPM `%changelog` section. * `RPM-Changelog-Stop`. Boolean. When `yes`, generation of changelog entries stops at this commit. Requires `Parent`. diff --git a/glibc-RHEL-111115-1.patch b/glibc-RHEL-111115-1.patch new file mode 100644 index 0000000..32e48af --- /dev/null +++ b/glibc-RHEL-111115-1.patch @@ -0,0 +1,81 @@ +commit 84373ef7b72c9c8ab61ce1fdfd798777715a1a52 +Author: Frédéric Bérat +Date: Fri Mar 7 14:42:26 2025 +0100 + + Prepare inet_ntop to be fortified + + Rename inet_ntop to __inet_ntop and create the inet_ntop weak alias + based on it in order to prepare for disabling fortification when + available. + + Reviewed-by: Florian Weimer + +diff --git a/include/arpa/inet.h b/include/arpa/inet.h +index df9472ba3bc52689..d1ea13bb19d4a497 100644 +--- a/include/arpa/inet.h ++++ b/include/arpa/inet.h +@@ -5,7 +5,9 @@ + extern int __inet_aton_exact (const char *__cp, struct in_addr *__inp); + libc_hidden_proto (__inet_aton_exact) + +-libc_hidden_proto (inet_ntop) ++extern __typeof (inet_ntop) __inet_ntop; ++libc_hidden_proto (__inet_ntop) ++ + libc_hidden_proto (inet_pton) + extern __typeof (inet_pton) __inet_pton; + libc_hidden_proto (__inet_pton) +diff --git a/nss/getnameinfo.c b/nss/getnameinfo.c +index 889c0a35d29b676e..36c5401a7645d7d2 100644 +--- a/nss/getnameinfo.c ++++ b/nss/getnameinfo.c +@@ -338,7 +338,7 @@ gni_host_inet_numeric (struct scratch_buffer *tmpbuf, + if (sa->sa_family == AF_INET6) + { + const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa; +- if (inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL) ++ if (__inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL) + return EAI_OVERFLOW; + + uint32_t scopeid = sin6p->sin6_scope_id; +@@ -365,7 +365,7 @@ gni_host_inet_numeric (struct scratch_buffer *tmpbuf, + else + { + const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa; +- if (inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL) ++ if (__inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL) + return EAI_OVERFLOW; + } + return 0; +diff --git a/resolv/inet_ntop.c b/resolv/inet_ntop.c +index c4d38c0f951013e5..acf5f3cb885e2e47 100644 +--- a/resolv/inet_ntop.c ++++ b/resolv/inet_ntop.c +@@ -42,7 +42,7 @@ static const char *inet_ntop4 (const u_char *src, char *dst, socklen_t size); + static const char *inet_ntop6 (const u_char *src, char *dst, socklen_t size); + + /* char * +- * inet_ntop(af, src, dst, size) ++ * __inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). +@@ -50,7 +50,7 @@ static const char *inet_ntop6 (const u_char *src, char *dst, socklen_t size); + * Paul Vixie, 1996. + */ + const char * +-inet_ntop (int af, const void *src, char *dst, socklen_t size) ++__inet_ntop (int af, const void *src, char *dst, socklen_t size) + { + switch (af) { + case AF_INET: +@@ -63,7 +63,8 @@ inet_ntop (int af, const void *src, char *dst, socklen_t size) + } + /* NOTREACHED */ + } +-libc_hidden_def (inet_ntop) ++libc_hidden_def (__inet_ntop) ++weak_alias (__inet_ntop, inet_ntop) + + /* const char * + * inet_ntop4(src, dst, size) diff --git a/glibc-RHEL-111115-2.patch b/glibc-RHEL-111115-2.patch new file mode 100644 index 0000000..ca6bade --- /dev/null +++ b/glibc-RHEL-111115-2.patch @@ -0,0 +1,26 @@ +commit 3cdb99d8bb9d0008b2b297080e61d6c10dd66cc8 +Author: Frédéric Bérat +Date: Tue Mar 11 10:40:11 2025 +0100 + + Add missing guards in include/arpa/inet.h + + Add the missing guards in the header, similarly to other headers at the + same level + + Reviewed-by: Florian Weimer + +diff --git a/include/arpa/inet.h b/include/arpa/inet.h +index d1ea13bb19d4a497..d9e55a3c7f2db9f2 100644 +--- a/include/arpa/inet.h ++++ b/include/arpa/inet.h +@@ -1,3 +1,5 @@ ++#ifndef _ARPA_INET_H ++/* Note: _ARPA_INET_H is defined by inet/arpa/inet.h below. */ + #include + + #ifndef _ISOMAC +@@ -17,3 +19,4 @@ libc_hidden_proto (inet_netof) + extern __typeof (inet_network) __inet_network; + libc_hidden_proto (__inet_network) + #endif ++#endif diff --git a/glibc-RHEL-111115-3.patch b/glibc-RHEL-111115-3.patch new file mode 100644 index 0000000..c230044 --- /dev/null +++ b/glibc-RHEL-111115-3.patch @@ -0,0 +1,318 @@ +Partial backport (without ABI changes, using libc_nonshared.a instead) +of: + +commit 090dfa40a5e46f7c0e4d6e8369bcbbd51267625f +Author: Frédéric Bérat +Date: Fri Mar 7 18:16:30 2025 +0100 + + Add _FORTIFY_SOURCE support for inet_ntop + + - Create the __inet_ntop_chk routine that verifies that the builtin size + of the destination buffer is at least as big as the size given by the + user. + - Redirect calls from inet_ntop to __inet_ntop_chk or __inet_ntop_warn + - Update the abilist (Dropped) for this new routine + - Update the manual to mention the new fortification + + Reviewed-by: Florian Weimer + +Conflicts: + debug/inet_ntop_chk.c + (attribute_hidden added, use inet_ntop instead of __inet_ntop) + debug/Makefile + (Routine added to static-only-routines) + debug/Versions + (Dropped) + inet/bits/inet-fortified.h + (removed attribute_overloadable and clang specific handling) + sysdeps/mach/hurd/i386/libc.abilist + sysdeps/mach/hurd/x86_64/libc.abilist + sysdeps/unix/sysv/linux/aarch64/libc.abilist + sysdeps/unix/sysv/linux/alpha/libc.abilist + sysdeps/unix/sysv/linux/arc/libc.abilist + sysdeps/unix/sysv/linux/arm/be/libc.abilist + sysdeps/unix/sysv/linux/arm/le/libc.abilist + sysdeps/unix/sysv/linux/csky/libc.abilist + sysdeps/unix/sysv/linux/hppa/libc.abilist + sysdeps/unix/sysv/linux/i386/libc.abilist + sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist + sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist + sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist + sysdeps/unix/sysv/linux/microblaze/be/libc.abilist + sysdeps/unix/sysv/linux/microblaze/le/libc.abilist + sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist + sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist + sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist + sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist + sysdeps/unix/sysv/linux/or1k/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist + sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist + sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist + sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist + sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist + sysdeps/unix/sysv/linux/sh/be/libc.abilist + sysdeps/unix/sysv/linux/sh/le/libc.abilist + sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist + sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist + sysdeps/unix/sysv/linux/x86_64/64/libc.abilist + sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist + (Dropped) + +diff --git a/debug/Makefile b/debug/Makefile +index 76c311d2845df9c1..db9a400711a2ce91 100644 +--- a/debug/Makefile ++++ b/debug/Makefile +@@ -119,7 +119,10 @@ routines = \ + wmemset_chk \ + wprintf_chk \ + # routines +-static-only-routines := stack_chk_fail_local ++static-only-routines := \ ++ inet_ntop_chk \ ++ stack_chk_fail_local \ ++ # static-only-routines + + # Don't add stack_chk_fail_local.o to libc.a since __stack_chk_fail_local + # is an alias of __stack_chk_fail in stack_chk_fail.o. +diff --git a/debug/inet_ntop_chk.c b/debug/inet_ntop_chk.c +new file mode 100644 +index 0000000000000000..8a3994dd3fc9bfe4 +--- /dev/null ++++ b/debug/inet_ntop_chk.c +@@ -0,0 +1,31 @@ ++/* Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++attribute_hidden ++const char * ++__inet_ntop_chk (int af, const void *src, char *dst, ++ socklen_t size, size_t dst_size) ++{ ++ if (size > dst_size) ++ __chk_fail (); ++ ++ return inet_ntop (af, src, dst, size); ++} ++libc_hidden_def (__inet_ntop_chk) +diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c +index ae738ff10a305575..f9b97531749d363b 100644 +--- a/debug/tst-fortify.c ++++ b/debug/tst-fortify.c +@@ -23,6 +23,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -1832,6 +1833,26 @@ do_test (void) + # endif + #endif + ++ struct in6_addr addr6 = {}; ++ struct in_addr addr = {}; ++ char addrstr6[INET6_ADDRSTRLEN]; ++ char addrstr[INET_ADDRSTRLEN]; ++ ++ if (inet_ntop (AF_INET6, &addr6, addrstr6, sizeof (addrstr6)) == NULL) ++ FAIL (); ++ if (inet_ntop (AF_INET, &addr, addrstr, sizeof (addrstr)) == NULL) ++ FAIL (); ++ ++#if __USE_FORTIFY_LEVEL >= 1 ++ CHK_FAIL_START ++ inet_ntop (AF_INET6, &addr6, buf, INET6_ADDRSTRLEN); ++ CHK_FAIL_END ++ ++ CHK_FAIL_START ++ inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN); ++ CHK_FAIL_END ++#endif ++ + return ret; + } + +diff --git a/include/arpa/inet.h b/include/arpa/inet.h +index d9e55a3c7f2db9f2..a02892f48a27454e 100644 +--- a/include/arpa/inet.h ++++ b/include/arpa/inet.h +@@ -3,12 +3,18 @@ + #include + + #ifndef _ISOMAC ++/* Declare functions with security checks. ++ This needs to be included unconditionally as these definition are needed even ++ when fortification is disabled in inet/arpa/inet.h. */ ++#include ++ + /* Variant of inet_aton which rejects trailing garbage. */ + extern int __inet_aton_exact (const char *__cp, struct in_addr *__inp); + libc_hidden_proto (__inet_aton_exact) + + extern __typeof (inet_ntop) __inet_ntop; + libc_hidden_proto (__inet_ntop) ++libc_hidden_proto (__inet_ntop_chk) + + libc_hidden_proto (inet_pton) + extern __typeof (inet_pton) __inet_pton; +diff --git a/include/bits/inet-fortified-decl.h b/include/bits/inet-fortified-decl.h +new file mode 100644 +index 0000000000000000..e6ad4d4663c61a0d +--- /dev/null ++++ b/include/bits/inet-fortified-decl.h +@@ -0,0 +1 @@ ++#include +diff --git a/include/bits/inet-fortified.h b/include/bits/inet-fortified.h +new file mode 100644 +index 0000000000000000..abba7c57014c2a23 +--- /dev/null ++++ b/include/bits/inet-fortified.h +@@ -0,0 +1 @@ ++#include +diff --git a/inet/Makefile b/inet/Makefile +index cb97b45f0f9d223f..01208235c4f800bb 100644 +--- a/inet/Makefile ++++ b/inet/Makefile +@@ -25,6 +25,8 @@ include ../Makeconfig + headers := \ + $(wildcard arpa/*.h protocols/*.h) \ + bits/in.h \ ++ bits/inet-fortified-decl.h \ ++ bits/inet-fortified.h \ + ifaddrs.h \ + netinet/ether.h \ + netinet/icmp6.h \ +diff --git a/inet/arpa/inet.h b/inet/arpa/inet.h +index c005340a8004dcaf..2b8eac147280306e 100644 +--- a/inet/arpa/inet.h ++++ b/inet/arpa/inet.h +@@ -101,6 +101,11 @@ extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp, + char *__buf) __THROW; + #endif + ++#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function ++/* Include functions with security checks. */ ++# include ++#endif ++ + __END_DECLS + + #endif /* arpa/inet.h */ +diff --git a/inet/bits/inet-fortified-decl.h b/inet/bits/inet-fortified-decl.h +new file mode 100644 +index 0000000000000000..23e3cf4b2238c81a +--- /dev/null ++++ b/inet/bits/inet-fortified-decl.h +@@ -0,0 +1,35 @@ ++/* Declarations of checking macros for inet functions. ++ Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _BITS_INET_FORTIFIED_DEC_H ++#define _BITS_INET_FORTIFIED_DEC_H 1 ++ ++#ifndef _ARPA_INET_H ++# error "Never include directly; use instead." ++#endif ++ ++extern const char *__inet_ntop_chk (int, const void *, char *, socklen_t, size_t); ++ ++extern const char *__REDIRECT_FORTIFY_NTH (__inet_ntop_alias, ++ (int, const void *, char *, socklen_t), inet_ntop); ++extern const char *__REDIRECT_NTH (__inet_ntop_chk_warn, ++ (int, const void *, char *, socklen_t, size_t), __inet_ntop_chk) ++ __warnattr ("inet_ntop called with bigger length than " ++ "size of destination buffer"); ++ ++#endif /* bits/inet-fortified-decl.h. */ +diff --git a/inet/bits/inet-fortified.h b/inet/bits/inet-fortified.h +new file mode 100644 +index 0000000000000000..af26f36ef6ae0533 +--- /dev/null ++++ b/inet/bits/inet-fortified.h +@@ -0,0 +1,37 @@ ++/* Checking macros for inet functions. ++ Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _BITS_INET_FORTIFIED_H ++#define _BITS_INET_FORTIFIED_H 1 ++ ++#ifndef _ARPA_INET_H ++# error "Never include directly; use instead." ++#endif ++ ++#include ++ ++__fortify_function const char * ++__NTH (inet_ntop (int __af, const void * __restrict __src, ++ char *__restrict __dst, socklen_t __dst_size)) ++{ ++ return __glibc_fortify (inet_ntop, __dst_size, sizeof (char), ++ __glibc_objsize (__dst), ++ __af, __src, __dst, __dst_size); ++}; ++ ++#endif /* bits/inet-fortified.h. */ +diff --git a/manual/maint.texi b/manual/maint.texi +index 04faa222e2bd2fc4..ce6a556c68925b49 100644 +--- a/manual/maint.texi ++++ b/manual/maint.texi +@@ -303,6 +303,8 @@ The following functions and macros are fortified in @theglibc{}: + + @item @code{getwd} + ++@item @code{inet_ntop} ++ + @item @code{longjmp} + + @item @code{mbsnrtowcs} diff --git a/glibc-RHEL-111115-4.patch b/glibc-RHEL-111115-4.patch new file mode 100644 index 0000000..09a32a9 --- /dev/null +++ b/glibc-RHEL-111115-4.patch @@ -0,0 +1,471 @@ +commit a71db81ed1353edd00ca2901d2fefd98c53209d3 +Author: Aaron Merey +Date: Thu Mar 20 11:07:05 2025 -0400 + + Prepare inet_pton to be fortified + + Split inet_pton internals such as __inet_pton_length from the + inet_pton entry point. + + This allows the internals to be built with fortification while + leaving the inet_pton entry point unchanged. + + Co-authored-by: Frédéric Bérat + Reviewed-by: Florian Weimer + +diff --git a/resolv/Makefile b/resolv/Makefile +index abff7fc0074e893b..05fb04edf1082690 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -38,6 +38,7 @@ routines := \ + inet_addr \ + inet_ntop \ + inet_pton \ ++ inet_pton_length \ + ns_makecanon \ + ns_name_compress \ + ns_name_length_uncompressed \ +@@ -73,6 +74,11 @@ routines := \ + resolv_context \ + # routines + ++# Exclude fortified routines from being built with _FORTIFY_SOURCE ++routines_no_fortify += \ ++ inet_pton \ ++ # routines_no_fortify ++ + tests = tst-aton tst-leaks tst-inet_ntop + tests-container = tst-leaks2 + +diff --git a/resolv/inet_pton.c b/resolv/inet_pton.c +index 96ca3e4f9e1790a0..70f6fa177582f2cc 100644 +--- a/resolv/inet_pton.c ++++ b/resolv/inet_pton.c +@@ -33,33 +33,7 @@ + */ + + #include +-#include +-#include +-#include +-#include + #include +-#include +-#include +-#include +- +-static int inet_pton4 (const char *src, const char *src_end, u_char *dst); +-static int inet_pton6 (const char *src, const char *src_end, u_char *dst); +- +-int +-__inet_pton_length (int af, const char *src, size_t srclen, void *dst) +-{ +- switch (af) +- { +- case AF_INET: +- return inet_pton4 (src, src + srclen, dst); +- case AF_INET6: +- return inet_pton6 (src, src + srclen, dst); +- default: +- __set_errno (EAFNOSUPPORT); +- return -1; +- } +-} +-libc_hidden_def (__inet_pton_length) + + /* Like __inet_pton_length, but use strlen (SRC) as the length of + SRC. */ +@@ -71,164 +45,3 @@ __inet_pton (int af, const char *src, void *dst) + libc_hidden_def (__inet_pton) + weak_alias (__inet_pton, inet_pton) + libc_hidden_weak (inet_pton) +- +-/* Like inet_aton but without all the hexadecimal, octal and shorthand +- (and trailing garbage is not ignored). Return 1 if SRC is a valid +- dotted quad, else 0. This function does not touch DST unless it's +- returning 1. +- Author: Paul Vixie, 1996. */ +-static int +-inet_pton4 (const char *src, const char *end, unsigned char *dst) +-{ +- int saw_digit, octets, ch; +- unsigned char tmp[NS_INADDRSZ], *tp; +- +- saw_digit = 0; +- octets = 0; +- *(tp = tmp) = 0; +- while (src < end) +- { +- ch = *src++; +- if (ch >= '0' && ch <= '9') +- { +- unsigned int new = *tp * 10 + (ch - '0'); +- +- if (saw_digit && *tp == 0) +- return 0; +- if (new > 255) +- return 0; +- *tp = new; +- if (! saw_digit) +- { +- if (++octets > 4) +- return 0; +- saw_digit = 1; +- } +- } +- else if (ch == '.' && saw_digit) +- { +- if (octets == 4) +- return 0; +- *++tp = 0; +- saw_digit = 0; +- } +- else +- return 0; +- } +- if (octets < 4) +- return 0; +- memcpy (dst, tmp, NS_INADDRSZ); +- return 1; +-} +- +-/* Return the value of CH as a hexadecimal digit, or -1 if it is a +- different type of character. */ +-static int +-hex_digit_value (char ch) +-{ +- if ('0' <= ch && ch <= '9') +- return ch - '0'; +- if ('a' <= ch && ch <= 'f') +- return ch - 'a' + 10; +- if ('A' <= ch && ch <= 'F') +- return ch - 'A' + 10; +- return -1; +-} +- +-/* Convert presentation-level IPv6 address to network order binary +- form. Return 1 if SRC is a valid [RFC1884 2.2] address, else 0. +- This function does not touch DST unless it's returning 1. +- Author: Paul Vixie, 1996. Inspired by Mark Andrews. */ +-static int +-inet_pton6 (const char *src, const char *src_endp, unsigned char *dst) +-{ +- unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; +- const char *curtok; +- int ch; +- size_t xdigits_seen; /* Number of hex digits since colon. */ +- unsigned int val; +- +- tp = memset (tmp, '\0', NS_IN6ADDRSZ); +- endp = tp + NS_IN6ADDRSZ; +- colonp = NULL; +- +- /* Leading :: requires some special handling. */ +- if (src == src_endp) +- return 0; +- if (*src == ':') +- { +- ++src; +- if (src == src_endp || *src != ':') +- return 0; +- } +- +- curtok = src; +- xdigits_seen = 0; +- val = 0; +- while (src < src_endp) +- { +- ch = *src++; +- int digit = hex_digit_value (ch); +- if (digit >= 0) +- { +- if (xdigits_seen == 4) +- return 0; +- val <<= 4; +- val |= digit; +- if (val > 0xffff) +- return 0; +- ++xdigits_seen; +- continue; +- } +- if (ch == ':') +- { +- curtok = src; +- if (xdigits_seen == 0) +- { +- if (colonp) +- return 0; +- colonp = tp; +- continue; +- } +- else if (src == src_endp) +- return 0; +- if (tp + NS_INT16SZ > endp) +- return 0; +- *tp++ = (unsigned char) (val >> 8) & 0xff; +- *tp++ = (unsigned char) val & 0xff; +- xdigits_seen = 0; +- val = 0; +- continue; +- } +- if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) +- && inet_pton4 (curtok, src_endp, tp) > 0) +- { +- tp += NS_INADDRSZ; +- xdigits_seen = 0; +- break; /* '\0' was seen by inet_pton4. */ +- } +- return 0; +- } +- if (xdigits_seen > 0) +- { +- if (tp + NS_INT16SZ > endp) +- return 0; +- *tp++ = (unsigned char) (val >> 8) & 0xff; +- *tp++ = (unsigned char) val & 0xff; +- } +- if (colonp != NULL) +- { +- /* Replace :: with zeros. */ +- if (tp == endp) +- /* :: would expand to a zero-width field. */ +- return 0; +- size_t n = tp - colonp; +- memmove (endp - n, colonp, n); +- memset (colonp, 0, endp - n - colonp); +- tp = endp; +- } +- if (tp != endp) +- return 0; +- memcpy (dst, tmp, NS_IN6ADDRSZ); +- return 1; +-} +diff --git a/resolv/inet_pton_length.c b/resolv/inet_pton_length.c +new file mode 100644 +index 0000000000000000..c3614074a47140c1 +--- /dev/null ++++ b/resolv/inet_pton_length.c +@@ -0,0 +1,223 @@ ++/* Copyright (C) 1996-2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++/* ++ * Copyright (c) 1996,1999 by Internet Software Consortium. ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ++ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE ++ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL ++ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR ++ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ++ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ++ * SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int inet_pton4 (const char *src, const char *src_end, u_char *dst); ++static int inet_pton6 (const char *src, const char *src_end, u_char *dst); ++ ++int ++__inet_pton_length (int af, const char *src, size_t srclen, void *dst) ++{ ++ switch (af) ++ { ++ case AF_INET: ++ return inet_pton4 (src, src + srclen, dst); ++ case AF_INET6: ++ return inet_pton6 (src, src + srclen, dst); ++ default: ++ __set_errno (EAFNOSUPPORT); ++ return -1; ++ } ++} ++libc_hidden_def (__inet_pton_length) ++ ++/* Like inet_aton but without all the hexadecimal, octal and shorthand ++ (and trailing garbage is not ignored). Return 1 if SRC is a valid ++ dotted quad, else 0. This function does not touch DST unless it's ++ returning 1. ++ Author: Paul Vixie, 1996. */ ++static int ++inet_pton4 (const char *src, const char *end, unsigned char *dst) ++{ ++ int saw_digit, octets, ch; ++ unsigned char tmp[NS_INADDRSZ], *tp; ++ ++ saw_digit = 0; ++ octets = 0; ++ *(tp = tmp) = 0; ++ while (src < end) ++ { ++ ch = *src++; ++ if (ch >= '0' && ch <= '9') ++ { ++ unsigned int new = *tp * 10 + (ch - '0'); ++ ++ if (saw_digit && *tp == 0) ++ return 0; ++ if (new > 255) ++ return 0; ++ *tp = new; ++ if (! saw_digit) ++ { ++ if (++octets > 4) ++ return 0; ++ saw_digit = 1; ++ } ++ } ++ else if (ch == '.' && saw_digit) ++ { ++ if (octets == 4) ++ return 0; ++ *++tp = 0; ++ saw_digit = 0; ++ } ++ else ++ return 0; ++ } ++ if (octets < 4) ++ return 0; ++ memcpy (dst, tmp, NS_INADDRSZ); ++ return 1; ++} ++ ++/* Return the value of CH as a hexadecimal digit, or -1 if it is a ++ different type of character. */ ++static int ++hex_digit_value (char ch) ++{ ++ if ('0' <= ch && ch <= '9') ++ return ch - '0'; ++ if ('a' <= ch && ch <= 'f') ++ return ch - 'a' + 10; ++ if ('A' <= ch && ch <= 'F') ++ return ch - 'A' + 10; ++ return -1; ++} ++ ++/* Convert presentation-level IPv6 address to network order binary ++ form. Return 1 if SRC is a valid [RFC1884 2.2] address, else 0. ++ This function does not touch DST unless it's returning 1. ++ Author: Paul Vixie, 1996. Inspired by Mark Andrews. */ ++static int ++inet_pton6 (const char *src, const char *src_endp, unsigned char *dst) ++{ ++ unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; ++ const char *curtok; ++ int ch; ++ size_t xdigits_seen; /* Number of hex digits since colon. */ ++ unsigned int val; ++ ++ tp = memset (tmp, '\0', NS_IN6ADDRSZ); ++ endp = tp + NS_IN6ADDRSZ; ++ colonp = NULL; ++ ++ /* Leading :: requires some special handling. */ ++ if (src == src_endp) ++ return 0; ++ if (*src == ':') ++ { ++ ++src; ++ if (src == src_endp || *src != ':') ++ return 0; ++ } ++ ++ curtok = src; ++ xdigits_seen = 0; ++ val = 0; ++ while (src < src_endp) ++ { ++ ch = *src++; ++ int digit = hex_digit_value (ch); ++ if (digit >= 0) ++ { ++ if (xdigits_seen == 4) ++ return 0; ++ val <<= 4; ++ val |= digit; ++ if (val > 0xffff) ++ return 0; ++ ++xdigits_seen; ++ continue; ++ } ++ if (ch == ':') ++ { ++ curtok = src; ++ if (xdigits_seen == 0) ++ { ++ if (colonp) ++ return 0; ++ colonp = tp; ++ continue; ++ } ++ else if (src == src_endp) ++ return 0; ++ if (tp + NS_INT16SZ > endp) ++ return 0; ++ *tp++ = (unsigned char) (val >> 8) & 0xff; ++ *tp++ = (unsigned char) val & 0xff; ++ xdigits_seen = 0; ++ val = 0; ++ continue; ++ } ++ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) ++ && inet_pton4 (curtok, src_endp, tp) > 0) ++ { ++ tp += NS_INADDRSZ; ++ xdigits_seen = 0; ++ break; /* '\0' was seen by inet_pton4. */ ++ } ++ return 0; ++ } ++ if (xdigits_seen > 0) ++ { ++ if (tp + NS_INT16SZ > endp) ++ return 0; ++ *tp++ = (unsigned char) (val >> 8) & 0xff; ++ *tp++ = (unsigned char) val & 0xff; ++ } ++ if (colonp != NULL) ++ { ++ /* Replace :: with zeros. */ ++ if (tp == endp) ++ /* :: would expand to a zero-width field. */ ++ return 0; ++ size_t n = tp - colonp; ++ memmove (endp - n, colonp, n); ++ memset (colonp, 0, endp - n - colonp); ++ tp = endp; ++ } ++ if (tp != endp) ++ return 0; ++ memcpy (dst, tmp, NS_IN6ADDRSZ); ++ return 1; ++} diff --git a/glibc-RHEL-111115-5.patch b/glibc-RHEL-111115-5.patch new file mode 100644 index 0000000..585e570 --- /dev/null +++ b/glibc-RHEL-111115-5.patch @@ -0,0 +1,216 @@ +Partial backport (without ABI changes, using libc_nonshared.a instead) +of: + +commit e3a6e85d67f1a48dec3e2557a83d6ce1544a58cb +Author: Aaron Merey +Date: Thu Mar 20 13:13:33 2025 -0400 + + Add _FORTIFY_SOURCE support for inet_pton + + Add function __inet_pton_chk which calls __chk_fail when the size of + argument dst is too small. inet_pton is redirected to __inet_pton_chk + or __inet_pton_warn when _FORTIFY_SOURCE is > 0. + + Also add tests to debug/tst-fortify.c, update the abilist (Dropped) with + __inet_pton_chk and mention inet_pton fortification in maint.texi. + + Co-authored-by: Frédéric Bérat + Reviewed-by: Florian Weimer + +Conflicts: + debug/inet_pton_chk.c + (attribute_hidden added, use inet_pton instead of __inet_pton) + debug/Makefile + (routine added to static-routines-only) + debug/Versions + (Dropped) + inet/bits/inet-fortified.h + (removed attribute_overloadable and clang specific handling) + sysdeps/mach/hurd/i386/libc.abilist + sysdeps/mach/hurd/x86_64/libc.abilist + sysdeps/unix/sysv/linux/aarch64/libc.abilist + sysdeps/unix/sysv/linux/alpha/libc.abilist + sysdeps/unix/sysv/linux/arc/libc.abilist + sysdeps/unix/sysv/linux/arm/be/libc.abilist + sysdeps/unix/sysv/linux/arm/le/libc.abilist + sysdeps/unix/sysv/linux/csky/libc.abilist + sysdeps/unix/sysv/linux/hppa/libc.abilist + sysdeps/unix/sysv/linux/i386/libc.abilist + sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist + sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist + sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist + sysdeps/unix/sysv/linux/microblaze/be/libc.abilist + sysdeps/unix/sysv/linux/microblaze/le/libc.abilist + sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist + sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist + sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist + sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist + sysdeps/unix/sysv/linux/or1k/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist + sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist + sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist + sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist + sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist + sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist + sysdeps/unix/sysv/linux/sh/be/libc.abilist + sysdeps/unix/sysv/linux/sh/le/libc.abilist + sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist + sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist + sysdeps/unix/sysv/linux/x86_64/64/libc.abilist + sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist + (Dropped) + +diff --git a/debug/Makefile b/debug/Makefile +index a2d236e28eda8b47..5a7295678cb1eed1 100644 +--- a/debug/Makefile ++++ b/debug/Makefile +@@ -122,6 +122,7 @@ routines = \ + static-only-routines := \ + inet_ntop_chk \ ++ inet_pton_chk \ + stack_chk_fail_local \ + # static-only-routines + + # Don't add stack_chk_fail_local.o to libc.a since __stack_chk_fail_local +diff --git a/debug/inet_pton_chk.c b/debug/inet_pton_chk.c +new file mode 100644 +index 0000000000000000..feca3bff5a4ded2f +--- /dev/null ++++ b/debug/inet_pton_chk.c +@@ -0,0 +1,31 @@ ++/* Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++attribute_hidden ++int ++__inet_pton_chk (int af, const char *src, void *dst, size_t dst_size) ++{ ++ if ((af == AF_INET && dst_size < 4) ++ || (af == AF_INET6 && dst_size < 16)) ++ __chk_fail (); ++ ++ return inet_pton (af, src, dst); ++} ++libc_hidden_def (__inet_pton_chk) +diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c +index f9b97531749d363b..0e44594a181fdba5 100644 +--- a/debug/tst-fortify.c ++++ b/debug/tst-fortify.c +@@ -1853,6 +1853,30 @@ do_test (void) + CHK_FAIL_END + #endif + ++ const char *ipv4str = "127.0.0.1"; ++ const char *ipv6str = "::1"; ++ ++ if (inet_pton (AF_INET, ipv4str, (void *) &addr) != 1) ++ FAIL (); ++ if (inet_pton (AF_INET6, ipv6str, (void *) &addr6) != 1) ++ FAIL (); ++ ++#if __USE_FORTIFY_LEVEL >= 1 ++ char smallbuf[2]; ++ ++ CHK_FAIL_START ++ inet_pton (AF_INET, ipv4str, (void *) smallbuf); ++ CHK_FAIL_END ++ ++ CHK_FAIL_START ++ inet_pton (AF_INET6, ipv6str, (void *) smallbuf); ++ CHK_FAIL_END ++ ++ CHK_FAIL_START ++ inet_pton (AF_INET6, ipv6str, (void *) &addr); ++ CHK_FAIL_END ++#endif ++ + return ret; + } + +diff --git a/include/arpa/inet.h b/include/arpa/inet.h +index a02892f48a27454e..3db8f1a96fdbd6fd 100644 +--- a/include/arpa/inet.h ++++ b/include/arpa/inet.h +@@ -19,6 +19,8 @@ libc_hidden_proto (__inet_ntop_chk) + libc_hidden_proto (inet_pton) + extern __typeof (inet_pton) __inet_pton; + libc_hidden_proto (__inet_pton) ++libc_hidden_proto (__inet_pton_chk) ++ + extern __typeof (inet_makeaddr) __inet_makeaddr; + libc_hidden_proto (__inet_makeaddr) + libc_hidden_proto (inet_netof) +diff --git a/inet/bits/inet-fortified-decl.h b/inet/bits/inet-fortified-decl.h +index 23e3cf4b2238c81a..748a119f149d790b 100644 +--- a/inet/bits/inet-fortified-decl.h ++++ b/inet/bits/inet-fortified-decl.h +@@ -32,4 +32,11 @@ extern const char *__REDIRECT_NTH (__inet_ntop_chk_warn, + __warnattr ("inet_ntop called with bigger length than " + "size of destination buffer"); + ++extern int __inet_pton_chk (int, const char *, void *, size_t); ++ ++extern int __REDIRECT_FORTIFY_NTH (__inet_pton_alias, ++ (int, const char *, void *), inet_pton); ++extern int __REDIRECT_NTH (__inet_pton_chk_warn, ++ (int, const char *, void *, size_t), __inet_pton_chk) ++ __warnattr ("inet_pton called with a destination buffer size too small"); + #endif /* bits/inet-fortified-decl.h. */ +diff --git a/inet/bits/inet-fortified.h b/inet/bits/inet-fortified.h +index af26f36ef6ae0533..8420a4b7fb41086f 100644 +--- a/inet/bits/inet-fortified.h ++++ b/inet/bits/inet-fortified.h +@@ -34,4 +34,21 @@ __NTH (inet_ntop (int __af, const void * __restrict __src, + __af, __src, __dst, __dst_size); + }; + ++__fortify_function int ++__NTH (inet_pton (int __af, const char *__restrict __src, ++ void * __restrict __dst)) ++{ ++ size_t sz = 0; ++ if (__af == AF_INET) ++ sz = sizeof (struct in_addr); ++ else if (__af == AF_INET6) ++ sz = sizeof (struct in6_addr); ++ else ++ return __inet_pton_alias (__af, __src, __dst); ++ ++ return __glibc_fortify (inet_pton, sz, sizeof (char), ++ __glibc_objsize (__dst), ++ __af, __src, __dst); ++}; ++ + #endif /* bits/inet-fortified.h. */ +diff --git a/manual/maint.texi b/manual/maint.texi +index ce6a556c68925b49..b6ee5b6e3bdf768c 100644 +--- a/manual/maint.texi ++++ b/manual/maint.texi +@@ -305,6 +305,8 @@ The following functions and macros are fortified in @theglibc{}: + + @item @code{inet_ntop} + ++@item @code{inet_pton} ++ + @item @code{longjmp} + + @item @code{mbsnrtowcs} diff --git a/glibc-RHEL-111115-6.patch b/glibc-RHEL-111115-6.patch new file mode 100644 index 0000000..d8232fc --- /dev/null +++ b/glibc-RHEL-111115-6.patch @@ -0,0 +1,34 @@ +commit 87afbd7a1ad9c1dd116921817fa97198171045db +Author: Sam James +Date: Mon Jul 28 21:55:30 2025 +0100 + + inet-fortified: fix namespace violation (bug 33227) + + We need to use __sz, not sz, as we do elsewhere. + + Reviewed-by: Florian Weimer + +diff --git a/inet/bits/inet-fortified.h b/inet/bits/inet-fortified.h +index 8420a4b7fb41086f..5d16b1f871c49e6f 100644 +--- a/inet/bits/inet-fortified.h ++++ b/inet/bits/inet-fortified.h +@@ -38,15 +38,15 @@ __fortify_function int + __NTH (inet_pton (int __af, const char *__restrict __src, + void * __restrict __dst)) + { +- size_t sz = 0; ++ size_t __sz = 0; + if (__af == AF_INET) +- sz = sizeof (struct in_addr); ++ __sz = sizeof (struct in_addr); + else if (__af == AF_INET6) +- sz = sizeof (struct in6_addr); ++ __sz = sizeof (struct in6_addr); + else + return __inet_pton_alias (__af, __src, __dst); + +- return __glibc_fortify (inet_pton, sz, sizeof (char), ++ return __glibc_fortify (inet_pton, __sz, sizeof (char), + __glibc_objsize (__dst), + __af, __src, __dst); + }; diff --git a/glibc.spec b/glibc.spec index 46ea643..7413fa0 100644 --- a/glibc.spec +++ b/glibc.spec @@ -2348,7 +2348,7 @@ update_gconv_modules_cache () %endif %changelog -* Tue Sep 23 2025 Eduard Abdullin - 2.39-66.alma.1 +* Wed Sep 24 2025 Eduard Abdullin - 2.39-67.alma.1 - Overwrite target for x86_64_v2 - Update patch-git.lua to handle AlmaLinux branches correctly diff --git a/patch-git.lua b/patch-git.lua index 5a46d53..312806f 100644 --- a/patch-git.lua +++ b/patch-git.lua @@ -283,6 +283,137 @@ do end end +-- Testing helper: assert_eq(a, b) asserts if not a == b. +-- A third argument can be provided with a context string. +local assert_eq +do + -- quote(v) returns a string that evaluates to v, mostly in Lua syntax. + local quote + do + local quote_table = { + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t', + ['\0'] = '\000', + ['"'] = '\\"', + ['\\'] = '\\\\', + } + local function quote_table_update(i) + local ch = string.char(i) + if quote_table[ch] == nil then + quote_table[ch] = string.format('\\x%02x', i) + end + end + for i=0,31 do + quote_table_update(i) + end + for i=127,255 do + quote_table_update(i) + end + local function quote1(v, seen) + if v == nil then + return 'nil' + elseif v == true then + return 'true' + elseif v == false then + return 'false' + elseif type(v) == 'number' then + return string.format('%q', v) + elseif type(v) == 'string' then + return '"' .. string.gsub(v, '.', quote_table) .. '"' + elseif type(v) == 'table' then + -- Prevent infinite recursion. + local idx = seen[v] + if idx then + return '&' .. idx + end + local seen_count = seen[1] + 1 + seen[1] = seen_count + seen[v] = seen_count + + local count = 0 + for _, _ in pairs(v) do + count = count + 1 + end + local result = {} + if count == #v then + -- Regular table. + for i=1,count do + result[i] = quote1(v[i], seen) + end + else + -- Not a regular table. + for key, value in pairs(v) do + result[#result + 1] = + '[' .. quote1(key, seen) .. ']=' + .. quote1(value, seen) + end + end + return '{' .. table.concat(result, ', ') .. '}' + else + return '#<' .. type(v) .. ':' .. quote(tostring(v)) .. '>' + end + end + function quote(v) + return quote1(v, {0}) + end + end + assert(quote('') == '""') + assert(quote('\n') == '"\\n"') + assert(quote('{}') == '"{}"') + assert(quote({}) == '{}') + assert(quote({1, 2, 3}) == '{1, 2, 3}') + assert(quote({a=1}) == '{["a"]=1}') + + local deep_eq + do + local function deep_eq1(a, b, seen) + if a == b then + return true + elseif type(a) == 'table' and type(b) == 'table' then + assert(not seen[a]) + assert(not seen[b]) + seen[a] = true + seen[b] = true + local acount = 0 + for ak, av in pairs(a) do + if not deep_eq1(av, b[ak], seen) then + return false + end + acount = acount + 1 + end + local bcount = 0 + for bk, bv in pairs(b) do + bcount = bcount + 1 + end + return acount == bcount + else + return false + end + end + function deep_eq(a, b) + if a == b then + return true + elseif type(a) == 'table' and type(b) == 'table' then + return deep_eq1(a, b, {}) + end + end + end + + function assert_eq(a, b, ctx) + if deep_eq(a, b) then + return + end + local prefix + if ctx then + prefix = ctx .. ': ' + else + prefix = '' + end + assert(a == b, prefix .. quote(a) .. ' ~= ' .. quote(b)) + end +end + -- Sort the list lexicographically, in place, treating sequences of -- digits as a single positive decimal number. local function version_sort(list) @@ -676,7 +807,7 @@ function patchgit.patches(options) -- Perform version sort on the table and emit the patch names in -- that order. - function emit_patchlist(patchlist) + local function emit_patchlist(patchlist) -- Within one commit, patches are sorted lexicographically. -- Remove the '.patch' suffix, so that it does not interfere -- with sorting ('patch2b.patch' sorting before 'patch2.patch'). @@ -768,6 +899,22 @@ do assert(err == 'ticket reference without trailing number: bug') end +-- Append a line to the changelog table. It is prefixed with '- '. +-- Long lines are wrapped at word boundaries and indented. +local function append_to_rpm_changelog(line, result) + if #result == 0 then + result[1] = '-' + end + for word in string.gmatch(line, '%S+') do + if #result[#result] + #word > 76 then + result[#result + 1] = ' ' .. word + else + result[#result] = result[#result] .. ' ' .. word + end + end +end + + -- Extract the trailers from the passed commit message. Returns a -- single table where keys are derived from the trailer tags. -- On error, returns nil and an error message. @@ -813,7 +960,9 @@ do assert(not n and err == 'Patch-Git-Version does not contain a number') end - -- Validator for RPM-Changelog. + -- Validator for RPM-Changelog. It returns the changelog entries + -- as a table of lines. The lines start with '- ' or ' ', unless + -- they are empty. local function parse_rpm_changelog(tag, s) assert(string.sub(s, #s) == '\n') @@ -823,11 +972,11 @@ do end local lines = {} - for line in string.gmatch(s, '([^\n]+\n)') do + for line in string.gmatch(s, '([^\n]+)\n') do lines[#lines + 1] = line end -- Remove leading whitespace from the first line. - lines[1] = assert(string.match(lines[1], '^[ \t]*(.*\n)$')) + lines[1] = assert(string.match(lines[1], '^[ \t]*(.*)$')) if #lines == 1 then -- Nothing to do else @@ -849,22 +998,31 @@ do local result = {} if not string.match(lines[1], '^- ') then - -- This is not an itemized changelog entry. The loop below - -- appends to this entry. - result[1] = '' - end - for _, line in ipairs(lines) do - local tagged = string.match(line, '^-[ \t]+(.*)%s*\n') - if tagged then - -- New entry. - result[#result + 1] = tagged - else - local stripped = assert(string.match(line, '%s*(.*)%s*\n')) - local old = result[#result] - if old ~= '' then - old = old .. ' ' - end - result[#result] = old .. stripped + -- This is not an itemized changelog entry. Concatenate all + -- lines with word-wrapping. + for _, line in ipairs(lines) do + append_to_rpm_changelog(line, result) + end + else + local dashed = false + for lineno, line in ipairs(lines) do + if lineno > 1 and string.match(line, '^- ') then + dashed = true + end + end + + for lineno, line in ipairs(lines) do + if not string.match(line, '^- ') then + if string.match(line, '^%s*$') then + line = '' + elseif not dashed then + -- If there are no '- ' lines in the continuation + -- part, all lines need to be indented to line up + -- with with the '- ' from the first line. + line = ' ' .. line + end + end + result[lineno] = line end end return result @@ -874,35 +1032,33 @@ do local function prc(s) return parse_rpm_changelog('RPM-Changelog', s) end - local t, err -- Single-line changelog entry, not itemized. - t = assert(prc('Switch to patch-git\n')) - assert(#t == 1) - assert(t[1] == 'Switch to patch-git') + assert_eq(assert(prc('Switch to patch-git\n')), + {'- Switch to patch-git'}) -- Single-line changelog entry, itemized. - t = assert(prc(' - Switch to patch-git\n')) - assert(#t == 1) - assert(t[1] == 'Switch to patch-git') + assert_eq(assert(prc(' - Switch to patch-git\n')), + {'- Switch to patch-git'}) -- Multi-line changelog entry, not itemized. - t = assert(prc(' Switch to\n patch-git\n')) - assert(#t == 1) - assert(t[1] == 'Switch to patch-git') + assert_eq(assert(prc(' Switch to\n patch-git\n')), + {'- Switch to patch-git'}) -- Multi-line changelog entry, one item. - t = assert(prc('- Switch to\n patch-git\n')) - assert(#t == 1) - assert(t[1] == 'Switch to patch-git') + assert_eq(assert(prc('- Switch to\n patch-git\n')), + {'- Switch to', ' patch-git'}) -- Multi-line changelog entry, two items. - t = assert(prc( - '- Switch to\n patch-git\n' - .. ' - Additional patch-git\n fixes\n')) - assert(#t == 2, t[1]) - assert(t[1] == 'Switch to patch-git') - assert(t[2] == 'Additional patch-git fixes') + assert_eq(assert(prc([[- Switch to + patch-git + - Additional patch-git + fixes +]])), + {'- Switch to', + ' patch-git', + '- Additional patch-git', + ' fixes'}) end local function parse_rpm_release(tag, s) @@ -1338,7 +1494,9 @@ local function rpm_changelog_default(message, trailer) and tickets and #tickets > 0) then subject = subject .. ' (' .. table.concat(tickets, ', ') .. ')' end - return {subject} + local result = {} + append_to_rpm_changelog(subject, result) + return result end -- Tests for rpm_changelog_default. do @@ -1350,15 +1508,13 @@ do Resolves: RHEL-108475 ]]) - assert(#t == 1) - assert(t[1] == 'Remove memory leak in fdopen (RHEL-108475)', t[1]) + assert_eq(t, {'- Remove memory leak in fdopen (RHEL-108475)'}) t = rcd([[Remove memory leak in fdopen (RHEL-108475) Resolves: RHEL-108475 ]]) - assert(#t == 1) - assert(t[1] == 'Remove memory leak in fdopen (RHEL-108475)', t[1]) + assert_eq(t, {'- Remove memory leak in fdopen (RHEL-108475)'}) t = rcd([[Remove memory leak in fdopen (RHEL-108475) @@ -1374,9 +1530,73 @@ RPM-Changelog: - Remove memory leak in fdopen (bug 31840) - libio: Test for fdopen memory leak without SEEK_END ]]) - assert(#t == 2) - assert(t[1] == 'Remove memory leak in fdopen (bug 31840)') - assert(t[2] == 'libio: Test for fdopen memory leak without SEEK_END') + assert_eq(t, + {'- Remove memory leak in fdopen (bug 31840)', + '- libio: Test for fdopen memory leak without SEEK_END'}) + + t = rcd([[Do not wrap the cat + +Resolves: RHEL-108475 +RPM-Changelog: + - Do not wrap the cat! + /\_/\ + ( o.o ) + > ^ < + - Thank you. +]]) + assert_eq(t, + {'- Do not wrap the cat!', + [[ /\_/\]], + [[ ( o.o )]], + [[ > ^ <]], + '- Thank you.'}) + + t = rcd([[Do not wrap the cat + +Resolves: RHEL-108475 +RPM-Changelog: + - Do not wrap the cat! + /\_/\ + ( o.o ) + > ^ < + - Thank you. +]]) + assert_eq(t, + {'- Do not wrap the cat!', + [[ /\_/\]], + [[ ( o.o )]], + [[ > ^ <]], + '- Thank you.'}) + + t = rcd([[Do not wrap the cat + +Resolves: RHEL-108475 +RPM-Changelog: + - Do not wrap the cat! + /\_/\ + ( o.o ) + > ^ < +]]) + assert_eq(t, + {[[- Do not wrap the cat!]], + [[ /\_/\]], + [[ ( o.o )]], + [[ > ^ <]]}) + + -- Variant that has the dash on the RPM-Changelog line. + t = rcd([[Do not wrap the cat + +Resolves: RHEL-108475 +RPM-Changelog: - Do not wrap the cat! + /\_/\ + ( o.o ) + > ^ < +]]) + assert_eq(t, + {[[- Do not wrap the cat!]], + [[ /\_/\]], + [[ ( o.o )]], + [[ > ^ <]]}) end @@ -1655,9 +1875,8 @@ local function process_commits(changelog, changelog_after_commit) assert_commit(commit, target_cl and #target_cl > 0, 'first commit skips changelog and has an entry') - for i=1,#cl_entries do - target_cl[#target_cl + 1] = '- ' .. cl_entries[i] - end + table.move(cl_entries, 1, #cl_entries, + #target_cl + 1, target_cl) end end end