From: Dirk Eddelbuettel <edd@debian.org>
Date: Oct, 15 2026 11:11:00 -0500
Subject: [PATCH] Avoid race condition in saving per-package rds

Based on joint work with Kevin Ushey and Claude.  Quoting from the analysis:

Difference:
  "John M. Chambers (2008)\n``Software for Data Analysis: Programming with R''; Springer NY."
  "John M. Chambers (2008) ``Software for Data Analysis:\nProgramming with R''; Springer NY."
The first is the source formatting from src/library/methods/DESCRIPTION.in. The second is
what you get after the field is re-wrapped at 72 columns by R's DESCRIPTION writer. The
mechanism is a timestamp race:

1. mkdesc runs .install_package_description, which writes the installed DESCRIPTION
through write.dcf (re-wrapping References), then immediately saves package.rds from the
original, unwrapped values read from the source file.
2. Later, src/library/Makefile.in:53 runs .vinstall_package_descriptions_as_RDS, which
regenerates package.rds from the installed DESCRIPTION unless package.rds is strictly
newer than it (file_test("-nt") at src/library/tools/R/admin.R:184).
3. The two files are written microseconds apart. Linux stamps files with a coarse clock
(one tick, ~4 ms), so their mtimes usually tie and the rds is regenerated with the wrapped
value. When a tick happens to land between the two writes, the rds is strictly newer and
keeps the unwrapped value. Both builds ran the same 6.12 kernel, so this is pure timing,
not a kernel variation.

I reproduced the re-wrap locally with R 4.6.1 by round-tripping every base package's
DESCRIPTION.in through the same read/write functions. Only two packages change: methods
(References, exactly as diffoscope shows) and tools (Suggests, which gets split before
mathjaxr). So tools/Meta/package.rds has the same exposure and will show up in some future
run even though it happened to match this time. All other base packages are unaffected
because their fields either fit in 72 columns or are in the keep-whitespace list.

Suggested fix. Make the regeneration unconditional so package.rds always comes from the
installed DESCRIPTION, regardless of mtime ties. This only affects the R source build, so
it is a safe Debian patch and a reasonable upstream proposal:

--- r-base-4.6.1.orig/src/library/tools/R/admin.R
+++ r-base-4.6.1/src/library/tools/R/admin.R
@@ -181,10 +181,8 @@ function(dir, packages)
             stop(gettextf("cannot open directory '%s'", meta_dir))
         package_info_dcf_file <- file.path(dir, p, "DESCRIPTION")
         package_info_rds_file <- file.path(meta_dir, "package.rds")
-        if(file_test("-nt",
-                     package_info_rds_file,
-                     package_info_dcf_file))
-            next
+        ## Always regenerate: the mtime of package.rds and DESCRIPTION
+        ## can tie, making the result of a "-nt" test non-deterministic.
         saveRDS(.split_description(.read_description(package_info_dcf_file)),
                  package_info_rds_file)
     }
