Coverage Report

Created: 2026-06-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/random.c
Line
Count
Source
1
/*
2
% Copyright (C) 2009-2025 GraphicsMagick Group
3
%
4
% This program is covered by multiple licenses, which are described in
5
% Copyright.txt. You should have received a copy of Copyright.txt with this
6
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
7
%
8
% Random number generator.
9
%
10
% Currently based on George Marsaglia's multiply-with-carry generator.
11
% This is a k=2 generator with a period >2^60.
12
%
13
*/
14
15
#include "magick/studio.h"
16
#include "magick/random.h"
17
#include "magick/semaphore.h"
18
#include "magick/tsd.h"
19
#include "magick/utility.h"
20
21
#if defined(HAVE_PTHREAD)
22
#  define USE_POSIX_THREADS 1
23
#elif defined(MSWINDOWS)
24
#  define USE_WIN32_THREADS 1
25
#endif
26
27
#if defined(USE_POSIX_THREADS)
28
#  include <pthread.h>
29
#endif
30
#if defined(USE_WIN32_THREADS)
31
#  include <windows.h>
32
#endif
33
#if defined(MSWINDOWS) && HAVE_CRYPTGENRANDOM && HAVE_WINCRYPT_H
34
#  include <wincrypt.h>
35
#endif
36
37

38
static MagickBool initialized = MagickFalse;
39
static SemaphoreInfo *semaphore = (SemaphoreInfo *) 0;
40
static MagickTsdKey_t kernel_key = (MagickTsdKey_t) 0;
41
42
/*
43
  Acquire the default random number kernel.  Memory is owned by
44
  library and should not be freed.
45
*/
46
MagickExport MagickRandomKernel* AcquireMagickRandomKernel(void)
47
3.95M
{
48
3.95M
  MagickRandomKernel
49
3.95M
    *kernel;
50
51
3.95M
  if (!initialized)
52
0
    InitializeMagickRandomGenerator();
53
54
3.95M
  kernel=(MagickRandomKernel *) MagickTsdGetSpecific(kernel_key);
55
3.95M
  if (kernel == (MagickRandomKernel *) NULL)
56
19
    {
57
19
      kernel=MagickAllocateAlignedMemory(MagickRandomKernel *,
58
19
                                         MAGICK_CACHE_LINE_SIZE,
59
19
                                         sizeof(MagickRandomKernel));
60
19
      if (kernel == (MagickRandomKernel *) NULL)
61
0
        MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
62
19
                          UnableToAllocateRandomKernel);
63
19
      InitializeMagickRandomKernel(kernel);
64
19
      MagickTsdSetSpecific(kernel_key,(const void *) kernel);
65
19
    }
66
67
3.95M
  return kernel;
68
3.95M
}
69
70
/*
71
  Initialize the random number generator system.
72
*/
73
void InitializeMagickRandomGenerator()
74
254
{
75
254
  assert(semaphore == (SemaphoreInfo *) NULL);
76
254
  semaphore=AllocateSemaphoreInfo();
77
78
254
  if (!initialized)
79
254
    {
80
254
      MagickTsdKeyCreate2(&kernel_key,MagickFreeAligned);
81
254
      initialized=MagickTrue;
82
254
    }
83
254
}
84
85
/*
86
  Destroy the random number generator system.
87
*/
88
void DestroyMagickRandomGenerator()
89
0
{
90
0
  if (initialized)
91
0
    {
92
0
      MagickRandomKernel
93
0
        *kernel;
94
95
      /* FIXME: This only frees memory associated with one thread */
96
0
      kernel=(MagickRandomKernel *) MagickTsdGetSpecific(kernel_key);
97
0
      MagickFreeAlignedMemory(kernel);
98
0
      (void) MagickTsdSetSpecific(kernel_key,kernel);
99
0
      MagickTsdKeyDelete(kernel_key);
100
0
    }
101
0
  kernel_key=(MagickTsdKey_t) 0;
102
0
  initialized=MagickFalse;
103
0
  DestroySemaphoreInfo(&semaphore);
104
0
}
105
106
/*
107
  Initialize the random kernel with suitable entropy
108
*/
109
MagickExport void
110
InitializeMagickRandomKernel(MagickRandomKernel *kernel)
111
19
{
112
19
  kernel->z = 0U;
113
19
  kernel->w = 0U;
114
19
#if defined(POSIX) && HAVE_DEV_URANDOM
115
  /*
116
    Read from /dev/urandom
117
118
    MinGW's MSYS emulates /dev/random in its shell so a configure
119
      test for it produces an invalid result.
120
  */
121
19
  {
122
19
    MagickBool
123
19
      done = MagickFalse;
124
125
19
    int
126
19
      file;
127
128
19
    if ((file=open("/dev/urandom",O_RDONLY | O_BINARY)) != -1)
129
19
      {
130
19
        if (read(file,(void *) kernel,sizeof(*kernel)) == sizeof(*kernel))
131
19
          done=MagickTrue;
132
19
        (void) close(file);
133
19
      }
134
19
    if (!done)
135
0
      MagickFatalError(ResourceLimitFatalError,UnableToObtainRandomEntropy,
136
19
                       NULL);
137
19
  }
138
#elif defined(MSWINDOWS) && HAVE_CRYPTGENRANDOM
139
  /*
140
    Use Windows advapi32.lib CryptGenRandom
141
142
    Is claimed to be supported under Windows XP
143
  */
144
  {
145
    MagickBool
146
      done = MagickFalse;
147
148
    HCRYPTPROV
149
      hProvider = 0;
150
151
    if (CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL,
152
                             CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
153
      {
154
        /* Returns zero (FALSE) on failure */
155
        if (CryptGenRandom(hProvider, (DWORD) sizeof(*kernel), (BYTE *) kernel))
156
          done=MagickTrue;
157
        (void) CryptReleaseContext(hProvider, 0);
158
      }
159
    if (!done)
160
      MagickFatalError(ResourceLimitFatalError,UnableToObtainRandomEntropy,
161
                       NULL);
162
  }
163
#else
164
  /*
165
    Fallback implementation
166
  */
167
168
  /*
169
    Initial time of day.
170
  */
171
  kernel->z ^= (magick_uint32_t) time(0);
172
173
  /*
174
    Let's hash with the address of kernel as well.
175
  */
176
  kernel->z ^= (magick_uint32_t) ((magick_uintptr_t) kernel & 4294967295UL);
177
178
 /*
179
    Process ID
180
  */
181
  kernel->w ^= (magick_uint32_t) getpid();
182
183
  /*
184
    Thread ID
185
  */
186
#if defined(USE_POSIX_THREADS)
187
  {
188
    union
189
    {
190
      pthread_t thread_id;
191
      magick_uint32_t intval;
192
    } pthread_union;
193
194
    pthread_union.intval=0U;
195
    pthread_union.thread_id=pthread_self();
196
    kernel->w ^= pthread_union.intval;
197
  }
198
#elif defined(USE_WIN32_THREADS)
199
  kernel->w ^= ((magick_uint32_t) GetCurrentThreadId());
200
#endif
201
202
  /*
203
    It is quite likely that multiple threads will invoke this function
204
    during the same second so we also tap into the default random
205
    number generator to help produce a more random seed.
206
  */
207
  kernel->w ^= (magick_uint32_t) rand();
208
#endif
209
19
}
210
211
/*
212
  Generate a random integer value
213
*/
214
MagickExport magick_uint32_t MagickRandomInteger(void)
215
3.95M
{
216
3.95M
  MagickRandomKernel
217
3.95M
    *kernel;
218
219
3.95M
  kernel=AcquireMagickRandomKernel();
220
3.95M
  return MagickRandomIntegerInlined(kernel);
221
3.95M
}
222
223
/*
224
  Generate a random double value
225
*/
226
MagickExport double MagickRandomReal(void)
227
0
{
228
0
  MagickRandomKernel
229
0
    *kernel;
230
231
0
  kernel=AcquireMagickRandomKernel();
232
0
  return MagickRandomRealInlined(kernel);
233
0
}