emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.63k stars 3.29k forks source link

[dynamic linking] Uncaught RuntimeError: memory access out of bounds #19497

Open junyuecao opened 1 year ago

junyuecao commented 1 year ago

I use dynamic linking with pthreads. Sorry that my project is very large so that I can't get a tiny demo to reproduce this issue. The error is as follwing.

Uncaught RuntimeError: memory access out of bounds
    at std::__2::vector<bef_st, std::__2::allocator<bef_st>>::__destroy_vector::operator()[abi:v15007]() (0627d8b2:0x2cd72d)
    at invoke_iii (cadbca22-8309-42b5-a7d5-676c369a2b40:66992:34)
    at BRC::File::File(char const*, char const*, unsigned char*, long) (0627d8b2:0xf04613)
    at invoke_iiiiii (cadbca22-8309-42b5-a7d5-676c369a2b40:67080:34)
    at BRC::FileUtil::readFile(char const*) (0627d8b2:0xf042cb)
    ....

I tried my best to find out some information that might help. The wat content of the same function for W/O dynamic linking of BRC::File::File(char const*, char const*, unsigned char*, long) (0627d8b2:0xf04613) are followings:

// Without dynamic linking (works normal)
  (func $BRC::File::File(char const*, char const*, unsigned char*, long) (;155805;) (param $var0 i32) (param $var1 i32) (param $var2 i32) (param $var3 i32) (param $var4 i32) (result i32)
    (local $var5 i32)
    global.get $GOT.data.internal.__THREW__ 
    local.set $var5
    local.get $var0
    local.get $var2
    call $std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:v15007]<std::nullptr_t>(char const*)
    local.set $var2
    local.get $var5
    i32.const 0
    i32.store
    i32.const 61  ==========> here the func index is a const 
    local.get $var2
    i32.const 12
    i32.add
    local.get $var1
    call $invoke_iii
    drop
    local.get $var5
    i32.load
    local.set $var0
    local.get $var5
    i32.const 0
    i32.store
    block $label0
      local.get $var0
      i32.const 1
      i32.eq
      br_if $label0
      local.get $var2
      local.get $var4
      i32.store offset=28
      local.get $var2
      local.get $var3
      i32.store offset=24
      local.get $var2
      return
    end $label0
    call $__cxa_find_matching_catch_2
    local.set $var5
    call $getTempRet0
    drop
    local.get $var2
    call $std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::~basic_string()
    drop
    local.get $var5
    call $__resumeException
    unreachable
  )
// Side module (crash)
  (func $BRC::File::File(char const*, char const*, unsigned char*, long) (;49916;) (param $var0 i32) (param $var1 i32) (param $var2 i32) (param $var3 i32) (param $var4 i32) (result i32)
    (local $var5 i32)
    global.get $__THREW__
    local.set $var5
    local.get $var0
    local.get $var2
    call $std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:v15007]<std::nullptr_t>(char const*)
    local.set $var0
    local.get $var5
    i32.const 0
    i32.store
    global.get $__table_base       ===========> here the func index is __table_base (but why it  didn't add an offset )
    local.get $var0
    i32.const 12
    i32.add
    local.get $var1
    **call $invoke_iii** (Crashed) 
    drop
    local.get $var5
    i32.load
    local.set $var1
    local.get $var5
    i32.const 0
    i32.store
    local.get $var1
    i32.const 1
    i32.ne
    if
      local.get $var0
      local.get $var4
      i32.store offset=28
      local.get $var0
      local.get $var3
      i32.store offset=24
      local.get $var0
      return
    end
    call $__cxa_find_matching_catch_2
    local.set $var1
    call $getTempRet0
    drop
    local.get $var0
    call $std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::~basic_string()
    drop
    local.get $var1
    call $__resumeException
    unreachable
  )

The strange thing is that the table base is used directly without adding an offset. I checked some other usage of __table_base, most of them are like

    global.get $__table_base
    i32.const 5678
    i32.add
sbc100 commented 1 year ago

Can you build with -sERROR_ON_WASM_CHANGES_AFTER_LINK and see if the original output of the linker contains the addition of the offset? I'm supposing that its possible that the offset is zero in this case and binaryen is eliminating the add?

junyuecao commented 1 year ago

@sbc100 It's true that the offset is zero when build with -sERROR_ON_WASM_CHANGES_AFTER_LINK. But by debugging in to wasm code it seems that the function indices called by invoke_iii refers to different functions.

The working one calls to 61 std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:v15007]<std::nullptr_t>(char const*)

The crashed one calls to __table_base std::__2::vector<bef_st, std::__2::allocator<bef_st>>::__destroy_vector::operator()[abi:v15007]() or std::__2::vector<bef_st, std::__2::allocator<bef_st>>::__throw_length_error[abi:v15007]() const (with -sERROR_ON_WASM_CHANGES_AFTER_LINK)

So is it possible that the offset is wrong generated by the relocation algorithm?

171bbb5 func[121054] <BRC::File::File(char const*, char const*, unsigned char*, long)>:
 171bbb6: 01 7f                      | local[0] type=i32
 171bbb8: 23 87 80 80 80 00          | global.get 7 <__THREW__>
 171bbbe: 21 05                      | local.set 5
 171bbc0: 20 00                      | local.get 0
 171bbc2: 20 02                      | local.get 2
 171bbc4: 10 bd 80 80 80 00          | call 61 <std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:v15007]<std::nullptr_t>(char const*)>
 171bbca: 21 02                      | local.set 2
 171bbcc: 20 05                      | local.get 5
 171bbce: 41 00                      | i32.const 0
 171bbd0: 36 02 00                   | i32.store 2 0
 171bbd3: 23 82 80 80 80 00          | global.get 2 <__table_base> 
 171bbd9: 41 80 80 80 80 00          | i32.const 0  ==========> offset is zero
 171bbdf: 6a                         | i32.add
 171bbe0: 20 02                      | local.get 2
 171bbe2: 41 0c                      | i32.const 12
 171bbe4: 6a                         | i32.add
 171bbe5: 20 01                      | local.get 1
 171bbe7: 10 86 80 80 80 00          | call 6 <invoke_iii>
 171bbed: 1a                         | drop
 171bbee: 20 05                      | local.get 5
 171bbf0: 28 02 00                   | i32.load 2 0
 171bbf3: 21 00                      | local.set 0
 171bbf5: 20 05                      | local.get 5
 171bbf7: 41 00                      | i32.const 0
 171bbf9: 36 02 00                   | i32.store 2 0
 171bbfc: 02 40                      | block
 171bbfe: 20 00                      |   local.get 0
 171bc00: 41 01                      |   i32.const 1
 171bc02: 46                         |   i32.eq
 171bc03: 0d 00                      |   br_if 0
 171bc05: 20 02                      |   local.get 2
 171bc07: 20 04                      |   local.get 4
 171bc09: 36 02 1c                   |   i32.store 2 28
 171bc0c: 20 02                      |   local.get 2
 171bc0e: 20 03                      |   local.get 3
 171bc10: 36 02 18                   |   i32.store 2 24
 171bc13: 20 02                      |   local.get 2
 171bc15: 0f                         |   return
 171bc16: 0b                         | end
 171bc17: 10 87 80 80 80 00          | call 7 <__cxa_find_matching_catch_2>
 171bc1d: 21 05                      | local.set 5
 171bc1f: 10 88 80 80 80 00          | call 8 <getTempRet0>
 171bc25: 1a                         | drop
 171bc26: 20 02                      | local.get 2
 171bc28: 10 e8 80 80 80 00          | call 104 <std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::~basic_string()>
 171bc2e: 1a                         | drop
 171bc2f: 20 05                      | local.get 5
 171bc31: 10 96 80 80 80 00          | call 22 <__resumeException>
 171bc37: 00                         | unreachable
 171bc38: 0b                         | end
junyuecao commented 1 year ago

Another information may help is this File's content is very simple as following image

arsnyder16 commented 1 year ago

@junyuecao What sdk version are you using? Was this working in any previous versions?

junyuecao commented 1 year ago

@arsnyder16 What I was using is 3.1.42 at that time. I excluded some source code from the side module and this bug disappeared. It's weired that one or two source code files will affect the lingking result in another function.

jinyuejin commented 1 year ago

how did you resolve? I have the same problem

junyuecao commented 1 year ago

@jinyuejin No I didn't solve it. It just gone when I exclude two source code files.