quickjs-tart

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

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