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

eval and execute at toplevel is (still) invalid #51267

Open
jakobnissen opened this issue Sep 11, 2023 · 9 comments
Open

eval and execute at toplevel is (still) invalid #51267

jakobnissen opened this issue Sep 11, 2023 · 9 comments
Labels
bug Indicates an unexpected problem or unintended behavior

Comments

@jakobnissen
Copy link
Contributor

jakobnissen commented Sep 11, 2023

This problem happens on Julia 1.6.7, 1.7.3, 1.8.5, 1.9.3, 1.10-beta2 and master. It might possibly also happens on older versions, but Automa.jl only supports Julia 1.6 and above.
MWE:

Reduced to this snippet with the help of @maleadt

struct Tokenizer end
define_iterate() = :(Base.iterate(::Tokenizer) = nothing)

for x in 1
    eval(define_iterate())
    iterate(Tokenizer())
end

Old way of of reproducing
Make a test file /tmp/foo.jl with the following content

using Automa
for goto in (false,)
    make_tokenizer([re"ADF"]; goto=false) |> eval
    collect(Tokenizer{UInt32, String, 1}("")) == []
end

Now, in a project with Automa master branch, #733b8cf (or the released v1.0.0)

julia> using Automa
julia> include("/tmp/foo.jl") # crashes

Interestingly, if, instead of generating the code during the session, we instead manually write out the code like this, it does not crash:

julia> open("/tmp/foo3.jl", "w") do io
           println(io, "using Automa")
           println(io, "for goto in (false,)")
           println(io, make_tokenizer([re"ADF"]; goto=false))
           println(io, "collect(Tokenizer{UInt32, String, 1}(\"\")) == []")
           println(io, "end")
       end

Also, including the file after manually running make_tokenizer doesn't crash either:

julia> using Automa

julia> make_tokenizer([re"ADF"]; goto=false) |> eval

julia> include("/tmp/foo.jl")

...which suggests to me that the bug could have something to do with the Julia interpreter, which runs the top-level for loop in foo.jl

Crash message:

Unreachable reached at 0x7f4628f24b7a

[54249] signal (4.2): Illegal instruction
in expression starting at /tmp/foo.jl:3
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /tmp/foo.jl:7
jl_toplevel_eval_flex at /home/jakni/code/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /home/jakni/code/julia/src/toplevel.c:877
ijl_toplevel_eval_in at /home/jakni/code/julia/src/toplevel.c:985
eval at ./boot.jl:396 [inlined]
include_string at ./loading.jl:2070
_include at ./loading.jl:2130
include at ./client.jl:489
unknown function (ip: 0x7f4628f248e5)
jl_apply at /home/jakni/code/julia/src/julia.h:1993 [inlined]
do_call at /home/jakni/code/julia/src/interpreter.c:125
eval_value at /home/jakni/code/julia/src/interpreter.c:222
eval_stmt_value at /home/jakni/code/julia/src/interpreter.c:173 [inlined]
eval_body at /home/jakni/code/julia/src/interpreter.c:634
jl_interpret_toplevel_thunk at /home/jakni/code/julia/src/interpreter.c:774
jl_toplevel_eval_flex at /home/jakni/code/julia/src/toplevel.c:934
jl_toplevel_eval_flex at /home/jakni/code/julia/src/toplevel.c:877
ijl_toplevel_eval_in at /home/jakni/code/julia/src/toplevel.c:985
eval at ./boot.jl:396 [inlined]
eval_user_input at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:150
repl_backend_loop at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:246
#start_repl_backend#46 at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:231
start_repl_backend at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:228
#run_repl#59 at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:387
run_repl at /home/jakni/code/julia/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:373
jfptr_run_repl_92277 at /home/jakni/code/julia/usr/lib/julia/sys.so (unknown line)
#1043 at ./client.jl:432
jfptr_YY.1043_83306 at /home/jakni/code/julia/usr/lib/julia/sys.so (unknown line)
jl_apply at /home/jakni/code/julia/src/julia.h:1993 [inlined]
jl_f__call_latest at /home/jakni/code/julia/src/builtins.c:812
#invokelatest#2 at ./essentials.jl:899 [inlined]
invokelatest at ./essentials.jl:896 [inlined]
run_main_repl at ./client.jl:415
exec_options at ./client.jl:332
_start at ./client.jl:552
jfptr__start_83334 at /home/jakni/code/julia/usr/lib/julia/sys.so (unknown line)
jl_apply at /home/jakni/code/julia/src/julia.h:1993 [inlined]
true_main at /home/jakni/code/julia/src/jlapi.c:582
jl_repl_entrypoint at /home/jakni/code/julia/src/jlapi.c:731
main at /home/jakni/code/julia/cli/loader_exe.c:58
unknown function (ip: 0x7f462a227ccf)
__libc_start_main at /usr/lib/libc.so.6 (unknown line)
_start at /home/jakni/code/julia/julia (unknown line)
Allocations: 4208949 (Pool: 4205166; Big: 3783); GC: 6
zsh: illegal hardware instruction (core dumped)  ~/code/julia/julia --project=. --startup-file=no
@jakobnissen jakobnissen changed the title Illegal instruction crash on Julia 1.9 and 1.10 Illegal instruction crash Sep 11, 2023
@maleadt
Copy link
Member

maleadt commented Sep 12, 2023

Reduced:

struct Tokenizer
end
Base.IteratorSize(::Type{Tokenizer}) = Base.SizeUnknown()
function make_tokenizer()
    quote
        Base.iterate(::Tokenizer, state=0) = nothing
    end
end
for x in 1
    eval(make_tokenizer())
    collect(Tokenizer())
end

Reproduces all the way back to 1.0.

@maleadt maleadt added bug Indicates an unexpected problem or unintended behavior and removed bisect wanted labels Sep 12, 2023
@vtjnash
Copy link
Member

vtjnash commented Sep 13, 2023

No, that is just the well-known issue that you are not permitted to both eval and execute code in a loop at top-level

@vtjnash vtjnash changed the title Illegal instruction crash eval and execute at toplevel is invalid Sep 13, 2023
@vtjnash vtjnash changed the title eval and execute at toplevel is invalid eval and execute at toplevel is (still) invalid Sep 13, 2023
@brenhinkeller
Copy link
Contributor

brenhinkeller commented Sep 14, 2023

No, that is just the well-known issue that you are not permitted to both eval and execute code in a loop at top-level

Seems not so well-known.. what is the cause/rationale?

@jakobnissen
Copy link
Contributor Author

I've been watching the issue tracker for like two years and wrttiten Julia code daily for five years, so it can't be that well known.
If this is not technically fixable, could eval detect that it was being run in a top-level loop and throw an exception?

@maleadt
Copy link
Member

maleadt commented Sep 14, 2023

could eval detect that it was being run in a top-level loop and throw an exception?

Running eval in a top-level loop is a very common pattern, e.g., https://github.com/JuliaLang/julia/blob/87e31a5d5e054dd2e48fab0c8ce4f7e8f7facce7/base/arraymath.jl#L5C1-L10. The problem seems to be with the subsequent collect which relies on functionality evaled right before (a limitation I wasn't aware of either).

@jakobnissen
Copy link
Contributor Author

It's not the collect - see the reduced MWE at the top of OP which is your example, but a bit further reduced

@maleadt
Copy link
Member

maleadt commented Sep 14, 2023

It's not the collect - see the reduced MWE at the top of OP which is your example, but a bit further reduced

Well, it is the iterate that gets triggered by the collect. I just meant that it's the code executed after the eval

vchuravy pushed a commit that referenced this issue Sep 14, 2023
The guard instruction for unreachables and other crashes in aarch64 is
`brk`, in macos there isn't a distinction between a brk for a breakpoint
and one for a crash, as an attempt we check the value of `pc` when the
signal is triggered, if it is
`brk #0x1` we say that it is a crash and go into the sigdie_handler. 

We should probably do the same in aarch64 linux, though I haven't dug
too deep into what values it uses for traps, and if what compiler used
matters, on apple I assumed we use clang/LLVM

It might be possible to test this by calling some inline assembly.

This means that something like
#51267 actually crashes with
```c
[16908] signal (5): Trace/BPT trap: 5
in expression starting at /Users/gabrielbaraldi/julia/test.jl:2
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /Users/gabrielbaraldi/julia/test.jl:5
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877
ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined]
ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985
eval at ./boot.jl:383 [inlined]
include_string at ./loading.jl:2070
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
_include at ./loading.jl:2130
include at ./Base.jl:494
jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
exec_options at ./client.jl:317
_start at ./client.jl:552
jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined]
true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582
jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731
Allocations: 570978 (Pool: 570031; Big: 947); GC: 1
fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap)
```
instead of hanging silently

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
NHDaly pushed a commit that referenced this issue Sep 20, 2023
The guard instruction for unreachables and other crashes in aarch64 is
`brk`, in macos there isn't a distinction between a brk for a breakpoint
and one for a crash, as an attempt we check the value of `pc` when the
signal is triggered, if it is
`brk #0x1` we say that it is a crash and go into the sigdie_handler. 

We should probably do the same in aarch64 linux, though I haven't dug
too deep into what values it uses for traps, and if what compiler used
matters, on apple I assumed we use clang/LLVM

It might be possible to test this by calling some inline assembly.

This means that something like
#51267 actually crashes with
```c
[16908] signal (5): Trace/BPT trap: 5
in expression starting at /Users/gabrielbaraldi/julia/test.jl:2
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /Users/gabrielbaraldi/julia/test.jl:5
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877
ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined]
ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985
eval at ./boot.jl:383 [inlined]
include_string at ./loading.jl:2070
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
_include at ./loading.jl:2130
include at ./Base.jl:494
jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
exec_options at ./client.jl:317
_start at ./client.jl:552
jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined]
true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582
jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731
Allocations: 570978 (Pool: 570031; Big: 947); GC: 1
fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap)
```
instead of hanging silently

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
KristofferC pushed a commit that referenced this issue Oct 3, 2023
The guard instruction for unreachables and other crashes in aarch64 is
`brk`, in macos there isn't a distinction between a brk for a breakpoint
and one for a crash, as an attempt we check the value of `pc` when the
signal is triggered, if it is
`brk #0x1` we say that it is a crash and go into the sigdie_handler.

We should probably do the same in aarch64 linux, though I haven't dug
too deep into what values it uses for traps, and if what compiler used
matters, on apple I assumed we use clang/LLVM

It might be possible to test this by calling some inline assembly.

This means that something like
#51267 actually crashes with
```c
[16908] signal (5): Trace/BPT trap: 5
in expression starting at /Users/gabrielbaraldi/julia/test.jl:2
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /Users/gabrielbaraldi/julia/test.jl:5
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877
ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined]
ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985
eval at ./boot.jl:383 [inlined]
include_string at ./loading.jl:2070
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
_include at ./loading.jl:2130
include at ./Base.jl:494
jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
exec_options at ./client.jl:317
_start at ./client.jl:552
jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined]
true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582
jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731
Allocations: 570978 (Pool: 570031; Big: 947); GC: 1
fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap)
```
instead of hanging silently

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit d51ad06)
KristofferC pushed a commit that referenced this issue Oct 11, 2023
The guard instruction for unreachables and other crashes in aarch64 is
`brk`, in macos there isn't a distinction between a brk for a breakpoint
and one for a crash, as an attempt we check the value of `pc` when the
signal is triggered, if it is
`brk #0x1` we say that it is a crash and go into the sigdie_handler.

We should probably do the same in aarch64 linux, though I haven't dug
too deep into what values it uses for traps, and if what compiler used
matters, on apple I assumed we use clang/LLVM

It might be possible to test this by calling some inline assembly.

This means that something like
#51267 actually crashes with
```c
[16908] signal (5): Trace/BPT trap: 5
in expression starting at /Users/gabrielbaraldi/julia/test.jl:2
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /Users/gabrielbaraldi/julia/test.jl:5
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877
ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined]
ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985
eval at ./boot.jl:383 [inlined]
include_string at ./loading.jl:2070
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
_include at ./loading.jl:2130
include at ./Base.jl:494
jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
exec_options at ./client.jl:317
_start at ./client.jl:552
jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined]
true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582
jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731
Allocations: 570978 (Pool: 570031; Big: 947); GC: 1
fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap)
```
instead of hanging silently

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit d51ad06)
@mbauman
Copy link
Member

mbauman commented Oct 27, 2023

I think I would normally expect a world age error here, no?

@vtjnash
Copy link
Member

vtjnash commented Oct 27, 2023

Correct. But codegen intentionally skips emitting that error, which can sometimes lead to crashes like this

nalimilan pushed a commit that referenced this issue Nov 5, 2023
The guard instruction for unreachables and other crashes in aarch64 is
`brk`, in macos there isn't a distinction between a brk for a breakpoint
and one for a crash, as an attempt we check the value of `pc` when the
signal is triggered, if it is
`brk #0x1` we say that it is a crash and go into the sigdie_handler.

We should probably do the same in aarch64 linux, though I haven't dug
too deep into what values it uses for traps, and if what compiler used
matters, on apple I assumed we use clang/LLVM

It might be possible to test this by calling some inline assembly.

This means that something like
#51267 actually crashes with
```c
[16908] signal (5): Trace/BPT trap: 5
in expression starting at /Users/gabrielbaraldi/julia/test.jl:2
_collect at ./array.jl:768
collect at ./array.jl:757
top-level scope at /Users/gabrielbaraldi/julia/test.jl:5
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925
jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877
ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined]
ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985
eval at ./boot.jl:383 [inlined]
include_string at ./loading.jl:2070
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
_include at ./loading.jl:2130
include at ./Base.jl:494
jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
exec_options at ./client.jl:317
_start at ./client.jl:552
jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873
ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074
jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined]
true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582
jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731
Allocations: 570978 (Pool: 570031; Big: 947); GC: 1
fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap)
```
instead of hanging silently

---------

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit d51ad06)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

6 participants