tkomatsu/webserv

deallocate後に参照を行っている

Closed this issue · 1 comments

make debugでサニタイザをつけてコンパイルし、実行しました。
1件目のリクエストを受け付け、レスポンスを返した後にabortしました。
ログは以下の通りです。

❯ ./webserv 
localhost - [Sat, Sep 2021 03:20:58 GMT] "GET / HTTP/1.1" 200 647 "curl/7.64.1"
=================================================================
==14623==ERROR: AddressSanitizer: heap-use-after-free on address 0x617000000438 at pc 0x000107cf3013 bp 0x7ffee7f138a0 sp 0x7ffee7f13898
READ of size 4 at 0x617000000438 thread T0
    #0 0x107cf3012 in Client::GetStatus() const Client.cpp:159
    #1 0x107d2f028 in WebServ::ExecClientEvent(std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::__value_type<int, ISocket*>, std::__1::__tree_node<std::__1::__value_type<int, ISocket*>, void*>*, long> >) WebServ.cpp:185
    #2 0x107d2c58c in WebServ::Activate() WebServ.cpp:36
    #3 0x107de5cc0 in main main.cpp:15
    #4 0x7fff6b10dcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

0x617000000438 is located 56 bytes inside of 680-byte region [0x617000000400,0x6170000006a8)
freed by thread T0 here:
    #0 0x107f85c0d in wrap__ZdlPv+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x55c0d)
    #1 0x107cfdf9c in Client::~Client() Client.hpp:19
    #2 0x107d30291 in WebServ::ReadClient(std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::__value_type<int, ISocket*>, std::__1::__tree_node<std::__1::__value_type<int, ISocket*>, void*>*, long> >) WebServ.cpp:132
    #3 0x107d2f00d in WebServ::ExecClientEvent(std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::__value_type<int, ISocket*>, std::__1::__tree_node<std::__1::__value_type<int, ISocket*>, void*>*, long> >) WebServ.cpp:181
    #4 0x107d2c58c in WebServ::Activate() WebServ.cpp:36
    #5 0x107de5cc0 in main main.cpp:15
    #6 0x7fff6b10dcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

previously allocated by thread T0 here:
    #0 0x107f857ed in wrap__Znwm+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x557ed)
    #1 0x107d2e6a6 in WebServ::AcceptSession(std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::__value_type<int, ISocket*>, std::__1::__tree_node<std::__1::__value_type<int, ISocket*>, void*>*, long> >) WebServ.cpp:113
    #2 0x107d2c501 in WebServ::Activate() WebServ.cpp:34
    #3 0x107de5cc0 in main main.cpp:15
    #4 0x7fff6b10dcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

SUMMARY: AddressSanitizer: heap-use-after-free Client.cpp:159 in Client::GetStatus() const
Shadow bytes around the buggy address:
  0x1c2e00000030: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00000040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00000050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00000060: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00000070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x1c2e00000080: fd fd fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd
  0x1c2e00000090: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e000000a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e000000b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e000000c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e000000d0: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==14623==ABORTING
zsh: abort      ./webserv

原因として考えられるのは、クライアントを削除するタイミングだと思います。
以下のように修正するとabortは無くなりました。
リークするもしくは、メモリが無限に増えていく気がするので、よろしくない修正方法だと思います。
根本的な解決をしたいですね。。。

void WebServ::ReadClient(socket_iter it) {
  int client_fd = it->first;
  Client *client = dynamic_cast<Client *>(sockets_[client_fd]);
  int ret;

  if ((ret = client->RecvRequest(client_fd)) <= 0) {
    /*
    close(client_fd);
    delete it->second;
    sockets_.erase(it);
    */
  }
}

#86 にて対応