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 | |
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 (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. */ |
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 (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 | |
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 (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 | |