in csrc/aes_ctr_drbg.cpp [48:128]
bool drbg_getentropy(void *buffer, size_t length) {
#ifdef HAVE_GETENTROPY
int rv = getentropy(buffer, length);
if (likely(rv == 0) || errno != ENOSYS) {
// The getentropy call was available; return success if it
// gave us a successful return.
return !rv;
}
#endif
// Fallback path for when getentropy is unavailable
bool success = false;
int mutex_error = 0;
uint8_t *bufp = reinterpret_cast<uint8_t *>(buffer);
if (unlikely(mutex_error = pthread_mutex_lock(&getentropy_mutex))) {
errno = mutex_error;
perror("pthread_mutex_lock");
abort(); // mutex failure - don't even try to recover
}
if (unlikely(urandom_fd < 0)) {
if (O_CLOEXEC) {
// If O_CLOEXEC is undefined, make sure we go into the fallback path
// which will do a workaround using fcntl.
urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
}
if (unlikely(urandom_fd < 0)) {
if (errno == EINVAL || errno == ENOSYS) {
// We might be on an old kernel that doesn't support O_CLOEXEC.
// Try again and use fcntl instead. This is subject to an unavoidable
// race condition in which the process forks before we can set the CLOEXEC
// flag, which is why O_CLOEXEC was introduced in the first place.
int flags;
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd >= 0) {
flags = fcntl(F_GETFD, urandom_fd);
if (flags == -1) {
close(urandom_fd);
urandom_fd = -1;
} else {
if (-1 == fcntl(F_SETFD, urandom_fd, flags | FD_CLOEXEC)) {
close(urandom_fd);
urandom_fd = -1;
}
}
}
}
}
// If our workaround failed we'll still have urandom_fd = -1
if (unlikely(urandom_fd < 0)) {
goto out;
}
}
while (length) {
ssize_t rv = read(urandom_fd, bufp, length);
if (rv < 0) {
goto out;
}
length -= rv;
bufp += rv;
}
success = true;
out:
if (!success) {
secureZero(buffer, length);
}
if (unlikely(mutex_error = pthread_mutex_unlock(&getentropy_mutex))) {
errno = mutex_error;
perror("pthread_mutex_unlock");
abort(); // unexpected mutex failure - can't recover
}
return success;
}