Subject: Re: bug#57129: 29.0.50; Improve behavior of conditionals in Eshell

From: Bruno Haible <[email-masked]>
Date: Sun, 21 Aug 2022 18:20:52 +0200

Paul Eggert wrote:
> > Since this contradicts what you debugged on mingw, I ran the test 10000
> > times on native Windows. Result:
> >    - on 32-bit mingw, no failure.
> >    - on 64-bit mingw, around 30 failures (or around 10 failures with Paul's
> >      newest patch). That is, a probability of ca. 0.3% of getting the same
> >      file name as on the previous call.
> 
> That's odd, for two reasons. First, 64-bit and 32-bit mingw shouldn't 
> differ, as they both use uint_fast64_t which should be the same width on 
> both platforms. Second, I could not reproduce the problem on 64-bit 
> Ubuntu x86-64 (after altering tempname.c to always define 
> HAS_CLOCK_ENTROPY to false) test-tempname always succeeded in 10000 tries.
> 
> Could you investigate further why mingw 64-bit fails?

That's odd indeed, and here are more detailed results.

I changed test-tempname.c to skip the case 1 and execute only case 2, and
added printf statements per the attached tempname.c.diff. Then ran
$ for i in `seq 1000`; do ./test-tempname.exe; done 2>&1 | tee out1000

In 32-bit mingw, the result is fully deterministic: each run behaves the same.
The first file name is always gl-temp-3FXzHa.xyz;
the second file name is always gl-temp-HgtmVy.xyz.

Thus, for a single Emacs process, things will look fine, but as soon as someone
starts to use temporary files in two different Emacs processes, in the way Eli
described, there will be massive collisions.

In 64-bit mingw, the 'tempname 1' value is deterministic. This simply shows
that Windows 10 does not use address space randomization (ASLR).
The 'tempname 2' value is unique 99% of the time:
  $ grep 'tempname 2' out1000-mingw64 | sort | uniq -d | wc -l
  8
The interesting thing is that each of the duplicate values of v is occurring
in the same process:
  $ grep 'tempname 2' out1000-mingw64 | sort | uniq -d
  tempname 2 v=0x00c1efa91fb60900
  tempname 2 v=0x32ccb2946974f2f6
  tempname 2 v=0x5cafcc69e359a147
  tempname 2 v=0x6d0676018e27d771
  tempname 2 v=0x6d95bd6083168079
  tempname 2 v=0x7cb95116ffae8ece
  tempname 2 v=0xe0afc7086808ce33
  tempname 2 v=0xe46a60c28fb0ec7f
  $ grep 'tempname 2' out1000-mingw64 | grep -n 0x00c1efa91fb60900
  560:tempname 2 v=0x00c1efa91fb60900
  561:tempname 2 v=0x00c1efa91fb60900
  $ grep 'tempname 2' out1000-mingw64 | grep -n 0x32ccb2946974f2f6
  1129:tempname 2 v=0x32ccb2946974f2f6
  1130:tempname 2 v=0x32ccb2946974f2f6
  etc.

So, in this environment, different Emacs processes will not conflict. But
within a single Emacs process, with 1% probability, the two resulting file
names are the same.

Bruno
--- lib/tempname.c	2022-08-16 21:27:10.455608233 +0200
+++ tempname.c	2022-08-17 01:55:47.590241280 +0200
@@ -308,6 +308,7 @@
 
   for (count = 0; count < attempts; ++count)
     {
+fprintf(stderr,"tempname 1 v=0x%016llx\n", v);  fflush(stderr);
       for (size_t i = 0; i < x_suffix_len; i++)
         {
           if (vdigits == 0)
@@ -315,6 +316,7 @@
               do
                 {
                   v = random_bits (v, use_getrandom);
+fprintf(stderr,"tempname 2 v=0x%016llx\n", v);  fflush(stderr);
                   use_getrandom = true;
                 }
               while (unfair_min <= v);
@@ -326,7 +328,7 @@
           v /= 62;
           vdigits--;
         }
-
+fprintf(stderr,"tempname filename=%s\n", tmpl); fflush(stderr);
       fd = tryfunc (tmpl, args);
       if (fd >= 0)
         {