/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 | } |