Blame nss/tst-nss-files-alias-leak.c

Packit Service d047a0
/* Check for file descriptor leak in alias :include: processing (bug 23521).
Packit Service d047a0
   Copyright (C) 2018 Free Software Foundation, Inc.
Packit Service d047a0
   This file is part of the GNU C Library.
Packit Service d047a0
Packit Service d047a0
   The GNU C Library is free software; you can redistribute it and/or
Packit Service d047a0
   modify it under the terms of the GNU Lesser General Public
Packit Service d047a0
   License as published by the Free Software Foundation; either
Packit Service d047a0
   version 2.1 of the License, or (at your option) any later version.
Packit Service d047a0
Packit Service d047a0
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service d047a0
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d047a0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service d047a0
   Lesser General Public License for more details.
Packit Service d047a0
Packit Service d047a0
   You should have received a copy of the GNU Lesser General Public
Packit Service d047a0
   License along with the GNU C Library; if not, see
Packit Service d047a0
   <http://www.gnu.org/licenses/>.  */
Packit Service d047a0
Packit Service d047a0
#include <aliases.h>
Packit Service d047a0
#include <array_length.h>
Packit Service d047a0
#include <dlfcn.h>
Packit Service d047a0
#include <errno.h>
Packit Service d047a0
#include <gnu/lib-names.h>
Packit Service d047a0
#include <nss.h>
Packit Service d047a0
#include <stdlib.h>
Packit Service d047a0
#include <string.h>
Packit Service d047a0
#include <support/check.h>
Packit Service d047a0
#include <support/namespace.h>
Packit Service d047a0
#include <support/support.h>
Packit Service d047a0
#include <support/temp_file.h>
Packit Service d047a0
#include <support/test-driver.h>
Packit Service d047a0
#include <support/xstdio.h>
Packit Service d047a0
#include <support/xunistd.h>
Packit Service d047a0
Packit Service d047a0
static struct support_chroot *chroot_env;
Packit Service d047a0
Packit Service d047a0
/* Number of the aliases for the "many" user.  This must be large
Packit Service d047a0
   enough to trigger reallocation for the pointer array, but result in
Packit Service d047a0
   answers below the maximum size tried in do_test.  */
Packit Service d047a0
enum { many_aliases = 30 };
Packit Service d047a0
Packit Service d047a0
static void
Packit Service d047a0
prepare (int argc, char **argv)
Packit Service d047a0
{
Packit Service d047a0
  chroot_env = support_chroot_create
Packit Service d047a0
    ((struct support_chroot_configuration) { } );
Packit Service d047a0
Packit Service d047a0
  char *path = xasprintf ("%s/etc/aliases", chroot_env->path_chroot);
Packit Service d047a0
  add_temp_file (path);
Packit Service d047a0
  support_write_file_string
Packit Service d047a0
    (path,
Packit Service d047a0
     "user1: :include:/etc/aliases.user1\n"
Packit Service d047a0
     "user2: :include:/etc/aliases.user2\n"
Packit Service d047a0
     "comment: comment1, :include:/etc/aliases.comment\n"
Packit Service d047a0
     "many: :include:/etc/aliases.many\n");
Packit Service d047a0
  free (path);
Packit Service d047a0
Packit Service d047a0
  path = xasprintf ("%s/etc/aliases.user1", chroot_env->path_chroot);
Packit Service d047a0
  add_temp_file (path);
Packit Service d047a0
  support_write_file_string (path, "alias1\n");
Packit Service d047a0
  free (path);
Packit Service d047a0
Packit Service d047a0
  path = xasprintf ("%s/etc/aliases.user2", chroot_env->path_chroot);
Packit Service d047a0
  add_temp_file (path);
Packit Service d047a0
  support_write_file_string (path, "alias1a, alias2\n");
Packit Service d047a0
  free (path);
Packit Service d047a0
Packit Service d047a0
  path = xasprintf ("%s/etc/aliases.comment", chroot_env->path_chroot);
Packit Service d047a0
  add_temp_file (path);
Packit Service d047a0
  support_write_file_string
Packit Service d047a0
    (path,
Packit Service d047a0
     /* The line must be longer than the line with the :include:
Packit Service d047a0
        directive in /etc/aliases.  */
Packit Service d047a0
     "# Long line.  ##############################################\n"
Packit Service d047a0
     "comment2\n");
Packit Service d047a0
  free (path);
Packit Service d047a0
Packit Service d047a0
  path = xasprintf ("%s/etc/aliases.many", chroot_env->path_chroot);
Packit Service d047a0
  add_temp_file (path);
Packit Service d047a0
  FILE *fp = xfopen (path, "w");
Packit Service d047a0
  for (int i = 0; i < many_aliases; ++i)
Packit Service d047a0
    fprintf (fp, "a%d\n", i);
Packit Service d047a0
  TEST_VERIFY_EXIT (! ferror (fp));
Packit Service d047a0
  xfclose (fp);
Packit Service d047a0
  free (path);
Packit Service d047a0
}
Packit Service d047a0
Packit Service d047a0
/* The names of the users to test.  */
Packit Service d047a0
static const char *users[] = { "user1", "user2", "comment", "many" };
Packit Service d047a0
Packit Service d047a0
static void
Packit Service d047a0
check_aliases (int id, const struct aliasent *e)
Packit Service d047a0
{
Packit Service d047a0
  TEST_VERIFY_EXIT (id >= 0 || id < array_length (users));
Packit Service d047a0
  const char *name = users[id];
Packit Service d047a0
  TEST_COMPARE_BLOB (e->alias_name, strlen (e->alias_name),
Packit Service d047a0
                     name, strlen (name));
Packit Service d047a0
Packit Service d047a0
  switch (id)
Packit Service d047a0
    {
Packit Service d047a0
    case 0:
Packit Service d047a0
      TEST_COMPARE (e->alias_members_len, 1);
Packit Service d047a0
      TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
Packit Service d047a0
                         "alias1", strlen ("alias1"));
Packit Service d047a0
      break;
Packit Service d047a0
Packit Service d047a0
    case 1:
Packit Service d047a0
      TEST_COMPARE (e->alias_members_len, 2);
Packit Service d047a0
      TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
Packit Service d047a0
                         "alias1a", strlen ("alias1a"));
Packit Service d047a0
      TEST_COMPARE_BLOB (e->alias_members[1], strlen (e->alias_members[1]),
Packit Service d047a0
                         "alias2", strlen ("alias2"));
Packit Service d047a0
      break;
Packit Service d047a0
Packit Service d047a0
    case 2:
Packit Service d047a0
      TEST_COMPARE (e->alias_members_len, 2);
Packit Service d047a0
      TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
Packit Service d047a0
                         "comment1", strlen ("comment1"));
Packit Service d047a0
      TEST_COMPARE_BLOB (e->alias_members[1], strlen (e->alias_members[1]),
Packit Service d047a0
                         "comment2", strlen ("comment2"));
Packit Service d047a0
      break;
Packit Service d047a0
Packit Service d047a0
    case 3:
Packit Service d047a0
      TEST_COMPARE (e->alias_members_len, many_aliases);
Packit Service d047a0
      for (int i = 0; i < e->alias_members_len; ++i)
Packit Service d047a0
        {
Packit Service d047a0
          char alias[30];
Packit Service d047a0
          int len = snprintf (alias, sizeof (alias), "a%d", i);
Packit Service d047a0
          TEST_VERIFY_EXIT (len > 0);
Packit Service d047a0
          TEST_COMPARE_BLOB (e->alias_members[i], strlen (e->alias_members[i]),
Packit Service d047a0
                             alias, len);
Packit Service d047a0
        }
Packit Service d047a0
      break;
Packit Service d047a0
    }
Packit Service d047a0
}
Packit Service d047a0
Packit Service d047a0
static int
Packit Service d047a0
do_test (void)
Packit Service d047a0
{
Packit Service d047a0
  /* Make sure we don't try to load the module in the chroot.  */
Packit Service d047a0
  if (dlopen (LIBNSS_FILES_SO, RTLD_NOW) == NULL)
Packit Service d047a0
    FAIL_EXIT1 ("could not load " LIBNSS_FILES_SO ": %s", dlerror ());
Packit Service d047a0
Packit Service d047a0
  /* Some of these descriptors will become unavailable if there is a
Packit Service d047a0
     file descriptor leak.  10 is chosen somewhat arbitrarily.  The
Packit Service d047a0
     array must be longer than the number of files opened by nss_files
Packit Service d047a0
     at the same time (currently that number is 2).  */
Packit Service d047a0
  int next_descriptors[10];
Packit Service d047a0
  for (size_t i = 0; i < array_length (next_descriptors); ++i)
Packit Service d047a0
    {
Packit Service d047a0
      next_descriptors[i] = dup (0);
Packit Service d047a0
      TEST_VERIFY_EXIT (next_descriptors[i] > 0);
Packit Service d047a0
    }
Packit Service d047a0
  for (size_t i = 0; i < array_length (next_descriptors); ++i)
Packit Service d047a0
    xclose (next_descriptors[i]);
Packit Service d047a0
Packit Service d047a0
  support_become_root ();
Packit Service d047a0
  if (!support_can_chroot ())
Packit Service d047a0
    return EXIT_UNSUPPORTED;
Packit Service d047a0
Packit Service d047a0
  __nss_configure_lookup ("aliases", "files");
Packit Service d047a0
Packit Service d047a0
  xchroot (chroot_env->path_chroot);
Packit Service d047a0
Packit Service d047a0
  /* Attempt various buffer sizes.  If the operation succeeds, we
Packit Service d047a0
     expect correct data.  */
Packit Service d047a0
  for (int id = 0; id < array_length (users); ++id)
Packit Service d047a0
    {
Packit Service d047a0
      bool found = false;
Packit Service d047a0
      for (size_t size = 1; size <= 1000; ++size)
Packit Service d047a0
        {
Packit Service d047a0
          void *buffer = malloc (size);
Packit Service d047a0
          struct aliasent result;
Packit Service d047a0
          struct aliasent *res;
Packit Service d047a0
          errno = EINVAL;
Packit Service d047a0
          int ret = getaliasbyname_r (users[id], &result, buffer, size, &res;;
Packit Service d047a0
          if (ret == 0)
Packit Service d047a0
            {
Packit Service d047a0
              if (res != NULL)
Packit Service d047a0
                {
Packit Service d047a0
                  found = true;
Packit Service d047a0
                  check_aliases (id, res);
Packit Service d047a0
                }
Packit Service d047a0
              else
Packit Service d047a0
                {
Packit Service d047a0
                  support_record_failure ();
Packit Service d047a0
                  printf ("error: failed lookup for user \"%s\", size %zu\n",
Packit Service d047a0
                          users[id], size);
Packit Service d047a0
                }
Packit Service d047a0
            }
Packit Service d047a0
          else if (ret != ERANGE)
Packit Service d047a0
            {
Packit Service d047a0
              support_record_failure ();
Packit Service d047a0
              printf ("error: invalid return code %d (user \%s\", size %zu)\n",
Packit Service d047a0
                      ret, users[id], size);
Packit Service d047a0
            }
Packit Service d047a0
          free (buffer);
Packit Service d047a0
Packit Service d047a0
          /* Make sure that we did not have a file descriptor leak.  */
Packit Service d047a0
          for (size_t i = 0; i < array_length (next_descriptors); ++i)
Packit Service d047a0
            {
Packit Service d047a0
              int new_fd = dup (0);
Packit Service d047a0
              if (new_fd != next_descriptors[i])
Packit Service d047a0
                {
Packit Service d047a0
                  support_record_failure ();
Packit Service d047a0
                  printf ("error: descriptor %d at index %zu leaked"
Packit Service d047a0
                          " (user \"%s\", size %zu)\n",
Packit Service d047a0
                          next_descriptors[i], i, users[id], size);
Packit Service d047a0
Packit Service d047a0
                  /* Close unexpected descriptor, the leak probing
Packit Service d047a0
                     descriptors, and the leaked descriptor
Packit Service d047a0
                     next_descriptors[i].  */
Packit Service d047a0
                  xclose (new_fd);
Packit Service d047a0
                  for (size_t j = 0; j <= i; ++j)
Packit Service d047a0
                    xclose (next_descriptors[j]);
Packit Service d047a0
                  goto next_size;
Packit Service d047a0
                }
Packit Service d047a0
            }
Packit Service d047a0
          for (size_t i = 0; i < array_length (next_descriptors); ++i)
Packit Service d047a0
            xclose (next_descriptors[i]);
Packit Service d047a0
Packit Service d047a0
        next_size:
Packit Service d047a0
          ;
Packit Service d047a0
        }
Packit Service d047a0
      if (!found)
Packit Service d047a0
        {
Packit Service d047a0
          support_record_failure ();
Packit Service d047a0
          printf ("error: user %s not found\n", users[id]);
Packit Service d047a0
        }
Packit Service d047a0
    }
Packit Service d047a0
Packit Service d047a0
  support_chroot_free (chroot_env);
Packit Service d047a0
  return 0;
Packit Service d047a0
}
Packit Service d047a0
Packit Service d047a0
#define PREPARE prepare
Packit Service d047a0
#include <support/test-driver.c>