blob: cfa1179d7f30c6c37374d59daf73aadcf229f2f9 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22#include "test.h"
23
24#ifdef HAVE_SYS_RESOURCE_H
25#include <sys/resource.h>
26#endif
27#ifdef HAVE_FCNTL_H
28#include <fcntl.h>
29#endif
30#ifdef HAVE_LIMITS_H
31#include <limits.h>
32#endif
33
34#include "warnless.h"
35#include "memdebug.h"
36
37#if !defined(HAVE_POLL_FINE) && \
38 !defined(USE_WINSOCK) && \
39 !defined(TPF) && \
40 !defined(FD_SETSIZE)
41#error "this test requires FD_SETSIZE"
42#endif
43
44#define SAFETY_MARGIN (11)
45
46#if defined(WIN32) || defined(_WIN32) || defined(MSDOS)
47#define DEV_NULL "NUL"
48#else
49#define DEV_NULL "/dev/null"
50#endif
51
52#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
53
54static int *fd = NULL;
55static struct rlimit num_open;
56static char msgbuff[256];
57
58static void store_errmsg(const char *msg, int err)
59{
60 if(!err)
61 snprintf(msgbuff, sizeof(msgbuff), "%s", msg);
62 else
63 snprintf(msgbuff, sizeof(msgbuff), "%s, errno %d, %s", msg, err,
64 strerror(err));
65}
66
67static void close_file_descriptors(void)
68{
69 for(num_open.rlim_cur = 0;
70 num_open.rlim_cur < num_open.rlim_max;
71 num_open.rlim_cur++)
72 if(fd[num_open.rlim_cur] > 0)
73 close(fd[num_open.rlim_cur]);
74 free(fd);
75 fd = NULL;
76}
77
78static int fopen_works(void)
79{
80 FILE *fpa[3];
81 int i;
82 int ret = 1;
83
84 for(i = 0; i < 3; i++) {
85 fpa[i] = NULL;
86 }
87 for(i = 0; i < 3; i++) {
88 fpa[i] = fopen(DEV_NULL, FOPEN_READTEXT);
89 if(fpa[i] == NULL) {
90 store_errmsg("fopen failed", ERRNO);
91 fprintf(stderr, "%s\n", msgbuff);
92 ret = 0;
93 break;
94 }
95 }
96 for(i = 0; i < 3; i++) {
97 if(fpa[i] != NULL)
98 fclose(fpa[i]);
99 }
100 return ret;
101}
102
103static int rlimit(int keep_open)
104{
105 int *tmpfd;
106 rlim_t nitems, i;
107 int *memchunk = NULL;
108 char *fmt;
109 struct rlimit rl;
110 char strbuff[256];
111 char strbuff1[81];
112 char fmt_u[] = "%u";
113 char fmt_lu[] = "%lu";
114#ifdef HAVE_LONGLONG
115 char fmt_llu[] = "%llu";
116
117 if(sizeof(rl.rlim_max) > sizeof(long))
118 fmt = fmt_llu;
119 else
120#endif
121 fmt = (sizeof(rl.rlim_max) < sizeof(long))?fmt_u:fmt_lu;
122
123 /* get initial open file limits */
124
125 if(getrlimit(RLIMIT_NOFILE, &rl) != 0) {
126 store_errmsg("getrlimit() failed", ERRNO);
127 fprintf(stderr, "%s\n", msgbuff);
128 return -1;
129 }
130
131 /* show initial open file limits */
132
133#ifdef RLIM_INFINITY
134 if(rl.rlim_cur == RLIM_INFINITY)
135 strcpy(strbuff, "INFINITY");
136 else
137#endif
138 snprintf(strbuff, sizeof(strbuff), fmt, rl.rlim_cur);
139 fprintf(stderr, "initial soft limit: %s\n", strbuff);
140
141#ifdef RLIM_INFINITY
142 if(rl.rlim_max == RLIM_INFINITY)
143 strcpy(strbuff, "INFINITY");
144 else
145#endif
146 snprintf(strbuff, sizeof(strbuff), fmt, rl.rlim_max);
147 fprintf(stderr, "initial hard limit: %s\n", strbuff);
148
149 /*
150 * if soft limit and hard limit are different we ask the
151 * system to raise soft limit all the way up to the hard
152 * limit. Due to some other system limit the soft limit
153 * might not be raised up to the hard limit. So from this
154 * point the resulting soft limit is our limit. Trying to
155 * open more than soft limit file descriptors will fail.
156 */
157
158 if(rl.rlim_cur != rl.rlim_max) {
159
160#ifdef OPEN_MAX
161 if((rl.rlim_cur > 0) &&
162 (rl.rlim_cur < OPEN_MAX)) {
163 fprintf(stderr, "raising soft limit up to OPEN_MAX\n");
164 rl.rlim_cur = OPEN_MAX;
165 if(setrlimit(RLIMIT_NOFILE, &rl) != 0) {
166 /* on failure don't abort just issue a warning */
167 store_errmsg("setrlimit() failed", ERRNO);
168 fprintf(stderr, "%s\n", msgbuff);
169 msgbuff[0] = '\0';
170 }
171 }
172#endif
173
174 fprintf(stderr, "raising soft limit up to hard limit\n");
175 rl.rlim_cur = rl.rlim_max;
176 if(setrlimit(RLIMIT_NOFILE, &rl) != 0) {
177 /* on failure don't abort just issue a warning */
178 store_errmsg("setrlimit() failed", ERRNO);
179 fprintf(stderr, "%s\n", msgbuff);
180 msgbuff[0] = '\0';
181 }
182
183 /* get current open file limits */
184
185 if(getrlimit(RLIMIT_NOFILE, &rl) != 0) {
186 store_errmsg("getrlimit() failed", ERRNO);
187 fprintf(stderr, "%s\n", msgbuff);
188 return -3;
189 }
190
191 /* show current open file limits */
192
193#ifdef RLIM_INFINITY
194 if(rl.rlim_cur == RLIM_INFINITY)
195 strcpy(strbuff, "INFINITY");
196 else
197#endif
198 snprintf(strbuff, sizeof(strbuff), fmt, rl.rlim_cur);
199 fprintf(stderr, "current soft limit: %s\n", strbuff);
200
201#ifdef RLIM_INFINITY
202 if(rl.rlim_max == RLIM_INFINITY)
203 strcpy(strbuff, "INFINITY");
204 else
205#endif
206 snprintf(strbuff, sizeof(strbuff), fmt, rl.rlim_max);
207 fprintf(stderr, "current hard limit: %s\n", strbuff);
208
209 } /* (rl.rlim_cur != rl.rlim_max) */
210
211 /*
212 * test 537 is all about testing libcurl functionality
213 * when the system has nearly exhausted the number of
214 * available file descriptors. Test 537 will try to run
215 * with a very small number of file descriptors available.
216 * This implies that any file descriptor which is open
217 * when the test runs will have a number in the high range
218 * of whatever the system supports.
219 */
220
221 /*
222 * reserve a chunk of memory before opening file descriptors to
223 * avoid a low memory condition once the file descriptors are
224 * open. System conditions that could make the test fail should
225 * be addressed in the precheck phase. This chunk of memory shall
226 * be always free()ed before exiting the rlimit() function so
227 * that it becomes available to the test.
228 */
229
230 for(nitems = i = 1; nitems <= i; i *= 2)
231 nitems = i;
232 if(nitems > 0x7fff)
233 nitems = 0x40000;
234 do {
235 num_open.rlim_max = sizeof(*memchunk) * nitems;
236 snprintf(strbuff, sizeof(strbuff), fmt, num_open.rlim_max);
237 fprintf(stderr, "allocating memchunk %s byte array\n", strbuff);
238 memchunk = malloc(sizeof(*memchunk) * (size_t)nitems);
239 if(!memchunk) {
240 fprintf(stderr, "memchunk, malloc() failed\n");
241 nitems /= 2;
242 }
243 } while(nitems && !memchunk);
244 if(!memchunk) {
245 store_errmsg("memchunk, malloc() failed", ERRNO);
246 fprintf(stderr, "%s\n", msgbuff);
247 return -4;
248 }
249
250 /* initialize it to fight lazy allocation */
251
252 fprintf(stderr, "initializing memchunk array\n");
253
254 for(i = 0; i < nitems; i++)
255 memchunk[i] = -1;
256
257 /* set the number of file descriptors we will try to open */
258
259#ifdef RLIM_INFINITY
260 if((rl.rlim_cur > 0) && (rl.rlim_cur != RLIM_INFINITY)) {
261#else
262 if(rl.rlim_cur > 0) {
263#endif
264 /* soft limit minus SAFETY_MARGIN */
265 num_open.rlim_max = rl.rlim_cur - SAFETY_MARGIN;
266 }
267 else {
268 /* a huge number of file descriptors */
269 for(nitems = i = 1; nitems <= i; i *= 2)
270 nitems = i;
271 if(nitems > 0x7fff)
272 nitems = 0x40000;
273 num_open.rlim_max = nitems;
274 }
275
276 /* verify that we won't overflow size_t in malloc() */
277
278 if((size_t)(num_open.rlim_max) > ((size_t)-1) / sizeof(*fd)) {
279 snprintf(strbuff1, sizeof(strbuff1), fmt, num_open.rlim_max);
280 snprintf(strbuff, sizeof(strbuff), "unable to allocate an array for %s "
281 "file descriptors, would overflow size_t", strbuff1);
282 store_errmsg(strbuff, 0);
283 fprintf(stderr, "%s\n", msgbuff);
284 free(memchunk);
285 return -5;
286 }
287
288 /* allocate array for file descriptors */
289
290 do {
291 snprintf(strbuff, sizeof(strbuff), fmt, num_open.rlim_max);
292 fprintf(stderr, "allocating array for %s file descriptors\n", strbuff);
293 fd = malloc(sizeof(*fd) * (size_t)(num_open.rlim_max));
294 if(!fd) {
295 fprintf(stderr, "fd, malloc() failed\n");
296 num_open.rlim_max /= 2;
297 }
298 } while(num_open.rlim_max && !fd);
299 if(!fd) {
300 store_errmsg("fd, malloc() failed", ERRNO);
301 fprintf(stderr, "%s\n", msgbuff);
302 free(memchunk);
303 return -6;
304 }
305
306 /* initialize it to fight lazy allocation */
307
308 fprintf(stderr, "initializing fd array\n");
309
310 for(num_open.rlim_cur = 0;
311 num_open.rlim_cur < num_open.rlim_max;
312 num_open.rlim_cur++)
313 fd[num_open.rlim_cur] = -1;
314
315 snprintf(strbuff, sizeof(strbuff), fmt, num_open.rlim_max);
316 fprintf(stderr, "trying to open %s file descriptors\n", strbuff);
317
318 /* open a dummy descriptor */
319
320 fd[0] = open(DEV_NULL, O_RDONLY);
321 if(fd[0] < 0) {
322 snprintf(strbuff, sizeof(strbuff), "opening of %s failed", DEV_NULL);
323 store_errmsg(strbuff, ERRNO);
324 fprintf(stderr, "%s\n", msgbuff);
325 free(fd);
326 fd = NULL;
327 free(memchunk);
328 return -7;
329 }
330
331 /* create a bunch of file descriptors */
332
333 for(num_open.rlim_cur = 1;
334 num_open.rlim_cur < num_open.rlim_max;
335 num_open.rlim_cur++) {
336
337 fd[num_open.rlim_cur] = dup(fd[0]);
338
339 if(fd[num_open.rlim_cur] < 0) {
340
341 fd[num_open.rlim_cur] = -1;
342
343 snprintf(strbuff1, sizeof(strbuff1), fmt, num_open.rlim_cur);
344 snprintf(strbuff, sizeof(strbuff), "dup() attempt %s failed", strbuff1);
345 fprintf(stderr, "%s\n", strbuff);
346
347 snprintf(strbuff1, sizeof(strbuff1), fmt, num_open.rlim_cur);
348 snprintf(strbuff, sizeof(strbuff), "fds system limit seems close to %s",
349 strbuff1);
350 fprintf(stderr, "%s\n", strbuff);
351
352 num_open.rlim_max = num_open.rlim_cur - SAFETY_MARGIN;
353
354 num_open.rlim_cur -= num_open.rlim_max;
355 snprintf(strbuff1, sizeof(strbuff1), fmt, num_open.rlim_cur);
356 snprintf(strbuff, sizeof(strbuff), "closing %s file descriptors",
357 strbuff1);
358 fprintf(stderr, "%s\n", strbuff);
359
360 for(num_open.rlim_cur = num_open.rlim_max;
361 fd[num_open.rlim_cur] >= 0;
362 num_open.rlim_cur++) {
363 close(fd[num_open.rlim_cur]);
364 fd[num_open.rlim_cur] = -1;
365 }
366
367 snprintf(strbuff, sizeof(strbuff), fmt, num_open.rlim_max);
368 fprintf(stderr, "shrinking array for %s file descriptors\n", strbuff);
369
370 /* we don't care if we can't shrink it */
371
372 tmpfd = realloc(fd, sizeof(*fd) * (size_t)(num_open.rlim_max));
373 if(tmpfd) {
374 fd = tmpfd;
375 tmpfd = NULL;
376 }
377
378 break;
379
380 }
381
382 }
383
384 snprintf(strbuff, sizeof(strbuff), fmt, num_open.rlim_max);
385 fprintf(stderr, "%s file descriptors open\n", strbuff);
386
387#if !defined(HAVE_POLL_FINE) && \
388 !defined(USE_WINSOCK) && \
389 !defined(TPF)
390
391 /*
392 * when using select() instead of poll() we cannot test
393 * libcurl functionality with a socket number equal or
394 * greater than FD_SETSIZE. In any case, macro VERIFY_SOCK
395 * in lib/select.c enforces this check and protects libcurl
396 * from a possible crash. The effect of this protection
397 * is that test 537 will always fail, since the actual
398 * call to select() never takes place. We skip test 537
399 * with an indication that select limit would be exceeded.
400 */
401
402 num_open.rlim_cur = FD_SETSIZE - SAFETY_MARGIN;
403 if(num_open.rlim_max > num_open.rlim_cur) {
404 snprintf(strbuff, sizeof(strbuff), "select limit is FD_SETSIZE %d",
405 FD_SETSIZE);
406 store_errmsg(strbuff, 0);
407 fprintf(stderr, "%s\n", msgbuff);
408 close_file_descriptors();
409 free(memchunk);
410 return -8;
411 }
412
413 num_open.rlim_cur = FD_SETSIZE - SAFETY_MARGIN;
414 for(rl.rlim_cur = 0;
415 rl.rlim_cur < num_open.rlim_max;
416 rl.rlim_cur++) {
417 if((fd[rl.rlim_cur] > 0) &&
418 ((unsigned int)fd[rl.rlim_cur] > num_open.rlim_cur)) {
419 snprintf(strbuff, sizeof(strbuff), "select limit is FD_SETSIZE %d",
420 FD_SETSIZE);
421 store_errmsg(strbuff, 0);
422 fprintf(stderr, "%s\n", msgbuff);
423 close_file_descriptors();
424 free(memchunk);
425 return -9;
426 }
427 }
428
429#endif /* using a FD_SETSIZE bound select() */
430
431 /*
432 * Old or 'backwards compatible' implementations of stdio do not allow
433 * handling of streams with an underlying file descriptor number greater
434 * than 255, even when allowing high numbered file descriptors for sockets.
435 * At this point we have a big number of file descriptors which have been
436 * opened using dup(), so lets test the stdio implementation and discover
437 * if it is capable of fopen()ing some additional files.
438 */
439
440 if(!fopen_works()) {
441 snprintf(strbuff1, sizeof(strbuff1), fmt, num_open.rlim_max);
442 snprintf(strbuff, sizeof(strbuff), "fopen fails with %s fds open",
443 strbuff1);
444 fprintf(stderr, "%s\n", msgbuff);
445 snprintf(strbuff, sizeof(strbuff), "fopen fails with lots of fds open");
446 store_errmsg(strbuff, 0);
447 close_file_descriptors();
448 free(memchunk);
449 return -10;
450 }
451
452 /* free the chunk of memory we were reserving so that it
453 becomes becomes available to the test */
454
455 free(memchunk);
456
457 /* close file descriptors unless instructed to keep them */
458
459 if(!keep_open) {
460 close_file_descriptors();
461 }
462
463 return 0;
464}
465
466int test(char *URL)
467{
468 CURLcode res;
469 CURL *curl;
470
471 if(!strcmp(URL, "check")) {
472 /* used by the test script to ask if we can run this test or not */
473 if(rlimit(FALSE)) {
474 fprintf(stdout, "rlimit problem: %s\n", msgbuff);
475 return 1;
476 }
477 return 0; /* sure, run this! */
478 }
479
480 if(rlimit(TRUE)) {
481 /* failure */
482 return TEST_ERR_MAJOR_BAD;
483 }
484
485 /* run the test with the bunch of open file descriptors
486 and close them all once the test is over */
487
488 if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
489 fprintf(stderr, "curl_global_init() failed\n");
490 close_file_descriptors();
491 return TEST_ERR_MAJOR_BAD;
492 }
493
494 curl = curl_easy_init();
495 if(!curl) {
496 fprintf(stderr, "curl_easy_init() failed\n");
497 close_file_descriptors();
498 curl_global_cleanup();
499 return TEST_ERR_MAJOR_BAD;
500 }
501
502 test_setopt(curl, CURLOPT_URL, URL);
503 test_setopt(curl, CURLOPT_HEADER, 1L);
504
505 res = curl_easy_perform(curl);
506
507test_cleanup:
508
509 close_file_descriptors();
510 curl_easy_cleanup(curl);
511 curl_global_cleanup();
512
513 return (int)res;
514}
515
516#else /* defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) */
517
518int test(char *URL)
519{
520 (void)URL;
521 printf("system lacks necessary system function(s)");
522 return 1; /* skip test */
523}
524
525#endif /* defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) */