(root)/
glibc-2.38/
nss/
tst-nss-files-alias-leak.c
       1  /* Check for file descriptor leak in alias :include: processing (bug 23521).
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <aliases.h>
      20  #include <array_length.h>
      21  #include <dlfcn.h>
      22  #include <errno.h>
      23  #include <gnu/lib-names.h>
      24  #include <nss.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <support/check.h>
      28  #include <support/namespace.h>
      29  #include <support/support.h>
      30  #include <support/temp_file.h>
      31  #include <support/test-driver.h>
      32  #include <support/xstdio.h>
      33  #include <support/xunistd.h>
      34  
      35  static struct support_chroot *chroot_env;
      36  
      37  /* Number of the aliases for the "many" user.  This must be large
      38     enough to trigger reallocation for the pointer array, but result in
      39     answers below the maximum size tried in do_test.  */
      40  enum { many_aliases = 30 };
      41  
      42  static void
      43  prepare (int argc, char **argv)
      44  {
      45    chroot_env = support_chroot_create
      46      ((struct support_chroot_configuration) { } );
      47  
      48    char *path = xasprintf ("%s/etc/aliases", chroot_env->path_chroot);
      49    add_temp_file (path);
      50    support_write_file_string
      51      (path,
      52       "user1: :include:/etc/aliases.user1\n"
      53       "user2: :include:/etc/aliases.user2\n"
      54       "comment: comment1, :include:/etc/aliases.comment\n"
      55       "many: :include:/etc/aliases.many\n");
      56    free (path);
      57  
      58    path = xasprintf ("%s/etc/aliases.user1", chroot_env->path_chroot);
      59    add_temp_file (path);
      60    support_write_file_string (path, "alias1\n");
      61    free (path);
      62  
      63    path = xasprintf ("%s/etc/aliases.user2", chroot_env->path_chroot);
      64    add_temp_file (path);
      65    support_write_file_string (path, "alias1a, alias2\n");
      66    free (path);
      67  
      68    path = xasprintf ("%s/etc/aliases.comment", chroot_env->path_chroot);
      69    add_temp_file (path);
      70    support_write_file_string
      71      (path,
      72       /* The line must be longer than the line with the :include:
      73          directive in /etc/aliases.  */
      74       "# Long line.  ##############################################\n"
      75       "comment2\n");
      76    free (path);
      77  
      78    path = xasprintf ("%s/etc/aliases.many", chroot_env->path_chroot);
      79    add_temp_file (path);
      80    FILE *fp = xfopen (path, "w");
      81    for (int i = 0; i < many_aliases; ++i)
      82      fprintf (fp, "a%d\n", i);
      83    TEST_VERIFY_EXIT (! ferror (fp));
      84    xfclose (fp);
      85    free (path);
      86  }
      87  
      88  /* The names of the users to test.  */
      89  static const char *users[] = { "user1", "user2", "comment", "many" };
      90  
      91  static void
      92  check_aliases (int id, const struct aliasent *e)
      93  {
      94    TEST_VERIFY_EXIT (id >= 0 || id < array_length (users));
      95    const char *name = users[id];
      96    TEST_COMPARE_BLOB (e->alias_name, strlen (e->alias_name),
      97                       name, strlen (name));
      98  
      99    switch (id)
     100      {
     101      case 0:
     102        TEST_COMPARE (e->alias_members_len, 1);
     103        TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
     104                           "alias1", strlen ("alias1"));
     105        break;
     106  
     107      case 1:
     108        TEST_COMPARE (e->alias_members_len, 2);
     109        TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
     110                           "alias1a", strlen ("alias1a"));
     111        TEST_COMPARE_BLOB (e->alias_members[1], strlen (e->alias_members[1]),
     112                           "alias2", strlen ("alias2"));
     113        break;
     114  
     115      case 2:
     116        TEST_COMPARE (e->alias_members_len, 2);
     117        TEST_COMPARE_BLOB (e->alias_members[0], strlen (e->alias_members[0]),
     118                           "comment1", strlen ("comment1"));
     119        TEST_COMPARE_BLOB (e->alias_members[1], strlen (e->alias_members[1]),
     120                           "comment2", strlen ("comment2"));
     121        break;
     122  
     123      case 3:
     124        TEST_COMPARE (e->alias_members_len, many_aliases);
     125        for (int i = 0; i < e->alias_members_len; ++i)
     126          {
     127            char alias[30];
     128            int len = snprintf (alias, sizeof (alias), "a%d", i);
     129            TEST_VERIFY_EXIT (len > 0);
     130            TEST_COMPARE_BLOB (e->alias_members[i], strlen (e->alias_members[i]),
     131                               alias, len);
     132          }
     133        break;
     134      }
     135  }
     136  
     137  static int
     138  do_test (void)
     139  {
     140    /* Make sure we don't try to load the module in the chroot.  */
     141    if (dlopen (LIBNSS_FILES_SO, RTLD_NOW) == NULL)
     142      FAIL_EXIT1 ("could not load " LIBNSS_FILES_SO ": %s", dlerror ());
     143  
     144    /* Some of these descriptors will become unavailable if there is a
     145       file descriptor leak.  10 is chosen somewhat arbitrarily.  The
     146       array must be longer than the number of files opened by nss_files
     147       at the same time (currently that number is 2).  */
     148    int next_descriptors[10];
     149    for (size_t i = 0; i < array_length (next_descriptors); ++i)
     150      {
     151        next_descriptors[i] = dup (0);
     152        TEST_VERIFY_EXIT (next_descriptors[i] > 0);
     153      }
     154    for (size_t i = 0; i < array_length (next_descriptors); ++i)
     155      xclose (next_descriptors[i]);
     156  
     157    support_become_root ();
     158    if (!support_can_chroot ())
     159      return EXIT_UNSUPPORTED;
     160  
     161    __nss_configure_lookup ("aliases", "files");
     162  
     163    xchroot (chroot_env->path_chroot);
     164  
     165    /* Attempt various buffer sizes.  If the operation succeeds, we
     166       expect correct data.  */
     167    for (int id = 0; id < array_length (users); ++id)
     168      {
     169        bool found = false;
     170        for (size_t size = 1; size <= 1000; ++size)
     171          {
     172            void *buffer = malloc (size);
     173            struct aliasent result;
     174            struct aliasent *res;
     175            errno = EINVAL;
     176            int ret = getaliasbyname_r (users[id], &result, buffer, size, &res);
     177            if (ret == 0)
     178              {
     179                if (res != NULL)
     180                  {
     181                    found = true;
     182                    check_aliases (id, res);
     183                  }
     184                else
     185                  {
     186                    support_record_failure ();
     187                    printf ("error: failed lookup for user \"%s\", size %zu\n",
     188                            users[id], size);
     189                  }
     190              }
     191            else if (ret != ERANGE)
     192              {
     193                support_record_failure ();
     194                printf ("error: invalid return code %d (user \"%s\", size %zu)\n",
     195                        ret, users[id], size);
     196              }
     197            free (buffer);
     198  
     199            /* Make sure that we did not have a file descriptor leak.  */
     200            for (size_t i = 0; i < array_length (next_descriptors); ++i)
     201              {
     202                int new_fd = dup (0);
     203                if (new_fd != next_descriptors[i])
     204                  {
     205                    support_record_failure ();
     206                    printf ("error: descriptor %d at index %zu leaked"
     207                            " (user \"%s\", size %zu)\n",
     208                            next_descriptors[i], i, users[id], size);
     209  
     210                    /* Close unexpected descriptor, the leak probing
     211                       descriptors, and the leaked descriptor
     212                       next_descriptors[i].  */
     213                    xclose (new_fd);
     214                    for (size_t j = 0; j <= i; ++j)
     215                      xclose (next_descriptors[j]);
     216                    goto next_size;
     217                  }
     218              }
     219            for (size_t i = 0; i < array_length (next_descriptors); ++i)
     220              xclose (next_descriptors[i]);
     221  
     222          next_size:
     223            ;
     224          }
     225        if (!found)
     226          {
     227            support_record_failure ();
     228            printf ("error: user %s not found\n", users[id]);
     229          }
     230      }
     231  
     232    support_chroot_free (chroot_env);
     233    return 0;
     234  }
     235  
     236  #define PREPARE prepare
     237  #include <support/test-driver.c>