Skip to content

Commit

Permalink
Fix Linux getrandom failure in interpreted code (#15035)
Browse files Browse the repository at this point in the history
Whereas the syscall returns the negative of the `Errno` value on failure, the `LibC` function returns -1 and then sets `Errno.value`. Crystal always assumes the former:

```cr
 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 
```

https://github.com/crystal-lang/crystal/blob/cde543a762d56324e1d1261154d767c84db1ebc1/src/crystal/system/unix/getrandom.cr#L105-L110

As `EPERM` equals 1 on Linux, _all_ failures are treated like `EPERM` in interpreted code, even though `EPERM` itself is not listed as an error for [`getrandom(2)`](https://man7.org/linux/man-pages/man2/getrandom.2.html), hence the "Operation not permitted". The same can probably happen on other Linux distros if you run out of entropy.
  • Loading branch information
HertzDevil authored Sep 26, 2024
1 parent 7c3bf20 commit d58ede5
Showing 1 changed file with 4 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/crystal/system/unix/getrandom.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ require "./syscall"

# TODO: Implement syscall for interpreter
def self.getrandom(buf : UInt8*, buflen : LibC::SizeT, flags : UInt32) : LibC::SSizeT
LibC.getrandom(buf, buflen, flags)
# 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 %}
Expand Down

0 comments on commit d58ede5

Please sign in to comment.