]> granicus.if.org Git - libevent/commitdiff
windows socketpair tmpfile: use random prefix
authorPierce Lopez <pierce.lopez@gmail.com>
Sun, 26 Jul 2020 01:17:21 +0000 (21:17 -0400)
committerPierce Lopez <pierce.lopez@gmail.com>
Sun, 26 Jul 2020 22:01:35 +0000 (18:01 -0400)
fixes #1058

GetTempFileNameA() takes an optional prefix, and a "unique" long value
which can optionally be zero, which causes it to automatically
increment until a not-yet-existing filename is found.

When libevent creates many AF_UNIX socketpairs on windows,
it slows down dramatically, due to always using the same blank prefix,
and GetTempFileNameA() needing to iterate through all the existing
socketpair filenames. With a present and varying prefix, it will have
much less need to iterate.

It was also possible for a race with other processes also using
blank-prefix tmpfile names to result in both trying to start using
the same name at the same time (because libevent deletes the file
and then re-creates it as a unix socket), which should now be
much less likely.

Unfortuantely, "much" is just a factor of 32k, because the prefix
is only 3 characters, and windows filesystems are case-insensitive,
so doing better would require more sophisticated windows API usage
and charset trickyness.

evutil.c

index 93093b0e5c4cee51c826e2a5f19213b96be11412..a9207188e698cfa95b731094c74405d65b824348 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -212,11 +212,21 @@ create_tmpfile(char tmpfile[MAX_PATH])
 {
        char short_path[MAX_PATH] = {0};
        char long_path[MAX_PATH] = {0};
-       char *tmp_file = tmpfile;
+       char prefix[4] = {0};
+       // GetTempFileNameA() uses up to the first three characters of the prefix
+       // and windows filesystems are case-insensitive
+       const char *base32set = "abcdefghijklmnopqrstuvwxyz012345";
+       ev_uint16_t rnd;
+
+       evutil_secure_rng_get_bytes(&rnd, sizeof(rnd));
+       prefix[0] = base32set[(rnd      ) & 31];
+       prefix[1] = base32set[(rnd >>  5) & 31];
+       prefix[2] = base32set[(rnd >> 10) & 31];
+       prefix[3] = '\0';
 
        GetTempPathA(MAX_PATH, short_path);
        GetLongPathNameA(short_path, long_path, MAX_PATH);
-       if (!GetTempFileNameA(long_path, NULL, 0, tmp_file)) {
+       if (!GetTempFileNameA(long_path, prefix, 0, tmpfile)) {
                event_warnx("GetTempFileName failed: %d", EVUTIL_SOCKET_ERROR());
                return -1;
        }