crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.46k stars 1.62k forks source link

getrandom.cr fails in Alpine under Docker (crystal 1.13.2) #15034

Closed jadonk closed 1 month ago

jadonk commented 1 month ago
$ docker run --rm -it mycrystal/crystal:latest
/ # crystal i
Crystal interpreter 1.13.2 (2024-08-29).
EXPERIMENTAL SOFTWARE: if you find a bug, please consider opening an issue in
https://github.com/crystal-lang/crystal/issues/new/
Unhandled exception: getrandom: Operation not permitted (RuntimeError)
  from raise|219|1|/usr/lib/crystal/core/raise.cr
  from sys_getrandom|109|11|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr
  from loop|143|5|/usr/lib/crystal/core/kernel.cr
  from sys_getrandom|102|5|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr
  from getrandom|86|20|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr
  from random_bytes|53|7|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr
  from /usr/lib/crystal/core/crystal/hasher.cr|83|3|/usr/lib/crystal/core/crystal/hasher.cr

Dockerfile:

FROM alpine:edge AS crystal
RUN echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/community' >>/etc/apk/repositories
RUN apk add --update --no-cache --force-overwrite \
  git \
  make \
  crystal@edge

I did test and confirm that running docker with --privileged does not impact the failure.

I found a duplicate at https://github.com/crystal-lang/crystal/issues/12967 and didn't understand why it was marked "completed".

It seems the error should not be musl-specific as I don't see how musl would change the arguements to the syscall. It looks instead that crystal might be depending on only certain errors and a failure mode of providing a bad address isn't properly handled.

strace:

readlink("/proc", 0x7ffc18262dd0, 4087) = -1 EINVAL (Invalid argument)
readlink("/proc/self", "26", 4092)      = 2
readlink("/proc/26", 0x7ffc18262dd0, 4092) = -1 EINVAL (Invalid argument)
readlink("/proc/26/exe", "/usr/bin/crystal", 4096) = 16
readlink("/usr", 0x7ffc18262dd0, 4084)  = -1 EINVAL (Invalid argument)
readlink("/usr/bin", 0x7ffc18262dd0, 4088) = -1 EINVAL (Invalid argument)
readlink("/usr/bin/crystal", 0x7ffc18262dd0, 4096) = -1 EINVAL (Invalid argument)
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbac532c000
munmap(0x7bbac532c000, 16384)           = 0
open("/dev/null", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 9
fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
fcntl(9, F_GETFL)                       = 0x8001 (flags O_WRONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 10
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
fcntl(10, F_GETFL)                      = 0x8001 (flags O_WRONLY|O_LARGEFILE)
pipe2([11, 12], O_CLOEXEC)              = 0
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_SETFL, O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 33
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
close(12)                               = 0
read(11, 0x7bbabd8f0000, 32768)         = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 11, {events=EPOLLIN, data={u32=11, u64=11}}) = 0
epoll_pwait(14, [{events=EPOLLHUP, data={u32=11, u64=11}}], 32, 4173, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 11, 0x7ffc18263b1c) = 0
read(11, "", 32768)                     = 0
close(11)                               = 0
close(8)                                = 0
close(9)                                = 0
close(10)                               = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=33, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
write(7, "\21\0\0\0", 4)                = 4
rt_sigreturn({mask=[]})                 = 0
epoll_pwait(14, [{events=EPOLLIN, data={u32=6, u64=6}}], 32, 4167, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 6, 0x7ffc18263bac) = 0
read(6, "\21\0\0\0", 32768)             = 4
read(6, 0x7bbac93ec000, 32768)          = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 6, {events=EPOLLIN, data={u32=6, u64=6}}) = 0
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 33
wait4(-1, 0x7bbac79fff74, WNOHANG, NULL) = -1 ECHILD (No child process)
open("/dev/null", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
pipe2([9, 10], O_CLOEXEC)               = 0
fcntl(9, F_GETFL)                       = 0 (flags O_RDONLY)
fcntl(9, F_GETFL)                       = 0 (flags O_RDONLY)
fcntl(9, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(10, F_GETFL)                      = 0x1 (flags O_WRONLY)
pipe2([11, 12], O_CLOEXEC)              = 0
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_SETFL, O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 34
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
close(12)                               = 0
read(11, 0x7bbabd8fa000, 32768)         = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 11, {events=EPOLLIN, data={u32=11, u64=11}}) = 0
epoll_pwait(14, [{events=EPOLLHUP, data={u32=11, u64=11}}], 32, 4162, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 11, 0x7ffc18263b1c) = 0
read(11, "", 32768)                     = 0
close(11)                               = 0
close(8)                                = 0
close(10)                               = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=34, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
write(7, "\21\0\0\0", 4)                = 4
rt_sigreturn({mask=[]})                 = 0
read(9, "-lpcre2-8\n", 32768)           = 10
read(9, "", 32768)                      = 0
epoll_pwait(14, [{events=EPOLLIN, data={u32=6, u64=6}}], 32, 4155, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 6, 0x7ffc18263bac) = 0
read(6, "\21\0\0\0", 32768)             = 4
read(6, 0x7bbac93ec000, 32768)          = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 6, {events=EPOLLIN, data={u32=6, u64=6}}) = 0
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 34
wait4(-1, 0x7bbac79fff74, WNOHANG, NULL) = -1 ECHILD (No child process)
close(9)                                = 0
open("/dev/null", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 9
fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
fcntl(9, F_GETFL)                       = 0x8001 (flags O_WRONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 10
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
fcntl(10, F_GETFL)                      = 0x8001 (flags O_WRONLY|O_LARGEFILE)
pipe2([11, 12], O_CLOEXEC)              = 0
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_SETFL, O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 35
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
close(12)                               = 0
read(11, 0x7bbabd906000, 32768)         = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 11, {events=EPOLLIN, data={u32=11, u64=11}}) = 0
epoll_pwait(14, [{events=EPOLLHUP, data={u32=11, u64=11}}], 32, 4150, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 11, 0x7ffc18263b1c) = 0
read(11, "", 32768)                     = 0
close(11)                               = 0
close(8)                                = 0
close(9)                                = 0
close(10)                               = 0
epoll_pwait(14, 0x7bbaca8b97f0, 32, 4146, NULL, 8) = -1 EINTR (Interrupted system call)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=35, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---
write(7, "\21\0\0\0", 4)                = 4
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
epoll_pwait(14, [{events=EPOLLIN, data={u32=6, u64=6}}], 32, 4145, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 6, 0x7ffc18263bac) = 0
read(6, "\21\0\0\0", 32768)             = 4
read(6, 0x7bbac93ec000, 32768)          = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 6, {events=EPOLLIN, data={u32=6, u64=6}}) = 0
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], WNOHANG, NULL) = 35
wait4(-1, 0x7bbac79fff74, WNOHANG, NULL) = -1 ECHILD (No child process)
open("/dev/null", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 9
fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
fcntl(9, F_GETFL)                       = 0x8001 (flags O_WRONLY|O_LARGEFILE)
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 10
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
fcntl(10, F_GETFL)                      = 0x8001 (flags O_WRONLY|O_LARGEFILE)
pipe2([11, 12], O_CLOEXEC)              = 0
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_SETFL, O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 36
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
close(12)                               = 0
read(11, 0x7bbabd910000, 32768)         = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 11, {events=EPOLLIN, data={u32=11, u64=11}}) = 0
epoll_pwait(14, [{events=EPOLLHUP, data={u32=11, u64=11}}], 32, 4140, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 11, 0x7ffc18263b1c) = 0
read(11, "", 32768)                     = 0
close(11)                               = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=36, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
write(7, "\21\0\0\0", 4)                = 4
rt_sigreturn({mask=[]})                 = 36
close(8)                                = 0
close(9)                                = 0
close(10)                               = 0
epoll_pwait(14, [{events=EPOLLIN, data={u32=6, u64=6}}], 32, 4135, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 6, 0x7ffc18263bac) = 0
read(6, "\21\0\0\0", 32768)             = 4
read(6, 0x7bbac93ec000, 32768)          = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 6, {events=EPOLLIN, data={u32=6, u64=6}}) = 0
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 36
wait4(-1, 0x7bbac79fff74, WNOHANG, NULL) = -1 ECHILD (No child process)
open("/dev/null", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
pipe2([9, 10], O_CLOEXEC)               = 0
fcntl(9, F_GETFL)                       = 0 (flags O_RDONLY)
fcntl(9, F_GETFL)                       = 0 (flags O_RDONLY)
fcntl(9, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(10, F_GETFL)                      = 0x1 (flags O_WRONLY)
pipe2([11, 12], O_CLOEXEC)              = 0
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_GETFL)                      = 0 (flags O_RDONLY)
fcntl(11, F_SETFL, O_RDONLY|O_NONBLOCK|O_LARGEFILE) = 0
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_GETFL)                      = 0x1 (flags O_WRONLY)
fcntl(12, F_SETFL, O_WRONLY|O_NONBLOCK|O_LARGEFILE) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 37
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
close(12)                               = 0
read(11, 0x7bbabd91a000, 32768)         = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 11, {events=EPOLLIN, data={u32=11, u64=11}}) = 0
epoll_pwait(14, [{events=EPOLLHUP, data={u32=11, u64=11}}], 32, 4129, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 11, 0x7ffc18263b1c) = 0
read(11, "", 32768)                     = 0
close(11)                               = 0
close(8)                                = 0
close(10)                               = 0
read(9, "-levent\n", 32768)             = 8
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
write(7, "\21\0\0\0", 4)                = 4
rt_sigreturn({mask=[]})                 = 8
read(9, "", 32768)                      = 0
epoll_pwait(14, [{events=EPOLLIN, data={u32=6, u64=6}}], 32, 4126, NULL, 8) = 1
epoll_ctl(14, EPOLL_CTL_DEL, 6, 0x7ffc18263bac) = 0
read(6, "\21\0\0\0", 32768)             = 4
read(6, 0x7bbac93ec000, 32768)          = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(14, EPOLL_CTL_ADD, 6, {events=EPOLLIN, data={u32=6, u64=6}}) = 0
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 37
wait4(-1, 0x7bbac79fff74, WNOHANG, NULL) = -1 ECHILD (No child process)
close(9)                                = 0
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbac532c000
munmap(0x7bbac532c000, 16384)           = 0
access("/etc/ld.so.conf", R_OK)         = -1 ENOENT (No such file or directory)
stat("/lib64", 0x7ffc18263d30)          = -1 ENOENT (No such file or directory)
stat("/usr/lib64", 0x7ffc18263d30)      = -1 ENOENT (No such file or directory)
open("/usr/bin/../lib/crystal/libpcre2-8.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/libpcre2-8.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/libpcre2-8.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fstat(8, {st_mode=S_IFREG|0755, st_size=693992, ...}) = 0
close(8)                                = 0
open("/usr/bin/../lib/crystal/libevent.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/libevent.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/libevent.so", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fstat(8, {st_mode=S_IFREG|0755, st_size=301480, ...}) = 0
close(8)                                = 0
readlink("/proc", 0x7ffc18262e70, 4087) = -1 EINVAL (Invalid argument)
readlink("/proc/self", "26", 4092)      = 2
readlink("/proc/26", 0x7ffc18262e70, 4092) = -1 EINVAL (Invalid argument)
readlink("/proc/26/exe", "/usr/bin/crystal", 4096) = 16
readlink("/usr", 0x7ffc18262e70, 4084)  = -1 EINVAL (Invalid argument)
readlink("/usr/bin", 0x7ffc18262e70, 4088) = -1 EINVAL (Invalid argument)
readlink("/usr/bin/crystal", 0x7ffc18262e70, 4096) = -1 EINVAL (Invalid argument)
munmap(0x7bbaca400000, 4096)            = 0
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbac532c000
mmap(0x7bbabeec0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5f0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbaca400000
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbac5328000
mmap(0x7bbabd600000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5e0000
mmap(0x7bbabd5f0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5d0000
mmap(0x7bbabd5e0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5c0000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbac9201000
munmap(0x7bbaca400000, 4096)            = 0
mmap(0x7bbabd5d0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5b0000
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd5ac000
mmap(0x7bbabd5c0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd59c000
mmap(0x7bbabd5ac000, 8388608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabcc00000
mmap(0x7bbabd400000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd400000
stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getrandom("\xa2\x41\x40\xa6\xe4\xf3\xa6\xc8\x12\xc8\x33\xb9\xc0\x6e\x8a\x7a", 16, GRND_NONBLOCK) = 16
getrandom(0x10, 16, GRND_NONBLOCK)      = -1 EFAULT (Bad address)
mmap(NULL, 8388608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabc400000
madvise(0x7bbabc400000, 8388608, MADV_NOHUGEPAGE) = 0
mmap(0x7bbabd410000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd410000
mmap(0x7bbabd420000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd420000
mmap(0x7bbabd430000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd430000
mmap(0x7bbabd440000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd440000
mmap(0x7bbabd450000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd450000
mmap(0x7bbabd460000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd460000
mmap(0x7bbabd470000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd470000
mmap(0x7bbabd480000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbabd480000
ioctl(2, TIOCGWINSZ, {ws_row=21, ws_col=95, ws_xpixel=0, ws_ypixel=0}) = 0
readlink("/proc/self/fd/2", "/dev/pts/0", 256) = 10
stat("/dev/pts/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
fstat(2, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
open("/dev/pts/0", O_RDWR|O_LARGEFILE|O_CLOEXEC) = 8
fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
fcntl(8, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(8, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
fcntl(8, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fcntl(8, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
open("/proc/self/status", O_RDONLY|O_LARGEFILE) = 9
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbaca400000
read(9, "Name:\tcrystal\nUmask:\t0022\nState:"..., 1024) = 1024
read(9, ":\t00000fff\nCpus_allowed_list:\t0-"..., 1024) = 472
read(9, "", 1024)                       = 0
close(9)                                = 0
munmap(0x7bbaca400000, 4096)            = 0
statfs("/selinux", 0x7ffc18265350)      = -1 ENOENT (No such file or directory)
open("/proc/mounts", O_RDONLY|O_LARGEFILE) = 9
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbaca400000
read(9, "/dev/sdc1 / ext4 rw,relatime,err"..., 1024) = 1024
read(9, "/cgroup/cpuset cgroup rw,nosuid,"..., 1024) = 887
read(9, "", 1024)                       = 0
close(9)                                = 0
munmap(0x7bbaca400000, 4096)            = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7bbaca400000
mremap(0x7ffc18268000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18267000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18266000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18265000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18264000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18263000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18262000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18261000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18260000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825f000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825e000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825d000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825c000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825b000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1825a000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18259000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18258000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18257000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18256000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18255000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18254000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18253000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18252000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18251000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18250000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824f000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824e000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824d000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824c000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824b000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc1824a000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18249000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18248000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18247000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18246000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18245000, 4096, 8192, 0)   = -1 ENOMEM (Out of memory)
mremap(0x7ffc18244000, 4096, 8192, 0)   = -1 EFAULT (Bad address)
write(8, "Unhandled exception: ", 21Unhandled exception: )   = 21
write(8, "getrandom: Operation not permitt"..., 34getrandom: Operation not permitted) = 34
write(8, " (", 2 ()                       = 2
write(8, "RuntimeError", 12RuntimeError)            = 12
write(8, ")\n", 2)
)                      = 2
write(8, "  from ", 7  from )                  = 7
write(8, "raise|219|1|/usr/lib/crystal/cor"..., 42raise|219|1|/usr/lib/crystal/core/raise.cr) = 42
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "sys_getrandom|109|11|/usr/lib/cr"..., 75sys_getrandom|109|11|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr) = 75
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "loop|143|5|/usr/lib/crystal/core"..., 42loop|143|5|/usr/lib/crystal/core/kernel.cr) = 42
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "sys_getrandom|102|5|/usr/lib/cry"..., 74sys_getrandom|102|5|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr) = 74
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "getrandom|86|20|/usr/lib/crystal"..., 70getrandom|86|20|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr) = 70
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "random_bytes|53|7|/usr/lib/cryst"..., 72random_bytes|53|7|/usr/lib/crystal/core/crystal/system/unix/getrandom.cr) = 72
write(8, "\n", 1
)                       = 1
write(8, "  from ", 7  from )                  = 7
write(8, "/usr/lib/crystal/core/crystal/ha"..., 84/usr/lib/crystal/core/crystal/hasher.cr|83|3|/usr/lib/crystal/core/crystal/hasher.cr) = 84
write(8, "\n", 1
)                       = 1
ioctl(1, TIOCGWINSZ, {ws_row=21, ws_col=95, ws_xpixel=0, ws_ypixel=0}) = 0
readlink("/proc/self/fd/1", "/dev/pts/0", 256) = 10
stat("/dev/pts/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
open("/dev/pts/0", O_RDWR|O_LARGEFILE|O_CLOEXEC) = 9
fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
fcntl(9, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(9, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
fcntl(9, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fcntl(9, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
munmap(0x7bbaca8ae000, 4096)            = 0
munmap(0x7bbac69e7000, 102400)          = 0
exit_group(1)                           = ?
+++ exited with 1 +++
/ # 

https://git.musl-libc.org/cgit/musl/tree/src/linux/getrandom.c:

#include <sys/random.h>
#include "syscall.h"

ssize_t getrandom(void *buf, size_t buflen, unsigned flags)
{
    return syscall_cp(SYS_getrandom, buf, buflen, flags);
}

This seems identical to the way libc handles it:

/* Implementation of the getrandom system call.
   Copyright (C) 2016-2024 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <sys/random.h>
#include <errno.h>
#include <unistd.h>
#include <sysdep-cancel.h>

/* Write up to LENGTH bytes of randomness starting at BUFFER.
   Return the number of bytes written, or -1 on error.  */
ssize_t
__getrandom (void *buffer, size_t length, unsigned int flags)
{
  return SYSCALL_CANCEL (getrandom, buffer, length, flags);
}
libc_hidden_def (__getrandom)
weak_alias (__getrandom, getrandom)
Blacksmoke16 commented 1 month ago

I'm assuming the same code using crystal run would work just fine? If so this is specific to the interpreter.

straight-shoota commented 1 month ago

This failure only affects the interpreter. Compiled Crystal works fine.

The interpreter is an experimental feature and missing support for musl libc. This is tracked in the meta issue #11555 (although at this point I'm not aware of further details). Perhaps we should explicitly deactivate the interpreter on *-linux-musl targets to make this clear. But interpreter support is an opt-in build anyway, so I don't think this is necessary. It's certainly an error that the Alpine Linux packages a compiler build with interpreter support on a target platform that it does not support.

ysbaddaden commented 1 month ago

AFAIR we don't call the libc getrandom but make a direct syscall. Maybe this the the issue.

HertzDevil commented 1 month ago

We specifically use LibC.getrandom in interpreted code, because inline assembly used by Syscall is unavailable in the interpreter:

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

HertzDevil commented 1 month ago

The real problem is that 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:

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), hence the "Operation not permitted". The same can probably happen on other Linux distros if you run out of entropy.

jadonk commented 1 month ago

I am in the process of testing #15035 and suspect it should fix the issue for me, but I wonder why not use the musl libc function that imitates the standard libc function? Either way, I like making the Crystal function match the syscall itself is better than matching the libc functions. The less libc dependencies, the better.

ysbaddaden commented 1 month ago

When the feature was implemented getrandom(2) wasn't available in glibc while the syscall was generally available. We also wanted backward compatibility of binaries with kernels that didn't have the syscall.

That was in 2017. The libc symbol was released in glibc 2.25, and even the EOL Ubuntu 18.04 for example had glibc 2.27. I think it's fine to assume that the symbol exists nowadays, and we can simplify the implementation to always use the libc call. That would avoid the init check and the urandom fallback.

straight-shoota commented 1 month ago

Hm I can still reproduce this issue even with #15035 🤔

$ docker run --rm -it alpine:edge
$ apk add crystal git
$ git clone https://github.com/crystal-lang/crystal
$ cd crystal
$ bin/crystal i
Crystal interpreter 1.13.2 (2024-08-29).
EXPERIMENTAL SOFTWARE: if you find a bug, please consider opening an issue in
https://github.com/crystal-lang/crystal/issues/new/
Unhandled exception: getrandom: Bad address (RuntimeError)
  from raise|219|1|/crystal/src/raise.cr
  from sys_getrandom|112|11|/crystal/src/crystal/system/unix/getrandom.cr
  from loop|143|5|/crystal/src/kernel.cr
  from sys_getrandom|105|5|/crystal/src/crystal/system/unix/getrandom.cr
  from getrandom|89|20|/crystal/src/crystal/system/unix/getrandom.cr
  from random_bytes|56|7|/crystal/src/crystal/system/unix/getrandom.cr
  from /crystal/src/crystal/hasher.cr|83|3|/crystal/src/crystal/hasher.cr
HertzDevil commented 1 month ago

There is something wrong with Slice#[] in this line:

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

It returns a slice whose pointer is the same as the size, hence the getrandom(0x10, 16, GRND_NONBLOCK) = -1 EFAULT (Bad address) line. If you manually patch that line with read_bytes = sys_getrandom(Slice.new(buf.to_unsafe, chunk_size)) then the interpreter works until you call Slice#[] yourself:

Bytes.new(16)[0, 4].to_unsafe        # => Pointer(UInt8)@0x4
Bytes.new(16)[4, 8]?.try &.to_unsafe # => Pointer(UInt8)@0x8