public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/100897] New: Symmetric transfer does not prevent stack-overflow for C++20 coroutines
@ 2021-06-03 20:03 l.v.merzljak at gmail dot com
  2021-06-11  7:36 ` [Bug c++/100897] " iains at gcc dot gnu.org
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: l.v.merzljak at gmail dot com @ 2021-06-03 20:03 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100897

            Bug ID: 100897
           Summary: Symmetric transfer does not prevent stack-overflow for
                    C++20 coroutines
           Product: gcc
           Version: 11.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: l.v.merzljak at gmail dot com
  Target Milestone: ---

Although the following code uses symmetric transfer, it crashes due to a
stack-overflow. The crash is also reproducible when using the task<> type of
the cppcoro library. The crash does not occur when using clang.

```
// main.cc
#include <coroutine>
#include <exception>

class Task {
 public:
  struct promise_type {
    Task get_return_object() { return Handle::from_promise(*this); }

    struct FinalAwaitable {
      bool await_ready() const noexcept { return false; }

      // Use symmetric transfer. Resuming coro.promise().m_continuation should
      // not require extra stack space
      std::coroutine_handle<> await_suspend(
          std::coroutine_handle<promise_type> coro) noexcept {
        if (coro.promise().m_continuation) {
          return coro.promise().m_continuation;
        } else {
          // The top-level task started from within main() does not have a
          // continuation. This will give control back to the main function.
          return std::noop_coroutine();
        }
      }

      void await_resume() noexcept {}
    };

    std::suspend_always initial_suspend() noexcept { return {}; }

    FinalAwaitable final_suspend() noexcept { return {}; }

    void unhandled_exception() noexcept { std::terminate(); }

    void set_continuation(std::coroutine_handle<> continuation) noexcept {
      m_continuation = continuation;
    }

    void return_void() noexcept {}

   private:
    std::coroutine_handle<> m_continuation;
  };

  using Handle = std::coroutine_handle<promise_type>;

  Task(Handle coroutine) : m_coroutine(coroutine) {}

  ~Task() {
    if (m_coroutine) {
      m_coroutine.destroy();
    }
  }

  void start() noexcept { m_coroutine.resume(); }

  auto operator co_await() const noexcept { return Awaitable{m_coroutine}; }

 private:
  struct Awaitable {
    Handle m_coroutine;

    Awaitable(Handle coroutine) noexcept : m_coroutine(coroutine) {}

    bool await_ready() const noexcept { return false; }

    // Use symmetric transfer. Resuming m_coroutine should not require extra
    // stack space
    std::coroutine_handle<> await_suspend(
        std::coroutine_handle<> awaitingCoroutine) noexcept {
      m_coroutine.promise().set_continuation(awaitingCoroutine);
      return m_coroutine;
    }

    void await_resume() {}
  };

  Handle m_coroutine;
};

Task inner() { co_return; }

Task outer() {
  // Use large number of iterations to trigger stack-overflow
  for (int i = 0; i != 50000000; ++i) {
    co_await inner();
  }
}

int main() {
  auto task = outer();
  task.start();
}
```

I compile the code with `g++-11 main.cc -std=c++20 -O3 -fsanitize=address`.

Here is the output:
```
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==21002==ERROR: AddressSanitizer: stack-overflow on address 0x7fffc666dff8 (pc
0x7f6ec2dfa16d bp 0x7fffc666e870 sp 0x7fffc666e000 T0)
    #0 0x7f6ec2dfa16d in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned
long, unsigned long, void*, bool, unsigned int)
../../../../src/libsanitizer/asan/asan_stack.cpp:57
    #1 0x7f6ec2df00eb in __sanitizer::BufferedStackTrace::Unwind(unsigned long,
unsigned long, void*, bool, unsigned int)
../../../../src/libsanitizer/sanitizer_common/sanitizer_stacktrace.h:122
    #2 0x7f6ec2df00eb in operator delete(void*)
../../../../src/libsanitizer/asan/asan_new_delete.cpp:160
    #3 0x560193552e57 in _Z5innerv.destroy(inner()::_Z5innerv.frame*)
(/home/leonard/Desktop/hiwi/async_io_uring/stack-overflow/a.out+0x1e57)
    #4 0x560193553b30 in _Z5outerv.actor(outer()::_Z5outerv.frame*)
(/home/leonard/Desktop/hiwi/async_io_uring/stack-overflow/a.out+0x2b30)
    #5 0x560193552bbb in _Z5innerv.actor(inner()::_Z5innerv.frame*)
(/home/leonard/Desktop/hiwi/async_io_uring/stack-overflow/a.out+0x1bbb)
...
```

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-06-11 11:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-03 20:03 [Bug c++/100897] New: Symmetric transfer does not prevent stack-overflow for C++20 coroutines l.v.merzljak at gmail dot com
2021-06-11  7:36 ` [Bug c++/100897] " iains at gcc dot gnu.org
2021-06-11 10:33 ` l.v.merzljak at gmail dot com
2021-06-11 11:17 ` iains at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).