1 /* Test for bug 17079: heap overflow in NSS with small buffers.
2 Copyright (C) 2015-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
27 #include <support/support.h>
29 /* Check if two passwd structs contain the same data. */
31 equal (const struct passwd
*a
, const struct passwd
*b
)
33 return strcmp (a
->pw_name
, b
->pw_name
) == 0
34 && strcmp (a
->pw_passwd
, b
->pw_passwd
) == 0
35 && a
->pw_uid
== b
->pw_uid
36 && a
->pw_gid
== b
->pw_gid
37 && strcmp (a
->pw_gecos
, b
->pw_gecos
) == 0
38 && strcmp (a
->pw_dir
, b
->pw_dir
) == 0
39 && strcmp (a
->pw_shell
, b
->pw_shell
) == 0;
42 enum { MAX_TEST_ITEMS
= 10 };
43 static struct passwd test_items
[MAX_TEST_ITEMS
];
44 static int test_count
;
46 /* Initialize test_items and test_count above, with data from the
49 init_test_items (void)
54 struct passwd
*pwd
= getpwent ();
57 struct passwd
*target
= test_items
+ test_count
;
58 target
->pw_name
= xstrdup (pwd
->pw_name
);
59 target
->pw_passwd
= xstrdup (pwd
->pw_passwd
);
60 target
->pw_uid
= pwd
->pw_uid
;
61 target
->pw_gid
= pwd
->pw_gid
;
62 target
->pw_gecos
= xstrdup (pwd
->pw_gecos
);
63 target
->pw_dir
= xstrdup (pwd
->pw_dir
);
64 target
->pw_shell
= xstrdup (pwd
->pw_shell
);
66 while (++test_count
< MAX_TEST_ITEMS
);
69 /* Filter out those test items which cannot be looked up by name or
72 for (int i
= 0; i
< test_count
; ++i
)
74 struct passwd
*pwd1
= getpwnam (test_items
[i
].pw_name
);
75 struct passwd
*pwd2
= getpwuid (test_items
[i
].pw_uid
);
76 if (pwd1
== NULL
|| !equal (pwd1
, test_items
+ i
)
77 || pwd2
== NULL
|| !equal (pwd2
, test_items
+ i
))
79 printf ("info: skipping user \"%s\", UID %ld due to inconsistency\n",
80 test_items
[i
].pw_name
, (long) test_items
[i
].pw_uid
);
81 test_items
[i
].pw_name
= NULL
;
88 puts ("error: no accounts found which can be looked up by name and UID.");
92 /* Set to true if an error is encountered. */
95 /* Return true if the padding has not been tampered with. */
97 check_padding (char *buffer
, size_t size
, char pad
)
99 char *end
= buffer
+ size
;
109 /* Test one buffer size and padding combination. */
111 test_one (const struct passwd
*item
, size_t buffer_size
,
112 char pad
, size_t padding_size
)
114 char *buffer
= xmalloc (buffer_size
+ padding_size
);
117 struct passwd
*result
;
120 /* Test getpwname_r. */
121 memset (buffer
, pad
, buffer_size
+ padding_size
);
122 pwd
= (struct passwd
) {};
123 ret
= getpwnam_r (item
->pw_name
, &pwd
, buffer
, buffer_size
, &result
);
124 if (!check_padding (buffer
+ buffer_size
, padding_size
, pad
))
126 printf ("error: padding change: "
127 "name \"%s\", buffer size %zu, padding size %zu, pad 0x%02x\n",
128 item
->pw_name
, buffer_size
, padding_size
, (unsigned char) pad
);
135 printf ("error: no data: name \"%s\", buffer size %zu\n",
136 item
->pw_name
, buffer_size
);
139 else if (!equal (item
, result
))
141 printf ("error: lookup mismatch: name \"%s\", buffer size %zu\n",
142 item
->pw_name
, buffer_size
);
146 else if (ret
!= ERANGE
)
149 printf ("error: lookup failure for name \"%s\": %m (%d)\n",
154 /* Test getpwuid_r. */
155 memset (buffer
, pad
, buffer_size
+ padding_size
);
156 pwd
= (struct passwd
) {};
157 ret
= getpwuid_r (item
->pw_uid
, &pwd
, buffer
, buffer_size
, &result
);
158 if (!check_padding (buffer
+ buffer_size
, padding_size
, pad
))
160 printf ("error: padding change: "
161 "UID %ld, buffer size %zu, padding size %zu, pad 0x%02x\n",
162 (long) item
->pw_uid
, buffer_size
, padding_size
,
163 (unsigned char) pad
);
170 printf ("error: no data: UID %ld, buffer size %zu\n",
171 (long) item
->pw_uid
, buffer_size
);
174 else if (!equal (item
, result
))
176 printf ("error: lookup mismatch: UID %ld, buffer size %zu\n",
177 (long) item
->pw_uid
, buffer_size
);
181 else if (ret
!= ERANGE
)
184 printf ("error: lookup failure for UID \"%ld\": %m (%d)\n",
185 (long) item
->pw_uid
, ret
);
192 /* Test one buffer size with different paddings. */
194 test_buffer_size (size_t buffer_size
)
196 for (int i
= 0; i
< test_count
; ++i
)
197 for (size_t padding_size
= 0; padding_size
< 3; ++padding_size
)
199 /* Skip entries with inconsistent name/UID lookups. */
200 if (test_items
[i
].pw_name
== NULL
)
203 test_one (test_items
+ i
, buffer_size
, '\0', padding_size
);
204 if (padding_size
> 0)
206 test_one (test_items
+ i
, buffer_size
, ':', padding_size
);
207 test_one (test_items
+ i
, buffer_size
, '\n', padding_size
);
208 test_one (test_items
+ i
, buffer_size
, '\xff', padding_size
);
209 test_one (test_items
+ i
, buffer_size
, '@', padding_size
);
217 __nss_configure_lookup ("passwd", "files");
219 if (!init_test_items ())
221 printf ("info: %d test items\n", test_count
);
223 for (size_t buffer_size
= 0; buffer_size
<= 65; ++buffer_size
)
224 test_buffer_size (buffer_size
);
225 for (size_t buffer_size
= 64 + 4; buffer_size
< 256; buffer_size
+= 4)
226 test_buffer_size (buffer_size
);
227 test_buffer_size (255);
228 test_buffer_size (257);
229 for (size_t buffer_size
= 256; buffer_size
< 512; buffer_size
+= 8)
230 test_buffer_size (buffer_size
);
231 test_buffer_size (511);
232 test_buffer_size (513);
233 test_buffer_size (1024);
234 test_buffer_size (2048);
242 #include <support/test-driver.c>