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