This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Avoid concurrency problem in ldconfig (bug 23973)


* Andreas Schwab:

> On Apr 18 2019, Florian Weimer <fweimer@redhat.com> wrote:
>
>> * Andreas Schwab:
>>
>>> On Apr 18 2019, Florian Weimer <fweimer@redhat.com> wrote:
>>>
>>>> * Andreas Schwab:
>>>>
>>>>> Use a unique name for the temporary file when updating the ld.so cache, so
>>>>> that two concurrent runs of ldconfig don't write to the same file.
>>>>>
>>>>> 	* elf/cache.c (save_cache): Use unique temporary name.
>>>>> 	(save_aux_cache): Likewise.
>>>>
>>>> The downside of this change is that if ldconfig is interrupted, the
>>>> temporary file never goes away.
>>>>
>>>> Ideally, we would use O_TMPFILE if supported by the (file) system, but
>>>> that can get quite involved.
>>>
>>> That shortens the window, but won't close it, since we need a name to
>>> pass to rename in any case.
>>
>> The difference is that with a fixed name, the next ldconfig run will use
>> the same name and rename, cleaning up.  With a random, unique name, that
>> automatic cleanup is gone.
>
> O_TMPFILE won't change that.

Hmm.  I thought it would, but it does not.

It's possible to avoid writing to the fixed name though, like this:

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
  if (argc != 2)
    {
      fprintf (stderr, "usage: %s PATH\n", argv[0]);
      exit (1);
    }

  const char *path = argv[1];
  char *dir_storage = strdup (path);
  if (dir_storage == NULL)
    err (1, "strdup");
  char *dir = dirname (dir_storage);

  int fd = open64 (dir, O_TMPFILE | O_RDWR, 0700);
  if (fd < 0)
    err (1, "open (\"%s\"., O_TMPFILE | O_RDWR)", dir);

  time_t t = time (NULL);
  const char *data = asctime (gmtime (&t));
  if (write (fd, data, strlen (data)) != strlen (data))
    errx (1, "write failure");

  if (fsync (fd) != 0)
    err (1, "fsync");

  char *proc_fd_path;
  if (asprintf (&proc_fd_path, "/proc/self/fd/%d", fd) < 0)
    err (1, "asprintf");

  if (linkat (AT_FDCWD, proc_fd_path, AT_FDCWD, path, AT_SYMLINK_FOLLOW) == 0)
    {
      /* Nothing to do.  */
    }
  else
    {
      char *tmp_path;
      if (asprintf (&tmp_path, "%s~", path) < 0)
        err (1, "asprintf");

      while (true)
        if (linkat (AT_FDCWD, proc_fd_path, AT_FDCWD,
                    tmp_path, AT_SYMLINK_FOLLOW) == 0)
          break;
        else
          {
            if (errno == EEXIST)
              {
                if (unlink (tmp_path) != 0)
                  if (errno != ENOENT)
                    err (1, "unlink (\"%s\")", tmp_path);
                continue;
              }
            else
              err (1, "linkat");
          }

      if (renameat2 (AT_FDCWD, tmp_path, AT_FDCWD, path, RENAME_EXCHANGE) != 0)
        err (1, "renameat2");
      if (unlink (tmp_path) != 0)
        if (errno != ENOENT)
          err (1, "unlink (\"%s\")", tmp_path);

      free (tmp_path);
    }

  free (proc_fd_path);

  if (close (fd) != 0)
    err (1, "close");

  free (dir);

  return 0;
}

This is unfortunately way more complicated than it should be.

Thanks,
Florian


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]