]>
Commit | Line | Data |
---|---|---|
e95c6f61 | 1 | /* Check for file descriptor leak in alias :include: processing (bug 23521). |
6d7e8eda | 2 | Copyright (C) 2018-2023 Free Software Foundation, Inc. |
e95c6f61 FW |
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 | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
e95c6f61 FW |
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 (); | |
67112f7a | 194 | printf ("error: invalid return code %d (user \"%s\", size %zu)\n", |
e95c6f61 FW |
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> |