-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `getrandom(2)` syscall was added in 2017 and at the time we couldn't expect the glibc 2.25 to be widely available, but we're in 2024 now, and even Ubuntu 18.04 LTS that is now EOL had a compatible glibc release (2.27). I assume musl-libc also added the symbol at the same time. We can simplify the implementation to assume `getrandom` is available, which avoids the initial check, initialization and fallback to urandom. We still fallback to urandom at compile time when targeting android api level < 28 (we support 24+). An issue is that executables will now expect glibc 2.25+ (for example), though the interpreter already did. We also expect kernel 2.6.18 to be compatible, but `getrandom` was added in 3.17 which means it depends on how the libc symbol is implemented —does it fallback to urandom, does it fail? Related to #15034. Co-authored-by: Johannes Müller <straightshoota@gmail.com>
- Loading branch information
1 parent
b3a052c
commit 8b4fe41
Showing
12 changed files
with
68 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,39 @@ | ||
{% skip_file unless flag?(:linux) %} | ||
|
||
require "c/unistd" | ||
require "./syscall" | ||
|
||
{% if flag?(:interpreted) %} | ||
lib LibC | ||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : LibC::SSizeT | ||
end | ||
|
||
module Crystal::System::Syscall | ||
GRND_NONBLOCK = 1u32 | ||
|
||
# TODO: Implement syscall for interpreter | ||
def self.getrandom(buf : UInt8*, buflen : LibC::SizeT, flags : UInt32) : LibC::SSizeT | ||
# the syscall returns the negative of errno directly, the C function | ||
# doesn't, so we mimic the syscall behavior | ||
read_bytes = LibC.getrandom(buf, buflen, flags) | ||
read_bytes >= 0 ? read_bytes : LibC::SSizeT.new(-Errno.value.value) | ||
end | ||
end | ||
{% end %} | ||
require "c/sys/random" | ||
|
||
module Crystal::System::Random | ||
@@initialized = false | ||
@@getrandom_available = false | ||
@@urandom : ::File? | ||
|
||
private def self.init | ||
@@initialized = true | ||
|
||
if has_sys_getrandom | ||
@@getrandom_available = true | ||
else | ||
urandom = ::File.open("/dev/urandom", "r") | ||
return unless urandom.info.type.character_device? | ||
|
||
urandom.close_on_exec = true | ||
urandom.read_buffering = false # don't buffer bytes | ||
@@urandom = urandom | ||
end | ||
end | ||
|
||
private def self.has_sys_getrandom | ||
sys_getrandom(Bytes.new(16)) | ||
true | ||
rescue | ||
false | ||
end | ||
|
||
# Reads n random bytes using the Linux `getrandom(2)` syscall. | ||
def self.random_bytes(buf : Bytes) : Nil | ||
init unless @@initialized | ||
|
||
if @@getrandom_available | ||
getrandom(buf) | ||
elsif urandom = @@urandom | ||
urandom.read_fully(buf) | ||
else | ||
raise "Failed to access secure source to generate random bytes!" | ||
end | ||
def self.random_bytes(buffer : Bytes) : Nil | ||
getrandom(buffer) | ||
end | ||
|
||
def self.next_u : UInt8 | ||
init unless @@initialized | ||
|
||
if @@getrandom_available | ||
buf = uninitialized UInt8 | ||
getrandom(pointerof(buf).to_slice(1)) | ||
buf | ||
elsif urandom = @@urandom | ||
urandom.read_byte.not_nil! | ||
else | ||
raise "Failed to access secure source to generate random bytes!" | ||
end | ||
buffer = uninitialized UInt8 | ||
getrandom(pointerof(buffer).to_slice(1)) | ||
buffer | ||
end | ||
|
||
# Reads n random bytes using the Linux `getrandom(2)` syscall. | ||
private def self.getrandom(buf) | ||
private def self.getrandom(buffer) | ||
# getrandom(2) may only read up to 256 bytes at once without being | ||
# interrupted or returning early | ||
chunk_size = 256 | ||
|
||
while buf.size > 0 | ||
if buf.size < chunk_size | ||
chunk_size = buf.size | ||
end | ||
while buffer.size > 0 | ||
read_bytes = 0 | ||
|
||
read_bytes = sys_getrandom(buf[0, chunk_size]) | ||
loop do | ||
# pass GRND_NONBLOCK flag so that it fails with EAGAIN if the requested | ||
# entropy was not available | ||
read_bytes = LibC.getrandom(buffer, buffer.size.clamp(..chunk_size), LibC::GRND_NONBLOCK) | ||
break unless read_bytes == -1 | ||
|
||
buf += read_bytes | ||
end | ||
end | ||
err = Errno.value | ||
raise RuntimeError.from_os_error("getrandom", err) unless err.in?(Errno::EINTR, Errno::EAGAIN) | ||
|
||
# Low-level wrapper for the `getrandom(2)` syscall, returns the number of | ||
# bytes read or the errno as a negative number if an error occurred (or the | ||
# syscall isn't available). The GRND_NONBLOCK=1 flag is passed as last argument, | ||
# so that it returns -EAGAIN if the requested entropy was not available. | ||
# | ||
# We use the kernel syscall instead of the `getrandom` C function so any | ||
# binary compiled for Linux will always use getrandom if the kernel is 3.17+ | ||
# and silently fallback to read from /dev/urandom if not (so it's more | ||
# portable). | ||
private def self.sys_getrandom(buf : Bytes) | ||
loop do | ||
read_bytes = Syscall.getrandom(buf.to_unsafe, LibC::SizeT.new(buf.size), Syscall::GRND_NONBLOCK) | ||
if read_bytes < 0 | ||
err = Errno.new(-read_bytes.to_i) | ||
if err.in?(Errno::EINTR, Errno::EAGAIN) | ||
::Fiber.yield | ||
else | ||
raise RuntimeError.from_os_error("getrandom", err) | ||
end | ||
else | ||
return read_bytes | ||
::Fiber.yield | ||
end | ||
|
||
buffer += read_bytes | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
lib LibC | ||
{% if ANDROID_API >= 28 %} | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
{% end %} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lib LibC | ||
GRND_NONBLOCK = 1_u32 | ||
|
||
fun getrandom(buf : Void*, buflen : SizeT, flags : UInt32) : SSizeT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters