quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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) */