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

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