Search code examples
c++libuv

libuv + C++ segfaults


I'm writing a wrapper for streams in C++ with libuv, but it segfaults, and I can't find out why. The stream class:

class Stream{
  public:
  Stream(int fd);

  void Write(const std::string& data, const std::function<void(void)>& callback);


  const static int STDIN = 0;
  const static int STDOUT = 1;
  private:
  static void OnWrite(uv_write_t *req, int status);
  uv_pipe_t pipe;
};

And the implementation

Stream::Stream(int fd){
  uv_pipe_init(uv_default_loop(), &pipe, 0);
  uv_pipe_open(&pipe, fd);
}

void Stream::Write(const std::string& data, const std::function<void(void)>& callback){
  uv_write_t request;
  uv_buf_t buffer = uv_buf_init((char*)malloc(data.length()*sizeof(char)), data.length());
  strcpy(buffer.base, data.c_str());
  buffer.len = data.length();
  uv_write(&request, (uv_stream_t*)&pipe, &buffer, 1, OnWrite);
}

void Stream::OnWrite(uv_write_t *req, int status){

}

In the main file I do this:

Stream* out = new Stream(Stream::STDOUT);
  out->Write("Hello, World!", []() {
});

uv_run(uv_default_loop(), RUN_DEFAULT);

However, as soon as I run this, it segfaults on the uv_run statement. Backtrace:

#0  0x00007ffff7bcac94 in uv__write_callbacks (stream=stream@entry=0x603010) at src/unix/stream.c:907
#1  0x00007ffff7bcb0a1 in uv__stream_io (loop=<optimized out>, w=0x603098, events=<optimized out>) at src/unix/stream.c:1243
#2  0x00007ffff7bc31eb in uv__run_pending (loop=0x7ffff7dd98c0 <default_loop_struct>) at src/unix/core.c:710
#3  uv_run (loop=0x7ffff7dd98c0 <default_loop_struct>, mode=UV_RUN_DEFAULT) at src/unix/core.c:316
#4  0x0000000000400fcd in main ()

But, if I put the uv_run in the Stream::Write method, it works just fine. How can I fix this?


edit

I now allocate the pipe on the heap instead of on the stack, but it still gives me a segfault. I changed uv_pipe_t in the header to uv_pipe_t*, and the implementation is now changed to this:

Stream::Stream(int fd){
  pipe = new uv_pipe_t;
  uv_pipe_init(uv_default_loop(), pipe, 0);
  uv_pipe_open(pipe, fd);
}

void Stream::Write(const std::string& data, const std::function<void(void)>& callback){
  uv_write_t request;
  uv_buf_t buffer = uv_buf_init((char*)data.c_str(), data.length());
  uv_write(&request, (uv_stream_t*)pipe, &buffer, 1, OnWrite);
}

void Stream::OnWrite(uv_write_t *req, int status){
  delete req;
}

Backtrace from GDB:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7bcac94 in uv__write_callbacks (stream=stream@entry=0x603030) at src/unix/stream.c:907
907         uv__req_unregister(stream->loop, req);
(gdb) backtrace
#0  0x00007ffff7bcac94 in uv__write_callbacks (stream=stream@entry=0x603030) at src/unix/stream.c:907
#1  0x00007ffff7bcb0a1 in uv__stream_io (loop=<optimized out>, w=0x6030b8, events=<optimized out>) at src/unix/stream.c:1243
#2  0x00007ffff7bc31eb in uv__run_pending (loop=0x7ffff7dd98c0 <default_loop_struct>) at src/unix/core.c:710
#3  uv_run (loop=0x7ffff7dd98c0 <default_loop_struct>, mode=UV_RUN_DEFAULT) at src/unix/core.c:316
#4  0x0000000000400f4d in main ()

Solution

  • Your "write request" (uv_write_t) is allocated on the stack, and won't exist when libuv tries to pass its pointer to the write callback function (or, in fact when libuv itself tries to use it.) Remember that everything is asynchronous in libuv; that's probably why you are using it!

    You should allocate your uv_write_t on the heap (or as a global/class variable or from a pool or whatever) and remember to free it in the write callback.

    There is another memory/lifetime bug in your program, but this one is not a crash bug but a garbage-producing one. The buffer you malloc to put in uv_buffer_t is not needed and can be freed right after the call (this one is synchronous!) Really, you don't even need to allocate a separate buffer; you can just pass data.c_str() as the first argument to uv_buf_init().