Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
jadonk opened this issue Sep 25, 2024 · 9 comments · Fixed by #15035
Closed

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

jadonk opened this issue Sep 25, 2024 · 9 comments · Fixed by #15035
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. platform:linux-musl topic:compiler:interpreter topic:stdlib:system

Comments

@jadonk
Copy link

jadonk commented Sep 25, 2024

$ 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 #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
Copy link
Member

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

@Blacksmoke16 Blacksmoke16 added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:system topic:compiler:interpreter labels Sep 25, 2024
@straight-shoota
Copy link
Member

straight-shoota commented Sep 25, 2024

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
Copy link
Contributor

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

@HertzDevil
Copy link
Contributor

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

{% 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
LibC.getrandom(buf, buflen, flags)
end
end
{% end %}

@HertzDevil
Copy link
Contributor

HertzDevil commented Sep 25, 2024

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:

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

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
Copy link
Author

jadonk commented Sep 26, 2024

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
Copy link
Contributor

ysbaddaden commented Sep 26, 2024

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
Copy link
Member

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
Copy link
Contributor

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

read_bytes = sys_getrandom(buf[0, chunk_size])

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

straight-shoota added a commit that referenced this issue Oct 10, 2024
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. platform:linux-musl topic:compiler:interpreter topic:stdlib:system
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants