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

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