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