1 /* Check for file descriptor leak in alias :include: processing (bug 23521).
2    Copyright (C) 2018-2021 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
prepare(int argc,char ** argv)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
check_aliases(int id,const struct aliasent * e)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
do_test(void)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>
238