GothenburgBitFactory / taskwarrior

Taskwarrior - Command line Task Management
https://taskwarrior.org
MIT License
4.43k stars 303 forks source link

task sync crashes with SIGPIPE #3624

Closed gnojus closed 1 month ago

gnojus commented 1 month ago

With somewhat invalid server sync configuration (sync.server.url pointing to a service that is not a taskchampion server) running task sync results in the process crashing with unix return code 141 (SIGPIPE), without printing any output to stdout/stderr. I assume the bug is in the external taskwarrior rust lib.

task versoin 3.1.0

djmitche commented 1 month ago

Hm, I just get

Failed to synchronize with server                                                                                                                                                                                                                                                                                              

shell returned 2                                                                                                                                                                                                                                                                                                               

can you give more steps to reproduce, or better yet an strace of where the SIGPIPE occurs?

Also, can you please show how you're running task sync? SIGPIPE is typically associated with UNIX pipes, and Taskwarrior does not use those. Are you running something like task sync | <some command>?

gnojus commented 1 month ago

It was hard to create a simple reproducer, but this should work:

And this is the stack trace:

* thread #1, name = 'task', stop reason = signal SIGPIPE
  * frame #0: 0x00007ffff7b336f4 libc.so.6`writev + 20
    frame #1: 0x0000000000c8a74c task`_$LT$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write_vectored::h01796ceb5899e098 [inlined] std::sys::pal::unix::fd::FileDesc::write_vectored::h1771fd34b289caf5 at fd.rs:297:13
    frame #2: 0x0000000000c8a736 task`_$LT$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write_vectored::h01796ceb5899e098 [inlined] std::sys::pal::unix::net::Socket::write_vectored::h5761f22114f9fa19 at net.rs:349:9
    frame #3: 0x0000000000c8a736 task`_$LT$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write_vectored::h01796ceb5899e098 [inlined] std::sys_common::net::TcpStream::write_vectored::h3be77aff68416b47 at net.rs:303:9
    frame #4: 0x0000000000c8a736 task`_$LT$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write_vectored::h01796ceb5899e098 at tcp.rs:642:9
    frame #5: 0x00000000006f7978 task`rustls::vecbuf::ChunkVecBuffer::write_to::hb675df699816cf7a + 472
    frame #6: 0x00000000006b2621 task`rustls::conn::ConnectionCommon$LT$Data$GT$::complete_io::h3f72d9ac52703021 + 209
    frame #7: 0x00000000006b3c17 task`_$LT$rustls..stream..Stream$LT$C$C$T$GT$$u20$as$u20$std..io..Write$GT$::write::hd5bbf1637fb23518 + 87
    frame #8: 0x000000000069f8af task`_$LT$ureq..rtls..RustlsStream$u20$as$u20$std..io..Write$GT$::write::h538bf546b87f9aaa + 31
    frame #9: 0x00000000006a0132 task`std::io::Write::write_all::h9694e1f6fd613978 + 98
    frame #10: 0x00000000006b92a2 task`std::io::copy::stack_buffer_copy::h4751e70477f44635 + 274
    frame #11: 0x00000000006bb106 task`ureq::body::send_body::h0aed3ce946b1e396 + 646
    frame #12: 0x0000000000d78887 task`ureq::unit::connect_inner::hafef95ff878b21c2 + 1319
    frame #13: 0x0000000000d7728d task`ureq::unit::connect::h7fa68a3ebe6033ab + 221
    frame #14: 0x00000000006ae270 task`ureq::request::Request::do_call::_$u7b$$u7b$closure$u7d$$u7d$::h97a40ab23c6f78e1 + 384
    frame #15: 0x00000000006ada77 task`ureq::request::Request::do_call::hc1e883cdf43c1ebd + 1287
    frame #16: 0x00000000006ae7dc task`ureq::request::Request::send_bytes::hd9f3615fe5d86643 + 44
    frame #17: 0x000000000064e0b0 task`_$LT$taskchampion..server..sync..SyncServer$u20$as$u20$taskchampion..server..types..Server$GT$::add_version::h54b33fd65917aa8a + 1072
    frame #18: 0x00000000006812bc task`taskchampion::taskdb::sync::sync::hcc30e39b8cab2d5c + 5996
    frame #19: 0x000000000065737d task`taskchampion::taskdb::TaskDb::sync::h0cb5d59b328d8fff + 77
    frame #20: 0x000000000069357a task`taskchampion::replica::Replica::sync::h56647844e61238e3 + 26
    frame #21: 0x0000000000636491 task`tc_replica_sync + 129
    frame #22: 0x00000000004dd199 task`tc::Replica::sync(tc::Server, bool) + 25
    frame #23: 0x0000000000490485 task`TDB2::sync(tc::Server, bool) + 37
    frame #24: 0x000000000059f63c task`CmdSync::execute(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) + 2524
    frame #25: 0x000000000046406c task`Context::dispatch(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) + 412
    frame #26: 0x00000000004646b8 task`Context::run() + 72
    frame #27: 0x000000000045a614 task`main + 116
    frame #28: 0x00007ffff7a4614a libc.so.6`__libc_start_call_main + 122
    frame #29: 0x00007ffff7a4620b libc.so.6`__libc_start_main@@GLIBC_2.34 + 139
    frame #30: 0x000000000045c7a5 task`_start + 37
djmitche commented 1 month ago

Thanks! I can reproduce with that result! I filed https://github.com/algesten/ureq/issues/819 to see if there's a good way to handle this, as a bit of searching didn't find any suggestions.

djmitche commented 1 month ago

They would like a simple reproduction with ureq alone. I won't get a chance to work on that for a bit, if anyone else wants to try.

djmitche commented 1 month ago

I built a little test script in the taskchampion crate, and this returned a Result::Err from Replica::sync, so this has something to do with how signals are handled in Taskwarrior.

djmitche commented 1 month ago
diff --git src/main.cpp src/main.cpp
index cbdeb0994..3d4a3d1b1 100644
--- src/main.cpp
+++ src/main.cpp
@@ -24,25 +24,36 @@
 //
 ////////////////////////////////////////////////////////////////////////////////

 #include <cmake.h>
 // cmake.h include header must come first

 #include <Context.h>
 #include <rust/cxx.h>
+#include <signal.h>

 #include <cstring>
 #include <iostream>
 #include <new>
 #include <regex>

 ////////////////////////////////////////////////////////////////////////////////
 int main(int argc, const char** argv) {
   int status{0};
+  struct sigaction sa;
+
+  // Initialize the signal action structure
+  sa.sa_handler = SIG_IGN;  // Set the handler to ignore SIGPIPE
+  sa.sa_flags = 0;
+
+  // Set the signal handler for SIGPIPE
+  if (sigaction(SIGPIPE, &sa, NULL) == -1) {
+    return 1;
+  }

   Context globalContext;
   Context::setContext(&globalContext);

   // Lightweight version checking that doesn't require initialization or any I/O.
   if (argc == 2 && !strcmp(argv[1], "--version")) {
     std::cout << VERSION << "\n";
   } else {

seems to help -- the error message is shown instead of dying on the signal. This matches what the Rust runtime does at startup.

I'm not sure that's the right way to set a signal handler, though.