LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson/libjson - random_seed.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 10 38 26.3 %
Date: 2024-05-06 22:33:47 Functions: 2 4 50.0 %

          Line data    Source code
       1             : /*
       2             :  * random_seed.c
       3             :  *
       4             :  * Copyright (c) 2013 Metaparadigm Pte. Ltd.
       5             :  * Michael Clark <michael@metaparadigm.com>
       6             :  *
       7             :  * This library is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the MIT license. See COPYING for details.
       9             :  *
      10             :  */
      11             : 
      12             : #include "random_seed.h"
      13             : #include "config.h"
      14             : #include "strerror_override.h"
      15             : #include <stdio.h>
      16             : #include <stdlib.h>
      17             : #ifdef HAVE_BSD_STDLIB_H
      18             : #include <bsd/stdlib.h>
      19             : #endif
      20             : 
      21             : #define DEBUG_SEED(s)
      22             : 
      23             : #if defined(__APPLE__) || defined(__unix__) || defined(__linux__)
      24             : #define HAVE_DEV_RANDOM 1
      25             : #endif
      26             : 
      27             : #ifdef HAVE_ARC4RANDOM
      28             : #undef HAVE_GETRANDOM
      29             : #undef HAVE_DEV_RANDOM
      30             : #undef HAVE_CRYPTGENRANDOM
      31             : #endif
      32             : 
      33             : #if defined ENABLE_RDRAND
      34             : 
      35             : /* cpuid */
      36             : 
      37             : #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
      38             : #define HAS_X86_CPUID 1
      39             : 
      40             : static void do_cpuid(int regs[], int h)
      41             : {
      42             :   /* clang-format off */
      43             :     __asm__ __volatile__("cpuid"
      44             :                          : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
      45             :                          : "a"(h));
      46             :   /* clang-format on */
      47             : }
      48             : 
      49             : #elif defined _MSC_VER
      50             : 
      51             : #define HAS_X86_CPUID 1
      52             : #define do_cpuid __cpuid
      53             : 
      54             : #endif
      55             : 
      56             : /* has_rdrand */
      57             : 
      58             : #if HAS_X86_CPUID
      59             : 
      60             : static int get_rdrand_seed(void);
      61             : 
      62             : /* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */
      63             : static int _has_rdrand = -1;
      64             : 
      65             : static int has_rdrand(void)
      66             : {
      67             :   if (_has_rdrand != -1)
      68             :   {
      69             :     return _has_rdrand;
      70             :   }
      71             : 
      72             :   /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
      73             :   int regs[4];
      74             :   do_cpuid(regs, 1);
      75             :   if (!(regs[2] & (1 << 30)))
      76             :   {
      77             :     _has_rdrand = 0;
      78             :     return 0;
      79             :   }
      80             : 
      81             :   /*
      82             :    * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
      83             :    * unconditionally. To avoid locking up later, test RDRAND here. If over
      84             :    * 3 trials RDRAND has returned the same value, declare it broken.
      85             :    * Example CPUs are AMD Ryzen 3000 series
      86             :    * and much older AMD APUs, such as the E1-1500
      87             :    * https://github.com/systemd/systemd/issues/11810
      88             :    * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend
      89             :    */
      90             :   _has_rdrand = 0;
      91             :   int prev = get_rdrand_seed();
      92             :   for (int i = 0; i < 3; i++)
      93             :   {
      94             :     int temp = get_rdrand_seed();
      95             :     if (temp != prev)
      96             :     {
      97             :       _has_rdrand = 1;
      98             :       break;
      99             :     }
     100             : 
     101             :     prev = temp;
     102             :   }
     103             : 
     104             :   return _has_rdrand;
     105             : }
     106             : 
     107             : #endif
     108             : 
     109             : /* get_rdrand_seed - GCC x86 and X64 */
     110             : 
     111             : #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
     112             : 
     113             : #define HAVE_RDRAND 1
     114             : 
     115             : static int get_rdrand_seed(void)
     116             : {
     117             :   DEBUG_SEED("get_rdrand_seed");
     118             :   int _eax;
     119             :   /* rdrand eax */
     120             :   /* clang-format off */
     121             :   __asm__ __volatile__("1: .byte 0x0F\n"
     122             :                        "   .byte 0xC7\n"
     123             :                        "   .byte 0xF0\n"
     124             :                        "   jnc 1b;\n"
     125             :                        : "=a" (_eax));
     126             :   /* clang-format on */
     127             :   return _eax;
     128             : }
     129             : 
     130             : #endif
     131             : 
     132             : #if defined _MSC_VER
     133             : 
     134             : #if _MSC_VER >= 1700
     135             : #define HAVE_RDRAND 1
     136             : 
     137             : /* get_rdrand_seed - Visual Studio 2012 and above */
     138             : 
     139             : static int get_rdrand_seed(void)
     140             : {
     141             :   DEBUG_SEED("get_rdrand_seed");
     142             :   int r;
     143             :   while (_rdrand32_step(&r) == 0)
     144             :     ;
     145             :   return r;
     146             : }
     147             : 
     148             : #elif defined _M_IX86
     149             : #define HAVE_RDRAND 1
     150             : 
     151             : /* get_rdrand_seed - Visual Studio 2010 and below - x86 only */
     152             : 
     153             : /* clang-format off */
     154             : static int get_rdrand_seed(void)
     155             : {
     156             :   DEBUG_SEED("get_rdrand_seed");
     157             :   int _eax;
     158             : retry:
     159             :   /* rdrand eax */
     160             :   __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
     161             :   __asm jnc retry
     162             :   __asm mov _eax, eax
     163             :   return _eax;
     164             : }
     165             : /* clang-format on */
     166             : 
     167             : #endif
     168             : #endif
     169             : 
     170             : #endif /* defined ENABLE_RDRAND */
     171             : 
     172             : #ifdef HAVE_GETRANDOM
     173             : 
     174             : #include <stdlib.h>
     175             : #ifdef HAVE_SYS_RANDOM_H
     176             : #include <sys/random.h>
     177             : #endif
     178             : 
     179         136 : static int get_getrandom_seed(int *seed)
     180             : {
     181             :   DEBUG_SEED("get_getrandom_seed");
     182             : 
     183             :   ssize_t ret;
     184             : 
     185             :   do
     186             :   {
     187         136 :     ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK);
     188         136 :   } while ((ret == -1) && (errno == EINTR));
     189             : 
     190         136 :   if (ret == -1)
     191             :   {
     192           0 :     if (errno == ENOSYS) /* syscall not available in kernel */
     193           0 :       return -1;
     194           0 :     if (errno == EAGAIN) /* entropy not yet initialized */
     195           0 :       return -1;
     196             : 
     197           0 :     fprintf(stderr, "error from getrandom(): %s", strerror(errno));
     198           0 :     return -1;
     199             :   }
     200             : 
     201         136 :   if (ret != sizeof(*seed))
     202           0 :     return -1;
     203             : 
     204         136 :   return 0;
     205             : }
     206             : #endif /* defined HAVE_GETRANDOM */
     207             : 
     208             : /* get_dev_random_seed */
     209             : 
     210             : #ifdef HAVE_DEV_RANDOM
     211             : 
     212             : #include <fcntl.h>
     213             : #include <string.h>
     214             : #if HAVE_UNISTD_H
     215             : #include <unistd.h>
     216             : #endif /* HAVE_UNISTD_H */
     217             : #include <stdlib.h>
     218             : #include <sys/stat.h>
     219             : 
     220             : static const char *dev_random_file = "/dev/urandom";
     221             : 
     222           0 : static int get_dev_random_seed(int *seed)
     223             : {
     224             :   DEBUG_SEED("get_dev_random_seed");
     225             : 
     226             :   struct stat buf;
     227           0 :   if (stat(dev_random_file, &buf))
     228           0 :     return -1;
     229           0 :   if ((buf.st_mode & S_IFCHR) == 0)
     230           0 :     return -1;
     231             : 
     232             :   /* coverity[toctou] */
     233           0 :   int fd = open(dev_random_file, O_RDONLY);
     234           0 :   if (fd < 0)
     235             :   {
     236           0 :     fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
     237           0 :     return -1;
     238             :   }
     239             : 
     240           0 :   ssize_t nread = read(fd, seed, sizeof(*seed));
     241             : 
     242           0 :   close(fd);
     243             : 
     244           0 :   if (nread != sizeof(*seed))
     245             :   {
     246           0 :     fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
     247           0 :     return -1;
     248             :   }
     249             : 
     250           0 :   return 0;
     251             : }
     252             : 
     253             : #endif
     254             : 
     255             : /* get_cryptgenrandom_seed */
     256             : 
     257             : #ifdef WIN32
     258             : 
     259             : #define HAVE_CRYPTGENRANDOM 1
     260             : 
     261             : /* clang-format off */
     262             : #include <windows.h>
     263             : 
     264             : /* Caution: these blank lines must remain so clang-format doesn't reorder
     265             :    includes to put windows.h after wincrypt.h */
     266             : 
     267             : #include <wincrypt.h>
     268             : /* clang-format on */
     269             : #ifndef __GNUC__
     270             : #pragma comment(lib, "advapi32.lib")
     271             : #endif
     272             : 
     273             : static int get_cryptgenrandom_seed(int *seed)
     274             : {
     275             :   HCRYPTPROV hProvider = 0;
     276             :   DWORD dwFlags = CRYPT_VERIFYCONTEXT;
     277             : 
     278             :   DEBUG_SEED("get_cryptgenrandom_seed");
     279             : 
     280             :   /* WinNT 4 and Win98 do no support CRYPT_SILENT */
     281             :   // E.Rouault: commented out to avoid warning C4996: 'GetVersion': was declared deprecated
     282             :   //if (LOBYTE(LOWORD(GetVersion())) > 4)
     283             :   dwFlags |= CRYPT_SILENT;
     284             : 
     285             :   if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags))
     286             :   {
     287             :     fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError());
     288             :     return -1;
     289             :   }
     290             :   else
     291             :   {
     292             :     BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed);
     293             :     CryptReleaseContext(hProvider, 0);
     294             :     if (!ret)
     295             :     {
     296             :       fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError());
     297             :       return -1;
     298             :     }
     299             :   }
     300             : 
     301             :   return 0;
     302             : }
     303             : 
     304             : #endif
     305             : 
     306             : /* get_time_seed */
     307             : 
     308             : #ifndef HAVE_ARC4RANDOM
     309             : #include <time.h>
     310             : 
     311           0 : static int get_time_seed(void)
     312             : {
     313             :   DEBUG_SEED("get_time_seed");
     314             : 
     315             :   /* coverity[store_truncates_time_t] */
     316           0 :   return (unsigned)time(NULL) * 433494437;
     317             : }
     318             : #endif
     319             : 
     320             : /* json_c_get_random_seed */
     321             : 
     322         136 : int json_c_get_random_seed(void)
     323             : {
     324             : #ifdef OVERRIDE_GET_RANDOM_SEED
     325             :   OVERRIDE_GET_RANDOM_SEED;
     326             : #endif
     327             : #if defined HAVE_RDRAND && HAVE_RDRAND
     328             :   if (has_rdrand())
     329             :     return get_rdrand_seed();
     330             : #endif
     331             : #ifdef HAVE_ARC4RANDOM
     332             :   /* arc4random never fails, so use it if it's available */
     333             :   return arc4random();
     334             : #else
     335             : #ifdef HAVE_GETRANDOM
     336             :   {
     337         136 :     int seed = 0;
     338         136 :     if (get_getrandom_seed(&seed) == 0)
     339         136 :       return seed;
     340             :   }
     341             : #endif
     342             : #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM
     343             :   {
     344           0 :     int seed = 0;
     345           0 :     if (get_dev_random_seed(&seed) == 0)
     346           0 :       return seed;
     347             :   }
     348             : #endif
     349             : #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM
     350             :   {
     351             :     int seed = 0;
     352             :     if (get_cryptgenrandom_seed(&seed) == 0)
     353             :       return seed;
     354             :   }
     355             : #endif
     356           0 :   return get_time_seed();
     357             : #endif /* !HAVE_ARC4RANDOM */
     358             : }

Generated by: LCOV version 1.14