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

WASM: RuntimeError: memory access out of bounds #658

Closed
Tracked by #1483
Shaikh-Ubaid opened this issue Aug 23, 2022 · 10 comments · Fixed by #1542 · May be fixed by #665
Closed
Tracked by #1483

WASM: RuntimeError: memory access out of bounds #658

Shaikh-Ubaid opened this issue Aug 23, 2022 · 10 comments · Fixed by #1542 · May be fixed by #665

Comments

@Shaikh-Ubaid
Copy link
Member

Shaikh-Ubaid commented Aug 23, 2022

Steps to recreate:

At https://dev.lfortran.org, if we

  1. switch to the WAT tab
  2. comment the following lines of the default example
! print *, "The Mandelbrot image in color:"
! call show_img_color(Nx, Ny, image_color)
! print *, "The Mandelbrot image in grayscale:"
! call show_img(Nx, Ny, image)
! print *, "Done."
  1. press the run button

Output:

RuntimeError: memory access out of bounds
ERROR: WAT could not be generated from the code

Notes:

  • It seems, after commenting, when the run button is pressed the second time, the WAT output seems fine.
@Shaikh-Ubaid
Copy link
Member Author

Shaikh-Ubaid commented Aug 23, 2022

I did several attempts at this, although did not succeed yet. I think/guess I need help in this.

@Shaikh-Ubaid Shaikh-Ubaid changed the title Bug: WASM: RuntimeError: memory access out of bounds WASM: RuntimeError: memory access out of bounds Aug 23, 2022
@Shaikh-Ubaid
Copy link
Member Author

Shaikh-Ubaid commented Aug 23, 2022

I noted this issue at #597

@Shaikh-Ubaid
Copy link
Member Author

It seems there is a slight possibility that this issue/bug is related to #585.

@certik
Copy link
Contributor

certik commented Aug 24, 2022

The best way to debug these things is to use "bisection", where you first reproduce it robustly, and then you manually simplify the test program to a minimal program that still reproduces it.

Then you do a similar process on the JavaScript side, if needed. Usually in the process you discover what the issue is. If not, post the minimal example here and I can have a look.

@Shaikh-Ubaid
Copy link
Member Author

Shaikh-Ubaid commented Aug 24, 2022

The best way to debug these things is to use "bisection", where you first reproduce it robustly, and then you manually simplify the test program to a minimal program that still reproduces it.

It seems it is being challenging to figure out a minimal program that still reproduces the same error, since for most cases, the WAT outputs seems to work fine.

Also, as shared in the notes section of the description of this issue, the WAT output seems fine when the run button is pressed twice. (I am unsure how it occurs).

@Shaikh-Ubaid
Copy link
Member Author

The best way to debug these things is to use "bisection"

I attempted this again for sometime. I added cout statements (hopefully) to find out where exactly the code fails. It seems that the code fails in the middle of decoding the import section while allocating space for the name of an imported function.

The following ways seem to fix the issue:

  • Using a separate/different FortranEvaluator specifically for obtaining wat from wasm. So, we use fe.get_wasm() and then fe2.get_wat2() in emit_wat_from_source() of src/bin/lfortran.cpp.
  • Using std::vector<Import> for imports in WASMDecoder Class of src/libasr/codegen/wasm_to_wat.cpp.

I am still testing if these are robust (enough) or if there are other/better ways to fix this issue or if the issue lies at some other place instead of the decoding of the imports section.

@Shaikh-Ubaid
Copy link
Member Author

Also, sharing some ways previously tried and did not succeed.

  • Increasing the default memory size of Allocator from 1024 * 1024 to 1024 * 1024 * 640 (i.e. 640MB). (I also tried with just 64MB)
  • Increasing the memory during compilation by passing the flag -s TOTAL_MEMORY=512MB to emscripten (inspiration/reference: https://stackoverflow.com/q/55884378)
    • this was tested in several ways, for example: replacing the flag -s ALLOW_MEMORY_GROWTH=1 with -s TOTAL_MEMORY=512MB or passing both the flags -s ALLOW_MEMORY_GROWTH=1 and -s TOTAL_MEMORY=512MB or passing the flag -s TOTAL_MEMORY=1024MB.
  • Increasing the fixed memory size for generated wasm from 100 pages to 1000 pages.
  • Using the address sanitizer UBSan that is passing the flag -fsanitize=undefined as mentioned here https://emscripten.ru/docs/debugging/Sanitizers.html
  • Using the address sanitizer ASan that is passing the flag -fsanitize=address as mentioned here https://emscripten.ru/docs/debugging/Sanitizers.html

@Shaikh-Ubaid
Copy link
Member Author

The following ways seem to fix the issue:

Using a separate/different FortranEvaluator specifically for obtaining wat from wasm. So, we use fe.get_wasm() and then fe2.get_wat2() in emit_wat_from_source() of src/bin/lfortran.cpp.
Using std::vector for imports in WASMDecoder Class of src/libasr/codegen/wasm_to_wat.cpp.

Out of the above ways, when using a separate/different FortranEvaluator, the WAT output does not seem complete.
Example WAT output:

(module
    (type (;0;) (func (param i32) (result)))
    (type (;1;) (func (param i64) (result)))
    (type (;2;) (func (param f32) (result)))
    (type (;3;) (func (param f64) (result)))
    (type (;4;) (func (param i32 i32) (result)))
    (type (;5;) (func (param) (result)))
    (type (;6;) (func (param i32) (result)))
    (type (;7;) (func (param i32 i32 i32) (result)))
    (type (;8;) (func (param i32 i32 i32) (result)))
    (type (;9;) (func (param) (result)))
    (import "js" "print_i32" (func (;0;) (type 0)))
    (import "js" "print_i64" (func (;1;) (type 1)))
    (import "js" "print_f32" (func (;2;) (type 2)))
    (import "js" "print_f64" (func (;3;) (type 3)))
    (import "js" "print_str" (func (;4;) (type 4)))
    (import "js" "flush_buf" (func (;5;) (type 5)))
    (import "js" "set_exit_code" (func (;6;) (type 6)))
    (import "js" "show_img" (func (;7;) (type 7)))
    (import "js" "show_img_col

Also, WAT output when both the above ways are used:

WAT
(module
    (type (;0;) (func (param i32) (result)))
    (type (;1;) (func (param i64) (result)))
    (type (;2;) (func (param f32) (result)))
    (type (;3;) (func (param f64) (result)))
    (type (;4;) (func (param i32 i32) (result)))
    (type (;5;) (func (param) (result)))
    (type (;6;) (func (param i32) (result)))
    (type (;7;) (func (param i32 i32 i32) (result)))
    (type (;8;) (func (param i32 i32 i32) (result)))
    (type (;9;) (func (param) (result)))
    (import "js" "print_i32" (func (;0;) (type 0)))
    (import "js" "print_i64" (func (;1;) (type 1)))
    (import "js" "print_f32" (func (;2;) (type 2)))
    (import "js" "print_f64" (func (;3;) (type 3)))
    (import "js" "print_str" (func (;4;) (type 4)))
    (import "js" "flush_buf" (func (;5;) (type 5)))
    (import "js" "set_exit_code" (func (;6;) (type 6)))
    (import "js" "show_img" (func (;7;) (type 7)))
    (import "js" "show_img_color" (func (;8;) (type 8)))
    (import "js" "memory" (memory (;0;) 100 100))
    (func $9 (type 9) (param) (result)
        (local i32 f64 f64 f64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64 f64)
        i32.const 8
        local.set 0
        f64.const 0.006667
        local.set 1
        f64.const -0.006667
        local.set 2
        f64.const 3.000000
        local.set 3
        i32.const 0
        local.set 6
        i32.const 1080000
        local.set 7
        i32.const 255
        local.set 10
        i32.const 600
        local.set 11
        i32.const 450
        local.set 12
        i32.const 5400000
        local.set 13
        f64.const 4.000000
        local.set 14
        f64.const -2.503333
        local.set 17
        f64.const -0.500000
        local.set 19
        f64.const 1.503333
        local.set 22
        f64.const 0.000000
        local.set 24
        i32.const 1
        i32.const 1
        i32.sub
        local.set 8
        loop
            local.get 8
            i32.const 1
            i32.add
            local.get 12
            i32.le_s
            if
                local.get 8
                i32.const 1
                i32.add
                local.set 8
                local.get 22
                local.get 2
                local.get 8
                f64.convert_i32_s
                f64.mul
                f64.add
                local.set 21
                i32.const 1
                i32.const 1
                i32.sub
                local.set 4
                loop
                    local.get 4
                    i32.const 1
                    i32.add
                    local.get 11
                    i32.le_s
                    if
                        local.get 4
                        i32.const 1
                        i32.add
                        local.set 4
                        local.get 17
                        local.get 1
                        local.get 4
                        f64.convert_i32_s
                        f64.mul
                        f64.add
                        local.set 16
                        f64.const 0.000000
                        local.set 15
                        f64.const 0.000000
                        local.set 20
                        i32.const 0
                        local.set 9
                        loop
                            i32.const 1
                            if
                                local.get 15
                                f64.const 2.000000
                                drop
                                local.get 15
                                f64.mul
                                local.set 18
                                local.get 20
                                f64.const 2.000000
                                drop
                                local.get 20
                                f64.mul
                                local.set 23
                                local.get 18
                                local.get 23
                                f64.add
                                f64.const 4.000000
                                f64.gt
                                local.get 9
                                local.get 10
                                i32.eq
                                i32.or
                                if
                                    local.get 6
                                    i32.const 0
                                    local.get 4
                                    i32.const 1
                                    i32.sub
                                    i32.add
                                    local.get 8
                                    i32.const 1
                                    i32.sub
                                    i32.const 600
                                    i32.mul
                                    i32.add
                                    i32.const 4
                                    i32.mul
                                    i32.add
                                    i32.const 255
                                    local.get 9
                                    i32.sub
                                    i32.store offset=0 align=1
                                    br 1
                                end
                                local.get 21
                                f64.const 2.000000
                                local.get 15
                                f64.mul
                                local.get 20
                                f64.mul
                                f64.add
                                local.set 20
                                local.get 16
                                local.get 18
                                f64.add
                                local.get 23
                                f64.sub
                                local.set 15
                                local.get 9
                                i32.const 1
                                i32.add
                                local.set 9
                                br 1
                            end
                        end
                        br 1
                    end
                end
                br 1
            end
        end
        local.get 13
        i32.const 0
        i32.const 1
        i32.const 1
        i32.sub
        i32.add
        i32.const 1
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 0
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 2
        i32.const 1
        i32.sub
        i32.add
        i32.const 1
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 135
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 3
        i32.const 1
        i32.sub
        i32.add
        i32.const 1
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 68
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 1
        i32.const 1
        i32.sub
        i32.add
        i32.const 2
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 0
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 2
        i32.const 1
        i32.sub
        i32.add
        i32.const 2
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 87
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 3
        i32.const 1
        i32.sub
        i32.add
        i32.const 2
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 231
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 1
        i32.const 1
        i32.sub
        i32.add
        i32.const 3
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 214
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 2
        i32.const 1
        i32.sub
        i32.add
        i32.const 3
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 45
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 3
        i32.const 1
        i32.sub
        i32.add
        i32.const 3
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 32
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 1
        i32.const 1
        i32.sub
        i32.add
        i32.const 4
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 255
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 2
        i32.const 1
        i32.sub
        i32.add
        i32.const 4
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 167
        i32.store offset=0 align=1
        local.get 13
        i32.const 0
        i32.const 3
        i32.const 1
        i32.sub
        i32.add
        i32.const 4
        i32.const 1
        i32.sub
        i32.const 3
        i32.mul
        i32.add
        i32.const 4
        i32.mul
        i32.add
        i32.const 0
        i32.store offset=0 align=1
        i32.const 1
        i32.const 1
        i32.sub
        local.set 8
        loop
            local.get 8
            i32.const 1
            i32.add
            local.get 12
            i32.le_s
            if
                local.get 8
                i32.const 1
                i32.add
                local.set 8
                i32.const 1
                i32.const 1
                i32.sub
                local.set 4
                loop
                    local.get 4
                    i32.const 1
                    i32.add
                    local.get 11
                    i32.le_s
                    if
                        local.get 4
                        i32.const 1
                        i32.add
                        local.set 4
                        local.get 6
                        i32.const 0
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.load offset=0 align=1
                        local.get 6
                        i32.const 0
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.load offset=0 align=1
                        i32.const 4
                        i32.div_s
                        i32.const 4
                        i32.mul
                        i32.sub
                        i32.const 1
                        i32.add
                        local.set 5
                        local.get 7
                        i32.const 0
                        i32.const 1
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 13
                        i32.const 0
                        i32.const 1
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 5
                        i32.const 1
                        i32.sub
                        i32.const 3
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.load offset=0 align=1
                        i32.store offset=0 align=1
                        local.get 7
                        i32.const 0
                        i32.const 2
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 13
                        i32.const 0
                        i32.const 2
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 5
                        i32.const 1
                        i32.sub
                        i32.const 3
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.load offset=0 align=1
                        i32.store offset=0 align=1
                        local.get 7
                        i32.const 0
                        i32.const 3
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 13
                        i32.const 0
                        i32.const 3
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 5
                        i32.const 1
                        i32.sub
                        i32.const 3
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.load offset=0 align=1
                        i32.store offset=0 align=1
                        local.get 7
                        i32.const 0
                        i32.const 4
                        i32.const 1
                        i32.sub
                        i32.add
                        local.get 4
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.add
                        local.get 8
                        i32.const 1
                        i32.sub
                        i32.const 4
                        i32.mul
                        i32.const 600
                        i32.mul
                        i32.add
                        i32.const 4
                        i32.mul
                        i32.add
                        i32.const 255
                        i32.store offset=0 align=1
                        br 1
                    end
                end
                br 1
            end
        end
        i32.const 5400048
        i32.const 30
        call 4
        call 5
        local.get 11
        local.get 12
        local.get 7
        call 8
        i32.const 5400078
        i32.const 34
        call 4
        call 5
        local.get 11
        local.get 12
        local.get 6
        call 7
        i32.const 5400112
        i32.const 5
        call 4
        call 5
        i32.const 0
        call 6
        return
    )
    (export "_lcompilers_main" (func $9))
    (data (;0;) (i32.const 5400048) "The Mandelbrot image in color:")
    (data (;1;) (i32.const 5400078) "The Mandelbr��e

Please observe the last line in the above output.

Notes:

  • The above outputs are when we directly switch to the WAT tab when the page has loaded.
  • When the run button is pressed (that is when we try to obtain WAT for second or consecutive times), the WAT output seems fine.

@Shaikh-Ubaid
Copy link
Member Author

Shaikh-Ubaid commented Aug 24, 2022

The following ways seem to fix the issue:

Using a separate/different FortranEvaluator specifically for obtaining wat from wasm. So, we use fe.get_wasm() and then fe2.get_wat2() in emit_wat_from_source() of src/bin/lfortran.cpp.

Using std::vector for imports in WASMDecoder Class of src/libasr/codegen/wasm_to_wat.cpp.

I submitted a PR at #665 which uses the second way. It seems to output the expected WAT inlcuding on the first run.

@Shaikh-Ubaid
Copy link
Member Author

Shaikh-Ubaid commented Aug 24, 2022

#665 seems to be more of a temporary fix. I submitted another PR #666 with the cout statements that helped me navigate and find the breaking code.

It seems I might need help in finding a robust/permanent solution. Ondrej Sir, please, could you possibly share if it would be possible for you to try #666, please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants