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