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