• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2019, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <gtest/gtest.h>
16 #include <stdlib.h>
17 
18 #include <openssl/rand.h>
19 
20 #include "internal.h"
21 
22 
23 #if defined(OPENSSL_X86_64) && defined(OPENSSL_LINUX) && \
24     !defined(BORINGSSL_SHARED_LIBRARY) &&                \
25     !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
26 
27 #include <linux/random.h>
28 #include <sys/ptrace.h>
29 #include <sys/syscall.h>
30 #include <sys/user.h>
31 
32 #if defined(OPENSSL_NO_ASM)
have_rdrand()33 static int have_rdrand() { return 0; }
34 #endif
35 
36 // This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to
37 // simulate the absence of RDRAND of machines that have it.
38 
39 // Event represents a system call from urandom.c that is observed by the ptrace
40 // code in |GetTrace|.
41 struct Event {
42   enum class Syscall {
43     kGetRandom,
44     kOpen,
45     kUrandomRead,
46     kUrandomIoctl,
47     kAbort,
48   };
49 
EventEvent50   explicit Event(Syscall syscall) : type(syscall) {}
51 
operator ==Event52   bool operator==(const Event &other) const {
53     return type == other.type && length == other.length &&
54            flags == other.flags &&
55            ((filename == nullptr && other.filename == nullptr) ||
56             strcmp(filename, other.filename) == 0);
57   }
58 
GetRandomEvent59   static Event GetRandom(size_t length, unsigned flags) {
60     Event e(Syscall::kGetRandom);
61     e.length = length;
62     e.flags = flags;
63     return e;
64   }
65 
OpenEvent66   static Event Open(const char *filename) {
67     Event e(Syscall::kOpen);
68     e.filename = filename;
69     return e;
70   }
71 
UrandomReadEvent72   static Event UrandomRead(size_t length) {
73     Event e(Syscall::kUrandomRead);
74     e.length = length;
75     return e;
76   }
77 
UrandomIoctlEvent78   static Event UrandomIoctl() {
79     Event e(Syscall::kUrandomIoctl);
80     return e;
81   }
82 
AbortEvent83   static Event Abort() {
84     Event e(Syscall::kAbort);
85     return e;
86   }
87 
StringEvent88   std::string String() const {
89     char buf[256];
90 
91     switch (type) {
92       case Syscall::kGetRandom:
93         snprintf(buf, sizeof(buf), "getrandom(_, %zu, %d)", length, flags);
94         break;
95 
96       case Syscall::kOpen:
97         snprintf(buf, sizeof(buf), "open(%s, _)", filename);
98         break;
99 
100       case Syscall::kUrandomRead:
101         snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
102         break;
103 
104       case Syscall::kUrandomIoctl:
105         return "ioctl(urandom_fd, RNDGETENTCNT, _)";
106 
107       case Syscall::kAbort:
108         return "abort()";
109     }
110 
111     return std::string(buf);
112   }
113 
114   const Syscall type;
115   size_t length = 0;
116   unsigned flags = 0;
117   const char *filename = nullptr;
118 };
119 
ToString(const std::vector<Event> & trace)120 static std::string ToString(const std::vector<Event> &trace) {
121   std::string ret;
122   for (const auto &event : trace) {
123     if (!ret.empty()) {
124       ret += ", ";
125     }
126     ret += event.String();
127   }
128   return ret;
129 }
130 
131 // The following are flags to tell |GetTrace| to inject faults, using ptrace,
132 // into the entropy-related system calls.
133 
134 // getrandom gives |ENOSYS|.
135 static const unsigned NO_GETRANDOM = 1;
136 // opening /dev/urandom fails.
137 static const unsigned NO_URANDOM = 2;
138 // getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|.
139 static const unsigned GETRANDOM_NOT_READY = 4;
140 // The ioctl on urandom returns only 255 bits of entropy the first time that
141 // it's called.
142 static const unsigned URANDOM_NOT_READY = 8;
143 // getrandom gives |EINVAL| unless |NO_GETRANDOM| is set.
144 static const unsigned GETRANDOM_ERROR = 16;
145 // Reading from /dev/urandom gives |EINVAL|.
146 static const unsigned URANDOM_ERROR = 32;
147 static const unsigned NEXT_FLAG = 64;
148 
149 // GetTrace runs |thunk| in a forked process and observes the resulting system
150 // calls using ptrace. It simulates a variety of failures based on the contents
151 // of |flags| and records the observed events by appending to |out_trace|.
GetTrace(std::vector<Event> * out_trace,unsigned flags,std::function<void ()> thunk)152 static void GetTrace(std::vector<Event> *out_trace, unsigned flags,
153                      std::function<void()> thunk) {
154   const int child_pid = fork();
155   ASSERT_NE(-1, child_pid);
156 
157   if (child_pid == 0) {
158     // Child process
159     if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) {
160       perror("PTRACE_TRACEME");
161       _exit(1);
162     }
163     raise(SIGSTOP);
164     thunk();
165     _exit(0);
166   }
167 
168   // Parent process
169   int status;
170   ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
171   ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
172 
173   // Set options so that:
174   //   a) the child process is killed once this process dies.
175   //   b) System calls result in a WSTOPSIG value of (SIGTRAP | 0x80) rather
176   //      than just SIGTRAP. (This doesn't matter here, but it's recommended
177   //      practice so that it's distinct from the signal itself.)
178   ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, nullptr,
179                       PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD))
180       << strerror(errno);
181 
182   // urandom_fd tracks the file descriptor number for /dev/urandom in the child
183   // process, if it opens it.
184   int urandom_fd = -1;
185 
186   for (;;) {
187     // Advance the child to the next system call.
188     ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
189     ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
190 
191     // The child may have aborted rather than made a system call.
192     if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) {
193       out_trace->push_back(Event::Abort());
194       break;
195     }
196 
197     // Otherwise the only valid ptrace event is a system call stop.
198     ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
199 
200     struct user_regs_struct regs;
201     ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
202     const auto syscall_number = regs.orig_rax;
203 
204     bool is_opening_urandom = false;
205     bool is_urandom_ioctl = false;
206     uintptr_t ioctl_output_addr = 0;
207     // inject_error is zero to indicate that the system call should run
208     // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call
209     // should not run and that error should be injected on return.
210     int inject_error = 0;
211 
212     switch (syscall_number) {
213       case __NR_getrandom:
214         if (flags & NO_GETRANDOM) {
215           inject_error = -ENOSYS;
216         } else if (flags & GETRANDOM_ERROR) {
217           inject_error = -EINVAL;
218         } else if (flags & GETRANDOM_NOT_READY) {
219           if (regs.rdx & GRND_NONBLOCK) {
220             inject_error = -EAGAIN;
221           }
222         }
223         out_trace->push_back(
224             Event::GetRandom(/*length=*/regs.rsi, /*flags=*/regs.rdx));
225         break;
226 
227       case __NR_openat:
228       case __NR_open: {
229         // It's assumed that any arguments to open(2) are constants in read-only
230         // memory and thus the pointer in the child's context will also be a
231         // valid pointer in our address space.
232         const char *filename = reinterpret_cast<const char *>(
233             (syscall_number == __NR_openat) ? regs.rsi : regs.rdi);
234         out_trace->push_back(Event::Open(filename));
235         is_opening_urandom = strcmp(filename, "/dev/urandom") == 0;
236         if (is_opening_urandom && (flags & NO_URANDOM)) {
237           inject_error = -ENOENT;
238         }
239         break;
240       }
241 
242       case __NR_read: {
243         const int read_fd = regs.rdi;
244         if (urandom_fd >= 0 && urandom_fd == read_fd) {
245           out_trace->push_back(Event::UrandomRead(/*length=*/regs.rdx));
246           if (flags & URANDOM_ERROR) {
247             inject_error = -EINVAL;
248           }
249         }
250         break;
251       }
252 
253       case __NR_ioctl: {
254         const int ioctl_fd = regs.rdi;
255         if (urandom_fd >= 0 && ioctl_fd == urandom_fd &&
256             regs.rsi == RNDGETENTCNT) {
257           out_trace->push_back(Event::UrandomIoctl());
258           is_urandom_ioctl = true;
259           ioctl_output_addr = regs.rdx;
260         }
261       }
262     }
263 
264     if (inject_error) {
265       // Replace the system call number with -1 to cause the kernel to ignore
266       // the call. The -ENOSYS will be replaced later with the value of
267       // |inject_error|.
268       regs.orig_rax = -1;
269       ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs));
270     }
271 
272     ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
273     ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
274     // If the system call was exit/exit_group, the process may be terminated
275     // rather than have exited the system call.
276     if (WIFEXITED(status)) {
277       ASSERT_EQ(0, WEXITSTATUS(status));
278       return;
279     }
280 
281     // Otherwise the next state must be a system call exit stop. This is
282     // indistinguishable from a system call entry, we just have to keep track
283     // and know that these events happen in pairs.
284     ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
285 
286     if (inject_error) {
287       if (inject_error != -ENOSYS) {
288         ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
289         regs.rax = inject_error;
290         ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs));
291       }
292     } else if (is_opening_urandom) {
293       ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, &regs));
294       urandom_fd = regs.rax;
295     } else if (is_urandom_ioctl) {
296       // The result is the number of bits of entropy that the kernel currently
297       // believes that it has. urandom.c waits until 256 bits are ready.
298       int result = 256;
299 
300       // If we are simulating urandom not being ready then we have the ioctl
301       // indicate one too few bits of entropy the first time it's queried.
302       if (flags & URANDOM_NOT_READY) {
303         result--;
304         flags &= ~URANDOM_NOT_READY;
305       }
306 
307       // ptrace always works with ill-defined "words", which appear to be 64-bit
308       // on x86-64. Since the ioctl result is a 32-bit int, do a
309       // read-modify-write to inject the answer.
310       const uintptr_t aligned_addr = ioctl_output_addr & ~7;
311       const uintptr_t offset = ioctl_output_addr - aligned_addr;
312       union {
313         uint64_t word;
314         uint8_t bytes[8];
315       } u;
316       u.word = ptrace(PTRACE_PEEKDATA, child_pid,
317                       reinterpret_cast<void *>(aligned_addr), nullptr);
318       memcpy(&u.bytes[offset], &result, sizeof(result));
319       ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid,
320                           reinterpret_cast<void *>(aligned_addr),
321                           reinterpret_cast<void *>(u.word)));
322     }
323   }
324 }
325 
326 // TestFunction is the function that |GetTrace| is asked to trace.
TestFunction()327 static void TestFunction() {
328   uint8_t byte;
329   RAND_bytes(&byte, sizeof(byte));
330   RAND_bytes(&byte, sizeof(byte));
331 }
332 
333 // TestFunctionPRNGModel is a model of how the urandom.c code will behave when
334 // |TestFunction| is run. It should return the same trace of events that
335 // |GetTrace| will observe the real code making.
TestFunctionPRNGModel(unsigned flags)336 static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
337 #if defined(BORINGSSL_FIPS)
338   static const bool is_fips = true;
339 #else
340   static const bool is_fips = false;
341 #endif
342 
343   std::vector<Event> ret;
344   bool urandom_probed = false;
345   bool getrandom_ready = false;
346 
347   // Probe for getrandom support
348   ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
349   std::function<void()> wait_for_entropy;
350   std::function<bool(bool, size_t)> sysrand;
351 
352   if (flags & NO_GETRANDOM) {
353     ret.push_back(Event::Open("/dev/urandom"));
354     if (flags & NO_URANDOM) {
355       ret.push_back(Event::Abort());
356       return ret;
357     }
358 
359     wait_for_entropy = [&ret, &urandom_probed, flags] {
360       if (!is_fips || urandom_probed) {
361         return;
362       }
363 
364       // Probe urandom for entropy.
365       ret.push_back(Event::UrandomIoctl());
366       if (flags & URANDOM_NOT_READY) {
367         // If the first attempt doesn't report enough entropy, probe
368         // repeatedly until it does, which will happen with the second attempt.
369         ret.push_back(Event::UrandomIoctl());
370       }
371 
372       urandom_probed = true;
373     };
374 
375     sysrand = [&ret, &wait_for_entropy, flags](bool block, size_t len) {
376       if (block) {
377         wait_for_entropy();
378       }
379       ret.push_back(Event::UrandomRead(len));
380       if (flags & URANDOM_ERROR) {
381         ret.push_back(Event::Abort());
382         return false;
383       }
384       return true;
385     };
386   } else {
387     if (flags & GETRANDOM_ERROR) {
388       ret.push_back(Event::Abort());
389       return ret;
390     }
391 
392     getrandom_ready = (flags & GETRANDOM_NOT_READY) == 0;
393     wait_for_entropy = [&ret, &getrandom_ready] {
394       if (getrandom_ready) {
395         return;
396       }
397 
398       ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
399       ret.push_back(Event::GetRandom(1, 0));
400       getrandom_ready = true;
401     };
402     sysrand = [&ret, &wait_for_entropy](bool block, size_t len) {
403       if (block) {
404         wait_for_entropy();
405       }
406       ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK));
407       return true;
408     };
409   }
410 
411   const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (is_fips ? 10 : 1);
412   const size_t kAdditionalDataLength = 32;
413 
414   if (!have_rdrand()) {
415     if (!sysrand(true, kAdditionalDataLength) ||
416         // Initialise CRNGT.
417         (is_fips && !sysrand(true, 16)) ||
418         !sysrand(true, kSeedLength) ||
419         // Second entropy draw.
420         !sysrand(true, kAdditionalDataLength)) {
421       return ret;
422     }
423   } else {
424     // Opportuntistic entropy draw in FIPS mode because RDRAND was used.
425     // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking
426     // way.
427     if (!sysrand(!is_fips, CTR_DRBG_ENTROPY_LEN)) {
428       return ret;
429     }
430   }
431 
432   return ret;
433 }
434 
435 // Tests that |TestFunctionPRNGModel| is a correct model for the code in
436 // urandom.c, at least to the limits of the the |Event| type.
TEST(URandomTest,Test)437 TEST(URandomTest, Test) {
438   char buf[256];
439 
440 #define TRACE_FLAG(flag)                                         \
441   snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \
442   SCOPED_TRACE(buf);
443 
444   for (unsigned flags = 0; flags < NEXT_FLAG; flags++) {
445     TRACE_FLAG(NO_GETRANDOM);
446     TRACE_FLAG(NO_URANDOM);
447     TRACE_FLAG(GETRANDOM_NOT_READY);
448     TRACE_FLAG(URANDOM_NOT_READY);
449     TRACE_FLAG(GETRANDOM_ERROR);
450     TRACE_FLAG(URANDOM_ERROR);
451 
452     const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags);
453     std::vector<Event> actual_trace;
454     GetTrace(&actual_trace, flags, TestFunction);
455 
456     if (expected_trace != actual_trace) {
457       ADD_FAILURE() << "Expected: " << ToString(expected_trace)
458                     << "\nFound:    " << ToString(actual_trace);
459     }
460   }
461 }
462 
main(int argc,char ** argv)463 int main(int argc, char **argv) {
464   ::testing::InitGoogleTest(&argc, argv);
465   return RUN_ALL_TESTS();
466 }
467 
468 #else
469 
main(int argc,char ** argv)470 int main(int argc, char **argv) {
471   printf("PASS\n");
472   return 0;
473 }
474 
475 #endif  // X86_64 && LINUX && !SHARED_LIBRARY && !UNSAFE_DETERMINISTIC_MODE
476