mpfr: Update to include the latest four bug patches

Message ID 20230511123943.3450454-2-adolf.belka@ipfire.org
State Accepted
Commit b2e7b0a28b882e9a55b9d7e4bb3eb9fbf3cbfde5
Headers
Series mpfr: Update to include the latest four bug patches |

Commit Message

Adolf Belka May 11, 2023, 12:39 p.m. UTC
  - The base version has not changed but patches to fix 4 bugs have been released.
- Update to rootfile not required.
- Bug fix changelog
	1    A test of the thousands separator in tsprintf.c is based on the output from
              the GNU C Library up to 2.36, which is incorrect. The output has changed in
              2.37 (partly fixed), so that tsprintf fails with glibc 2.37. The
              tsprintf-thousands patch modifies the test to conform to POSIX and also
              avoid the buggy case in 2.36 and below. However, this new test, which was
              expected to succeed, triggers a serious bug in 2.37
              (bug 30068 / CVE-2023-25139). We did not modify the test again since this
              bug affects MPFR's mpfr_sprintf function, with a possible buffer overflow
              in particular cases. This bug has been fixed in the 2.37 branch. In short,
              this patch is useful (and needed) for a fixed glibc 2.37 and some other
              libraries, depending on the current locales.
	     Corresponding changesets in the 4.2 branch: 4f03d40b5, 78ff7526d, e66bb7121.
	2    The mpfr_ui_pow_ui function has infinite loop in case of overflow. This can
              affect mpfr_log10, which uses this function (this is how this bug was
              found). This bug is fixed by the ui_pow_ui-overflow patch (with testcases).
	     Corresponding changeset in the 4.2 branch: 0216f40ed.
	3    The tfprintf and tprintf tests may fail in locales where decimal_point has
              several bytes, such as ps_AF. This is fixed by the multibyte-decimal_point
              patch, which makes the tests aware of the length of decimal_point.
	     Corresponding changeset in the 4.2 branch: 0383bea85.
	4    In particular cases that are very hard to round, mpfr_rec_sqrt may yield a
              stack overflow due to many small allocations in the stack, based on alloca().
              This is due to the fact that the working precision is increased each step
              (Ziv loop) by 32 or 64 bits only, until the approximate result can be
              rounded (thus we have an arithmetic progression here, while a geometric
              progression is used for the other functions), and that at each iteration,
              the previous allocations in the stack cannot be freed. Individual
              allocations in the stack are limited to 16384 bytes, so that the issue can
              occur only when there are many iterations in working precisions that are
              not too large, which is possible with an arithmetic progression. This bug
              is fixed by the rec_sqrt-zivloop patch, which changes the Ziv loop to use
              the standard MPFR_ZIV_* macros; the patch also provides a testcase obtained
              by a function that constructs a hard-to-round case involving large enough
              precisions (this function is commonly used in the MPFR testsuite, but not
              with so large precisions). This bug was originally reported by Fredrik
              Johansson.
	     Corresponding changeset in the 4.2 branch: 934dd8842.

Signed-off-by: Adolf Belka <adolf.belka@ipfire.org>
---
 lfs/mpfr                                      |   8 +-
 ...mpfr-4.2.0-cumulative-patches-1-to-4.patch | 521 ++++++++++++++++++
 2 files changed, 525 insertions(+), 4 deletions(-)
 create mode 100644 src/patches/mpfr-4.2.0-cumulative-patches-1-to-4.patch
  

Patch

diff --git a/lfs/mpfr b/lfs/mpfr
index 818566390..c799086d9 100644
--- a/lfs/mpfr
+++ b/lfs/mpfr
@@ -70,10 +70,10 @@  $(subst %,%_BLAKE2,$(objects)) :
 $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
 	@$(PREBUILD)
 	@rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar axf $(DIR_DL)/$(DL_FILE)
-# No patches yet for version 4.2.0
-#	cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/mpfr-4.1.1-cumulative-patches-1-to-1.patch
-	cd $(DIR_APP) && $(CONFIGURE_ARGS) ./configure --prefix=/usr \
-		--enable-thread-safe
+	cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/mpfr-4.2.0-cumulative-patches-1-to-4.patch
+	cd $(DIR_APP) && $(CONFIGURE_ARGS) ./configure \
+					--prefix=/usr \
+					--enable-thread-safe
 	cd $(DIR_APP) && make $(MAKETUNING)
 	cd $(DIR_APP) && make install
 	@rm -rf $(DIR_APP)
diff --git a/src/patches/mpfr-4.2.0-cumulative-patches-1-to-4.patch b/src/patches/mpfr-4.2.0-cumulative-patches-1-to-4.patch
new file mode 100644
index 000000000..e12ed4708
--- /dev/null
+++ b/src/patches/mpfr-4.2.0-cumulative-patches-1-to-4.patch
@@ -0,0 +1,521 @@ 
+diff -Naurd mpfr-4.2.0-a/PATCHES mpfr-4.2.0-b/PATCHES
+--- mpfr-4.2.0-a/PATCHES	2023-04-17 21:17:39.748645280 +0000
++++ mpfr-4.2.0-b/PATCHES	2023-04-17 21:17:39.792645218 +0000
+@@ -0,0 +1 @@
++tsprintf-thousands
+diff -Naurd mpfr-4.2.0-a/VERSION mpfr-4.2.0-b/VERSION
+--- mpfr-4.2.0-a/VERSION	2023-01-06 10:55:57.000000000 +0000
++++ mpfr-4.2.0-b/VERSION	2023-04-17 21:17:39.792645218 +0000
+@@ -1 +1 @@
+-4.2.0
++4.2.0-p1
+diff -Naurd mpfr-4.2.0-a/src/mpfr.h mpfr-4.2.0-b/src/mpfr.h
+--- mpfr-4.2.0-a/src/mpfr.h	2023-01-06 10:55:57.000000000 +0000
++++ mpfr-4.2.0-b/src/mpfr.h	2023-04-17 21:17:39.788645224 +0000
+@@ -27,7 +27,7 @@
+ #define MPFR_VERSION_MAJOR 4
+ #define MPFR_VERSION_MINOR 2
+ #define MPFR_VERSION_PATCHLEVEL 0
+-#define MPFR_VERSION_STRING "4.2.0"
++#define MPFR_VERSION_STRING "4.2.0-p1"
+ 
+ /* User macros:
+    MPFR_USE_FILE:        Define it to make MPFR define functions dealing
+diff -Naurd mpfr-4.2.0-a/src/version.c mpfr-4.2.0-b/src/version.c
+--- mpfr-4.2.0-a/src/version.c	2023-01-06 10:55:57.000000000 +0000
++++ mpfr-4.2.0-b/src/version.c	2023-04-17 21:17:39.792645218 +0000
+@@ -25,5 +25,5 @@
+ const char *
+ mpfr_get_version (void)
+ {
+-  return "4.2.0";
++  return "4.2.0-p1";
+ }
+diff -Naurd mpfr-4.2.0-a/tests/tsprintf.c mpfr-4.2.0-b/tests/tsprintf.c
+--- mpfr-4.2.0-a/tests/tsprintf.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/tsprintf.c	2023-04-17 21:17:39.784645229 +0000
+@@ -1715,7 +1715,25 @@
+   check_sprintf ("000000001,000", "%'013.4Rg", x);
+ 
+ #ifdef PRINTF_GROUPFLAG
+-  check_vsprintf ("+01,234,567  :", "%0+ -'13.10Pd:", (mpfr_prec_t) 1234567);
++  /* Do not test the thousands separator with a precision field larger
++     than the number of digits (thus needing leading zeros), such as
++     "%0+ -'13.10Pd:" (used up to MPFR 4.2.0), since the GNU libc is
++     buggy: https://sourceware.org/bugzilla/show_bug.cgi?id=23432
++     We don't know about the other implementations.
++     This new test works fine with glibc up to 2.36, but fails with 2.37
++     (as reported by Klaus Dittrich in the MPFR mailing-list); this is
++     actually a bug introduced in glibc 2.37, not in MPFR:
++       https://sourceware.org/bugzilla/show_bug.cgi?id=30068
++     Since this bug can yield a buffer overflow (CVE-2023-25139), possibly
++     affecting MPFR users, let us rather require a fix in glibc. This bug
++     has been fixed in the 2.37 branch:
++       https://sourceware.org/git/?p=glibc.git;a=commit;h=07b9521fc6
++     If we wanted to check that and avoid a failure of the test because of
++     a buggy C library (while MPFR would be consistent with the C library),
++     we could compare the MPFR output with both the correct output and the
++     output from the C library (possibly buggy). But to do that in a clean
++     way, this would require a change in the check_vsprintf() call. */
++  check_vsprintf ("+1,234,567   :", "%0+ -'13Pd:", (mpfr_prec_t) 1234567);
+ #endif
+ 
+   mpfr_clear (x);
+diff -Naurd mpfr-4.2.0-a/PATCHES mpfr-4.2.0-b/PATCHES
+--- mpfr-4.2.0-a/PATCHES	2023-04-17 21:18:00.464616127 +0000
++++ mpfr-4.2.0-b/PATCHES	2023-04-17 21:18:00.512616059 +0000
+@@ -0,0 +1 @@
++ui_pow_ui-overflow
+diff -Naurd mpfr-4.2.0-a/VERSION mpfr-4.2.0-b/VERSION
+--- mpfr-4.2.0-a/VERSION	2023-04-17 21:17:39.792645218 +0000
++++ mpfr-4.2.0-b/VERSION	2023-04-17 21:18:00.512616059 +0000
+@@ -1 +1 @@
+-4.2.0-p1
++4.2.0-p2
+diff -Naurd mpfr-4.2.0-a/src/mpfr.h mpfr-4.2.0-b/src/mpfr.h
+--- mpfr-4.2.0-a/src/mpfr.h	2023-04-17 21:17:39.788645224 +0000
++++ mpfr-4.2.0-b/src/mpfr.h	2023-04-17 21:18:00.508616065 +0000
+@@ -27,7 +27,7 @@
+ #define MPFR_VERSION_MAJOR 4
+ #define MPFR_VERSION_MINOR 2
+ #define MPFR_VERSION_PATCHLEVEL 0
+-#define MPFR_VERSION_STRING "4.2.0-p1"
++#define MPFR_VERSION_STRING "4.2.0-p2"
+ 
+ /* User macros:
+    MPFR_USE_FILE:        Define it to make MPFR define functions dealing
+diff -Naurd mpfr-4.2.0-a/src/ui_pow_ui.c mpfr-4.2.0-b/src/ui_pow_ui.c
+--- mpfr-4.2.0-a/src/ui_pow_ui.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/src/ui_pow_ui.c	2023-04-17 21:18:00.504616070 +0000
+@@ -23,7 +23,7 @@
+ #include "mpfr-impl.h"
+ 
+ int
+-mpfr_ui_pow_ui (mpfr_ptr x, unsigned long int y, unsigned long int n,
++mpfr_ui_pow_ui (mpfr_ptr x, unsigned long int k, unsigned long int n,
+                 mpfr_rnd_t rnd)
+ {
+   mpfr_exp_t err;
+@@ -35,22 +35,28 @@
+   MPFR_ZIV_DECL (loop);
+   MPFR_SAVE_EXPO_DECL (expo);
+ 
++  MPFR_LOG_FUNC
++    (("k=%lu n=%lu rnd=%d", k, n, rnd),
++     ("y[%Pu]=%.*Rg inexact=%d",
++      mpfr_get_prec (x), mpfr_log_prec, x, inexact));
++
+   if (MPFR_UNLIKELY (n <= 1))
+     {
+       if (n == 1)
+-        return mpfr_set_ui (x, y, rnd);     /* y^1 = y */
++        return mpfr_set_ui (x, k, rnd);     /* k^1 = k */
+       else
+-        return mpfr_set_ui (x, 1, rnd);     /* y^0 = 1 for any y */
++        return mpfr_set_ui (x, 1, rnd);     /* k^0 = 1 for any k */
+     }
+-  else if (MPFR_UNLIKELY (y <= 1))
++  else if (MPFR_UNLIKELY (k <= 1))
+     {
+-      if (y == 1)
++      if (k == 1)
+         return mpfr_set_ui (x, 1, rnd);     /* 1^n = 1 for any n > 0 */
+       else
+         return mpfr_set_ui (x, 0, rnd);     /* 0^n = 0 for any n > 0 */
+     }
+ 
+-  for (size_n = 0, m = n; m; size_n++, m >>= 1);
++  for (size_n = 0, m = n; m != 0; size_n++, m >>= 1)
++    ;
+ 
+   MPFR_SAVE_EXPO_MARK (expo);
+   prec = MPFR_PREC (x) + 3 + size_n;
+@@ -60,23 +66,55 @@
+   for (;;)
+     {
+       int i = size_n;
++      unsigned int inex_res;
+ 
+-      inexact = mpfr_set_ui (res, y, MPFR_RNDU);
++      inex_res = mpfr_set_ui (res, k, MPFR_RNDU);
+       err = 1;
+       /* now 2^(i-1) <= n < 2^i: i=1+floor(log2(n)) */
+       for (i -= 2; i >= 0; i--)
+         {
+-          inexact |= mpfr_sqr (res, res, MPFR_RNDU);
++          inex_res |= mpfr_sqr (res, res, MPFR_RNDU);
+           err++;
+           if (n & (1UL << i))
+-            inexact |= mpfr_mul_ui (res, res, y, MPFR_RNDU);
++            inex_res |= mpfr_mul_ui (res, res, k, MPFR_RNDU);
+         }
++
++      if (MPFR_UNLIKELY (MPFR_IS_INF (res)))
++        {
++          mpfr_t kf;
++          mpz_t z;
++          int size_k;
++          MPFR_BLOCK_DECL (flags);
++
++          /* Let's handle the overflow by calling mpfr_pow_z.
++             Alternatively, we could call mpfr_pow_ui; this would
++             need a bit shorter code below, but mpfr_pow_ui handles
++             the overflow by calling mpfr_pow_z, so that calling
++             mpfr_pow_z directly should be a bit more efficient. */
++
++          MPFR_ZIV_FREE (loop);
++          mpfr_clear (res);
++          for (size_k = 0, m = k; m != 0; size_k++, m >>= 1)
++            ;
++          mpfr_init2 (kf, size_k);
++          inexact = mpfr_set_ui (kf, k, MPFR_RNDN);
++          MPFR_ASSERTD (inexact == 0);
++          mpz_init (z);
++          mpz_set_ui (z, n);
++          MPFR_BLOCK (flags, inexact = mpfr_pow_z (x, kf, z, rnd););
++          mpz_clear (z);
++          mpfr_clear (kf);
++          MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, flags);
++          goto end;
++        }
++
+       /* since the loop is executed floor(log2(n)) times,
+          we have err = 1+floor(log2(n)).
+          Since prec >= MPFR_PREC(x) + 4 + floor(log2(n)), prec > err */
+       err = prec - err;
+ 
+-      if (MPFR_LIKELY (inexact == 0
++      MPFR_LOG_VAR (res);
++      if (MPFR_LIKELY (!inex_res
+                        || MPFR_CAN_ROUND (res, err, MPFR_PREC (x), rnd)))
+         break;
+ 
+@@ -90,6 +128,7 @@
+ 
+   mpfr_clear (res);
+ 
++ end:
+   MPFR_SAVE_EXPO_FREE (expo);
+   return mpfr_check_range (x, inexact, rnd);
+ }
+diff -Naurd mpfr-4.2.0-a/src/version.c mpfr-4.2.0-b/src/version.c
+--- mpfr-4.2.0-a/src/version.c	2023-04-17 21:17:39.792645218 +0000
++++ mpfr-4.2.0-b/src/version.c	2023-04-17 21:18:00.512616059 +0000
+@@ -25,5 +25,5 @@
+ const char *
+ mpfr_get_version (void)
+ {
+-  return "4.2.0-p1";
++  return "4.2.0-p2";
+ }
+diff -Naurd mpfr-4.2.0-a/tests/tlog10.c mpfr-4.2.0-b/tests/tlog10.c
+--- mpfr-4.2.0-a/tests/tlog10.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/tlog10.c	2023-04-17 21:18:00.504616070 +0000
+@@ -49,6 +49,60 @@
+ #define TEST_RANDOM_POS 8
+ #include "tgeneric.c"
+ 
++/* On 2023-02-13, one gets an infinite loop in mpfr_log10 on both
++   32-bit and 64-bit hosts when the precision is not large enough
++   (precision 12 and below). */
++static void
++bug20230213 (void)
++{
++  mpfr_exp_t old_emin, old_emax, e;
++  mpfr_t t, x, y0, y1, y2;
++  int prec;
++
++  old_emin = mpfr_get_emin ();
++  old_emax = mpfr_get_emax ();
++
++  set_emin (MPFR_EMIN_MIN);
++  set_emax (MPFR_EMAX_MAX);
++  e = mpfr_get_emax () - 1;
++
++  /* The precisions of t and y0 should be large enough to avoid
++     a hard-to-round case for the target precisions. */
++  mpfr_inits2 (64, t, y0, (mpfr_ptr) 0);
++  mpfr_set_exp_t (y0, e, MPFR_RNDN);
++  mpfr_log_ui (t, 10, MPFR_RNDN);
++  mpfr_div (y0, y0, t, MPFR_RNDN);
++  mpfr_log_ui (t, 2, MPFR_RNDN);
++  mpfr_mul (y0, y0, t, MPFR_RNDN);
++
++  for (prec = 16; prec >= MPFR_PREC_MIN; prec--)
++    {
++      mpfr_inits2 (prec, x, y1, y2, (mpfr_ptr) 0);
++      mpfr_set (y1, y0, MPFR_RNDN);
++
++      mpfr_set_ui_2exp (x, 1, e, MPFR_RNDN);
++      mpfr_log10 (y2, x, MPFR_RNDN);
++      MPFR_ASSERTN (MPFR_IS_PURE_FP (y2));
++      MPFR_ASSERTN (MPFR_IS_POS (y2));
++
++      if (! mpfr_equal_p (y1, y2))
++        {
++          printf ("Error in bug20230213.\n");
++          printf ("Expected ");
++          mpfr_dump (y1);
++          printf ("Got      ");
++          mpfr_dump (y2);
++          exit (1);
++        }
++      mpfr_clears (x, y1, y2, (mpfr_ptr) 0);
++    }
++
++  mpfr_clears (t, y0, (mpfr_ptr) 0);
++
++  set_emin (old_emin);
++  set_emax (old_emax);
++}
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -112,6 +166,8 @@
+   mpfr_clear (x);
+   mpfr_clear (y);
+ 
++  bug20230213 ();
++
+   data_check ("data/log10", mpfr_log10, "mpfr_log10");
+ 
+   tests_end_mpfr ();
+diff -Naurd mpfr-4.2.0-a/tests/tui_pow.c mpfr-4.2.0-b/tests/tui_pow.c
+--- mpfr-4.2.0-a/tests/tui_pow.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/tui_pow.c	2023-04-17 21:18:00.504616070 +0000
+@@ -142,6 +142,37 @@
+   mpfr_clear (t);
+ }
+ 
++static void
++huge (void)
++{
++  mpfr_exp_t old_emin, old_emax;
++  mpfr_t x;
++
++  old_emin = mpfr_get_emin ();
++  old_emax = mpfr_get_emax ();
++
++  set_emin (MPFR_EMIN_MIN);
++  set_emax (MPFR_EMAX_MAX);
++
++  mpfr_init2 (x, 8);
++
++  /* The purpose of this test is more to check that mpfr_ui_pow_ui
++     terminates (without taking much memory) rather than checking
++     the value of x. On 2023-02-13, the +Inf case was not handled
++     in the Ziv iteration, yielding an infinite loop, affecting
++     mpfr_log10 in particular. See
++       commit 90de094f0d9c309daca707aa227470d810866616
++  */
++  mpfr_ui_pow_ui (x, 5, ULONG_MAX, MPFR_RNDN);
++  if (MPFR_EMAX_MAX <= ULONG_MAX)  /* true with default _MPFR_EXP_FORMAT */
++    MPFR_ASSERTN (MPFR_IS_INF (x));
++
++  mpfr_clear (x);
++
++  set_emin (old_emin);
++  set_emax (old_emax);
++}
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -180,6 +211,7 @@
+     }
+ 
+   test1 ();
++  huge ();
+ 
+   {
+   mpfr_t z, t;
+diff -Naurd mpfr-4.2.0-a/PATCHES mpfr-4.2.0-b/PATCHES
+--- mpfr-4.2.0-a/PATCHES	2023-04-17 21:18:26.860579184 +0000
++++ mpfr-4.2.0-b/PATCHES	2023-04-17 21:18:26.904579122 +0000
+@@ -0,0 +1 @@
++multibyte-decimal_point
+diff -Naurd mpfr-4.2.0-a/VERSION mpfr-4.2.0-b/VERSION
+--- mpfr-4.2.0-a/VERSION	2023-04-17 21:18:00.512616059 +0000
++++ mpfr-4.2.0-b/VERSION	2023-04-17 21:18:26.904579122 +0000
+@@ -1 +1 @@
+-4.2.0-p2
++4.2.0-p3
+diff -Naurd mpfr-4.2.0-a/src/mpfr.h mpfr-4.2.0-b/src/mpfr.h
+--- mpfr-4.2.0-a/src/mpfr.h	2023-04-17 21:18:00.508616065 +0000
++++ mpfr-4.2.0-b/src/mpfr.h	2023-04-17 21:18:26.900579128 +0000
+@@ -27,7 +27,7 @@
+ #define MPFR_VERSION_MAJOR 4
+ #define MPFR_VERSION_MINOR 2
+ #define MPFR_VERSION_PATCHLEVEL 0
+-#define MPFR_VERSION_STRING "4.2.0-p2"
++#define MPFR_VERSION_STRING "4.2.0-p3"
+ 
+ /* User macros:
+    MPFR_USE_FILE:        Define it to make MPFR define functions dealing
+diff -Naurd mpfr-4.2.0-a/src/version.c mpfr-4.2.0-b/src/version.c
+--- mpfr-4.2.0-a/src/version.c	2023-04-17 21:18:00.512616059 +0000
++++ mpfr-4.2.0-b/src/version.c	2023-04-17 21:18:26.904579122 +0000
+@@ -25,5 +25,5 @@
+ const char *
+ mpfr_get_version (void)
+ {
+-  return "4.2.0-p2";
++  return "4.2.0-p3";
+ }
+diff -Naurd mpfr-4.2.0-a/tests/tfprintf.c mpfr-4.2.0-b/tests/tfprintf.c
+--- mpfr-4.2.0-a/tests/tfprintf.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/tfprintf.c	2023-04-17 21:18:26.896579133 +0000
+@@ -61,6 +61,12 @@
+       exit (1);                                                         \
+     }
+ 
++#if MPFR_LCONV_DPTS
++#define DPLEN ((int) strlen (localeconv()->decimal_point))
++#else
++#define DPLEN 1
++#endif
++
+ /* limit for random precision in random() */
+ const int prec_max_printf = 5000;
+ 
+@@ -195,12 +201,12 @@
+                   lo, &ulo);
+   check_length (2, ulo, 36, lu);
+   check_vfprintf (fout, "a. %hi, b. %*f, c. %Re%hn", ush, 3, f, mpfr, &ush);
+-  check_length (3, ush, 46, hu);
++  check_length (3, ush, 45 + DPLEN, hu);
+   check_vfprintf (fout, "a. %hi, b. %f, c. %#.2Rf%n", sh, d, mpfr, &i);
+-  check_length (4, i, 29, d);
++  check_length (4, i, 28 + DPLEN, d);
+   check_vfprintf (fout, "a. %R*A, b. %Fe, c. %i%zn", rnd, mpfr, mpf, sz,
+                   &sz);
+-  check_length (5, (unsigned long) sz, 34, lu); /* no format specifier "%zu" in C90 */
++  check_length (5, (unsigned long) sz, 33 + DPLEN, lu); /* no format specifier "%zu" in C90 */
+   check_vfprintf (fout, "a. %Pu, b. %c, c. %Zi%Zn", prec, ch, mpz, &mpz);
+   check_length_with_cmp (6, mpz, 17, mpz_cmp_ui (mpz, 17), Zi);
+   check_vfprintf (fout, "%% a. %#.0RNg, b. %Qx%Rn, c. %p", mpfr, mpq, &mpfr,
+@@ -224,7 +230,7 @@
+ 
+ #ifdef PRINTF_L
+   check_vfprintf (fout, "a. %RA, b. %Lf, c. %QX%zn", mpfr, ld, mpq, &sz);
+-  check_length (9, (unsigned long) sz, 30, lu); /* no format specifier "%zu" in C90 */
++  check_length (9, (unsigned long) sz, 29 + DPLEN, lu); /* no format specifier "%zu" in C90 */
+ #endif
+ 
+ #ifndef NPRINTF_HH
+diff -Naurd mpfr-4.2.0-a/tests/tprintf.c mpfr-4.2.0-b/tests/tprintf.c
+--- mpfr-4.2.0-a/tests/tprintf.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/tprintf.c	2023-04-17 21:18:26.896579133 +0000
+@@ -68,6 +68,12 @@
+       exit (1);                                                         \
+     }
+ 
++#if MPFR_LCONV_DPTS
++#define DPLEN ((int) strlen (localeconv()->decimal_point))
++#else
++#define DPLEN 1
++#endif
++
+ /* limit for random precision in random() */
+ const int prec_max_printf = 5000;
+ /* boolean: is stdout redirected to a file ? */
+@@ -316,11 +322,11 @@
+   check_vprintf ("a. %c, b. %Rb, c. %u, d. %li%ln", i, mpfr, i, lo, &ulo);
+   check_length (2, ulo, 36, lu);
+   check_vprintf ("a. %hi, b. %*f, c. %Re%hn", ush, 3, f, mpfr, &ush);
+-  check_length (3, ush, 46, hu);
++  check_length (3, ush, 45 + DPLEN, hu);
+   check_vprintf ("a. %hi, b. %f, c. %#.2Rf%n", sh, d, mpfr, &i);
+-  check_length (4, i, 29, d);
++  check_length (4, i, 28 + DPLEN, d);
+   check_vprintf ("a. %R*A, b. %Fe, c. %i%zn", rnd, mpfr, mpf, sz, &sz);
+-  check_length (5, (unsigned long) sz, 34, lu); /* no format specifier '%zu' in C90 */
++  check_length (5, (unsigned long) sz, 33 + DPLEN, lu); /* no format specifier '%zu' in C90 */
+   check_vprintf ("a. %Pu, b. %c, c. %RUG, d. %Zi%Zn", prec, ch, mpfr, mpz, &mpz);
+   check_length_with_cmp (6, mpz, 24, mpz_cmp_ui (mpz, 24), Zi);
+   check_vprintf ("%% a. %#.0RNg, b. %Qx%Rn c. %p",
+@@ -344,7 +350,7 @@
+ 
+ #ifdef PRINTF_L
+   check_vprintf ("a. %RA, b. %Lf, c. %QX%zn", mpfr, ld, mpq, &sz);
+-  check_length (9, (unsigned long) sz, 30, lu); /* no format specifier '%zu' in C90 */
++  check_length (9, (unsigned long) sz, 29 + DPLEN, lu); /* no format specifier '%zu' in C90 */
+ #endif
+ 
+ #ifndef NPRINTF_HH
+diff -Naurd mpfr-4.2.0-a/PATCHES mpfr-4.2.0-b/PATCHES
+--- mpfr-4.2.0-a/PATCHES	2023-04-17 21:19:01.988530337 +0000
++++ mpfr-4.2.0-b/PATCHES	2023-04-17 21:19:02.032530276 +0000
+@@ -0,0 +1 @@
++rec_sqrt-zivloop
+diff -Naurd mpfr-4.2.0-a/VERSION mpfr-4.2.0-b/VERSION
+--- mpfr-4.2.0-a/VERSION	2023-04-17 21:18:26.904579122 +0000
++++ mpfr-4.2.0-b/VERSION	2023-04-17 21:19:02.032530276 +0000
+@@ -1 +1 @@
+-4.2.0-p3
++4.2.0-p4
+diff -Naurd mpfr-4.2.0-a/src/mpfr.h mpfr-4.2.0-b/src/mpfr.h
+--- mpfr-4.2.0-a/src/mpfr.h	2023-04-17 21:18:26.900579128 +0000
++++ mpfr-4.2.0-b/src/mpfr.h	2023-04-17 21:19:02.032530276 +0000
+@@ -27,7 +27,7 @@
+ #define MPFR_VERSION_MAJOR 4
+ #define MPFR_VERSION_MINOR 2
+ #define MPFR_VERSION_PATCHLEVEL 0
+-#define MPFR_VERSION_STRING "4.2.0-p3"
++#define MPFR_VERSION_STRING "4.2.0-p4"
+ 
+ /* User macros:
+    MPFR_USE_FILE:        Define it to make MPFR define functions dealing
+diff -Naurd mpfr-4.2.0-a/src/rec_sqrt.c mpfr-4.2.0-b/src/rec_sqrt.c
+--- mpfr-4.2.0-a/src/rec_sqrt.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/src/rec_sqrt.c	2023-04-17 21:19:02.024530287 +0000
+@@ -463,6 +463,7 @@
+   int s, cy, inex;
+   mpfr_limb_ptr x;
+   MPFR_TMP_DECL(marker);
++  MPFR_ZIV_DECL (loop);
+ 
+   MPFR_LOG_FUNC
+     (("x[%Pu]=%.*Rg rnd=%d", mpfr_get_prec (u), mpfr_log_prec, u, rnd_mode),
+@@ -530,6 +531,7 @@
+   wp = rp + 11;
+   if (wp < rn * GMP_NUMB_BITS)
+     wp = rn * GMP_NUMB_BITS;
++  MPFR_ZIV_INIT (loop, wp);
+   for (;;)
+     {
+       MPFR_TMP_MARK (marker);
+@@ -561,8 +563,9 @@
+         }
+       MPFR_TMP_FREE(marker);
+ 
+-      wp += GMP_NUMB_BITS;
++      MPFR_ZIV_NEXT (loop, wp);
+     }
++  MPFR_ZIV_FREE (loop);
+   cy = mpfr_round_raw (MPFR_MANT(r), x, wp, 0, rp, rnd_mode, &inex);
+   MPFR_EXP(r) = - (MPFR_EXP(u) - 1 - s) / 2;
+   if (MPFR_UNLIKELY(cy != 0))
+diff -Naurd mpfr-4.2.0-a/src/version.c mpfr-4.2.0-b/src/version.c
+--- mpfr-4.2.0-a/src/version.c	2023-04-17 21:18:26.904579122 +0000
++++ mpfr-4.2.0-b/src/version.c	2023-04-17 21:19:02.032530276 +0000
+@@ -25,5 +25,5 @@
+ const char *
+ mpfr_get_version (void)
+ {
+-  return "4.2.0-p3";
++  return "4.2.0-p4";
+ }
+diff -Naurd mpfr-4.2.0-a/tests/trec_sqrt.c mpfr-4.2.0-b/tests/trec_sqrt.c
+--- mpfr-4.2.0-a/tests/trec_sqrt.c	2023-01-05 17:09:48.000000000 +0000
++++ mpfr-4.2.0-b/tests/trec_sqrt.c	2023-04-17 21:19:02.028530282 +0000
+@@ -242,6 +242,8 @@
+   data_check ("data/rec_sqrt", mpfr_rec_sqrt, "mpfr_rec_sqrt");
+   bad_cases (mpfr_rec_sqrt, pm2, "mpfr_rec_sqrt", 0, -256, 255, 4, 128,
+              800, 50);
++  bad_cases (mpfr_rec_sqrt, pm2, "mpfr_rec_sqrt", 0, -256, 255, 9999, 9999,
++             120000, 1);
+ 
+  end:
+   tests_end_mpfr ();