1/* Check for file descriptor leak in alias :include: processing (bug 23521).
2 Copyright (C) 2018-2022 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
35static 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. */
40enum { many_aliases = 30 };
41
42static void
43prepare (int argc, char **argv)
44{
45 chroot_env = support_chroot_create
46 ((struct support_chroot_configuration) { } );
47
48 char *path = xasprintf (format: "%s/etc/aliases", chroot_env->path_chroot);
49 add_temp_file (name: path);
50 support_write_file_string
51 (path,
52 contents: "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 (ptr: path);
57
58 path = xasprintf (format: "%s/etc/aliases.user1", chroot_env->path_chroot);
59 add_temp_file (name: path);
60 support_write_file_string (path, contents: "alias1\n");
61 free (ptr: path);
62
63 path = xasprintf (format: "%s/etc/aliases.user2", chroot_env->path_chroot);
64 add_temp_file (name: path);
65 support_write_file_string (path, contents: "alias1a, alias2\n");
66 free (ptr: path);
67
68 path = xasprintf (format: "%s/etc/aliases.comment", chroot_env->path_chroot);
69 add_temp_file (name: 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 contents: "# Long line. ##############################################\n"
75 "comment2\n");
76 free (ptr: path);
77
78 path = xasprintf (format: "%s/etc/aliases.many", chroot_env->path_chroot);
79 add_temp_file (name: path);
80 FILE *fp = xfopen (path, mode: "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 (ptr: path);
86}
87
88/* The names of the users to test. */
89static const char *users[] = { "user1", "user2", "comment", "many" };
90
91static void
92check_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 (s: alias, maxlen: sizeof (alias), format: "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
137static int
138do_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 (fd: 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 (dbname: "aliases", string: "files");
162
163 xchroot (path: 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: size);
173 struct aliasent result;
174 struct aliasent *res;
175 errno = EINVAL;
176 int ret = getaliasbyname_r (name: users[id], result_buf: &result, buffer: buffer, buflen: size, result: &res);
177 if (ret == 0)
178 {
179 if (res != NULL)
180 {
181 found = true;
182 check_aliases (id, e: res);
183 }
184 else
185 {
186 support_record_failure ();
187 printf (format: "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 (format: "error: invalid return code %d (user \"%s\", size %zu)\n",
195 ret, users[id], size);
196 }
197 free (ptr: 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 (fd: 0);
203 if (new_fd != next_descriptors[i])
204 {
205 support_record_failure ();
206 printf (format: "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 (format: "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

source code of glibc/nss/tst-nss-files-alias-leak.c