Commit Graph

345 Commits

Author SHA1 Message Date
rakshasa 3caa31113d Bumped scgi max content length to 64mb. 2026-09-06 14:50:19 +02:00
xirvik 16bfaf2694 Reject an XMLRPC size limit above the SCGI content size limit. 2026-08-27 10:48:26 +02:00
xirvik ac1b2685d5 Guard the value suffixes against negative overflow.
The k, m and g guards only checked the positive side, so -4611686018427387904K became 0.
2026-08-24 08:57:58 +02:00
xirvik 3ae91b6536 Check flag_public_rpc when dispatching jsonrpc calls.
Private commands and non-exported redirects were reachable over jsonrpc.
2026-08-19 19:11:53 +02:00
xirvik af6d8a10ad Add a timeout to scgi tasks.
A hundred connections sending a partial header held the whole task pool.
2026-08-18 12:25:58 +02:00
xirvik 9066afc063 Guard the unit multiplication in value commands against overflow.
The kb variants multiply the argument by 1024 without checking the range.
2026-08-18 11:58:47 +02:00
xirvik d73f245692 Add a depth limit to the command parser.
A million nested braces in one argument exhausted the main thread's stack.
2026-08-18 10:52:38 +02:00
xirvik 7db356cae4 Add a depth limit to json_to_object.
Deeply nested params recursed until the main thread's stack was exhausted.
2026-08-18 10:34:07 +02:00
xirvik 385e149ccc Let the download list own its downloads through a shared pointer.
A multicall could then dispatch commands on an erased download.
2026-08-17 10:40:41 +02:00
xirvik b24db0eaa5 Do not dereference missing text nodes in the tinyxml2 backend.
Empty elements and element children crashed the parser on a null.
2026-08-14 13:16:17 +02:00
xirvik a075001a73 Catch every json exception when processing a JSONRPC request.
A number overflow escaped the two handlers and killed the process.
2026-08-12 18:22:17 +02:00
xirvik 781520fef8 Close the connection when an SCGI header does not fit the buffer.
A malformed header aborted the process from the SCGI thread.
2026-08-12 15:48:39 +02:00
xirvik 87db1cf0f0 rpc: do not dereference a null result from the xmlrpc-c registry.
A failed call returns null, which was passed straight to xmlrpc_mem_block_contents.
2026-08-07 10:56:19 +02:00
Jari Sundell f90a96d57a Added WaitpidQueue to handle background process reaping. 2026-08-04 19:03:26 +09:00
Jari Sundell f52f20f1b6 Cleaned up system headers. 2026-07-26 19:53:18 +09:00
Jari Sundell f6b3ad0efd Minor cleanup of pex and other commands. 2026-07-15 19:40:13 +09:00
Jari Sundell 959448acda Fixed command_base t_pod align static asserts. 2026-07-08 17:15:56 +09:00
Jari Sundell 3b6da6feac Reordered http queue slots to avoid race conditions. 2026-07-07 16:34:19 +09:00
Jari Sundell ea2d22cb87 Removed unused add/remove error-event code. 2026-07-07 03:05:20 +09:00
Jari Sundell a07f57a703 Fixed fd close order in ExecFile. 2026-07-06 18:06:51 +09:00
fffe 9884c9dd9b Add support for posix_spawn_file_actions_addclosefrom_np by @fffe 2026-07-03 20:52:26 +09:00
Jari Sundell 786f1234d2 Fix background task execution. 2026-07-03 17:53:00 +09:00
Jari Sundell 23921cc97c Added new ProxyManager and support for socks5 for peer connections. 2026-06-30 19:55:45 +09:00
Jari Sundell acb02379b8 Use a cache for free diskspace when sync'ing all downloads. 2026-06-18 23:45:55 +09:00
trim21 bf74686c29 fix: replace reinterpret_cast UB in command_base with aligned placement new
Replace the union-based reinterpret_cast type erasure in command_base
with an alignas char buffer + typed copy/destroy helper pointers.

set_function<T>() placement-news the correct std::function<T> type
at the buffer address, and stores per-type copy/destroy helpers so
that the copy ctor, assignment, and destructor always operate on the
actual type rather than assuming base_function.

_reinterpret_cast<T&> access of t_pod remains zero-overhead and is
now well-defined because the object was constructed at that address
as T via placement new.

Fixes #1818
2026-06-18 11:05:58 +02:00
Jari Sundell dbe7997131 Use posix_spawn for execute commands. 2026-06-18 01:01:52 +09:00
Jari Sundell d595ebf7d8 Renamed scgi socket manager category to rpc. 2026-06-17 03:43:26 +09:00
trim21 19305ab662 fix: define LUA_OK for Lua 5.1 and LuaJIT compatibility
LUA_OK was introduced in Lua 5.2. Define it as 0 for compatibility
with Lua 5.1 and LuaJIT (which is based on Lua 5.1).
2026-06-12 09:59:42 +02:00
Xirvik efe258a137 fix(rpc): preserve c_str() stability of stored XMLRPC method names
Commit 6488131 ("Fix RPC/SCGI security and crash bugs by @sirus20x6")
replaced std::vector<std::unique_ptr<const char>> storage with
std::vector<std::string> and returned back().c_str() to the xmlrpc-c
registry as the per-method server_info pointer.

This is unsafe for any method name short enough to be SSO-stored
(<= 15 chars on libstdc++): such a string keeps its buffer inside the
std::string object itself. When a later push_back reallocates the
vector and move-constructs the existing elements into a new buffer,
the previously returned c_str() pointers — captured by xmlrpc-c at
registration time — dangle into freed memory.

Because xmlrpc-c does not dereference server_info until a call
dispatches, the failure surfaces later as nondeterministic garbage
in fault strings, e.g.

  faultString: Command "thod." does not exist.    (load.start, log.xmlrpc, log.execute)
  faultString: Command "in_rate" does not exist.  (log.add_output)
  faultString: Command ""       does not exist.   (log.open_file)
  faultString: Command "+U"     does not exist.   (method.set_key)

Long-named methods (e.g. system.client_version at 21 chars) are
heap-allocated above the SSO threshold and escape the bug because
the heap buffer's address is preserved across the vector move.

Switch the storage to std::deque<std::string>: per [deque.modifiers]
push_back does not invalidate references to existing elements, so
the std::string objects do not move and the c_str() pointers handed
to xmlrpc-c remain valid for the program's lifetime. The body of
store_command_name is unchanged.

Fixes the use-after-free; preserves the std::string-based storage
the original commit aimed for.
2026-06-05 10:55:51 +02:00
Xirvik Support 49625ab5e5 rpc: close SCGI task on EPIPE to stop event_write() busy-loop
When an SCGI client closes the connection before rtorrent finishes sending
the response, send() in SCgiTask::event_write() returns -1 with errno EPIPE.
EPIPE was grouped with EAGAIN/EINTR as a non-fatal retry-later condition, so
the task was not closed and its descriptor stayed registered for EPOLLOUT. A
broken socket is reported writable immediately, so epoll_wait() returns it on
every iteration and the SCGI thread spins at 100% CPU on one core
indefinitely. The dead connection fd is also leaked (stays ESTAB).

EPIPE is terminal here, not retryable: the peer is gone and the response can
never be delivered. Close the task on EPIPE, matching event_read(), which
already closes on any recv() error other than EAGAIN/EINTR.

Reproduction: open the SCGI socket, send a complete RPC request, then
shutdown(SHUT_RDWR)/close before reading the reply. Stock: the rtorrent-scgi
thread goes to 100% CPU and the connection leaks. With this change: CPU stays
at 0% and the descriptor is closed.
2026-06-04 10:41:19 +02:00
Jari Sundell cc15e9308a Fixed xmlrpc-c build errors and added better github workflow for unit-tests. 2026-06-04 17:16:47 +09:00
Jari Sundell a5a96236df Moved base64 transform and validation functions. 2026-06-04 06:37:01 +09:00
Jari Sundell f1cfe8ad72 Added hex to Object and string_utf8. 2026-06-04 04:22:16 +09:00
Jari Sundell 50a609ade9 Fixed cacheline size check. 2026-06-01 20:11:02 +09:00
Jari Sundell add305f90c Added support base64 support for non-UTF8 strings. 2026-05-30 00:25:16 +09:00
rakshasa 0989b2218d Various cleanup and fixes to SocketManager categories. 2026-05-27 12:38:52 +02:00
Trim21 3d7e6cb077 Added per-category allocation limits to SocketManager by @trim21 2026-05-27 18:16:02 +09:00
Jari Sundell 64881317c6 Fix RPC/SCGI security and crash bugs by @sirus20x6 2026-05-26 17:27:10 +09:00
Jari Sundell e41b066555 Converted callbacks to new interface. 2026-05-25 20:13:45 +09:00
trim21 b0ef95592b fix SCGI parse_headers: defer content-type body peek until body received
detect_content_type() peeked at m_buffer[m_body] to infer JSON vs XML
when no CONTENT_TYPE header was provided.  When the TCP header segment
arrives without any body bytes, m_body equals m_position and the peek
reads the null terminator padding byte — not the actual '{' or '[' —
causing JSON requests to be incorrectly classified as XML and fail.

Fix:
 - Remove the body peek from detect_content_type(); defer it to after
   the full body is confirmed present in event_read().
 - Add a m_content_type_set flag to distinguish header-provided type
   from auto-detected type.
2026-05-11 11:42:36 +02:00
Miroslav Marchev 26a7e9f545 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 0ed10984b3 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 5f09bb74de Updated to version 3.12.0 2026-05-10 09:25:02 +02:00
Jari Sundell c368b2de1e Fix SCGI event_read: return on partial header read instead of closing connection @trim21 2026-05-10 16:01:45 +09:00
Jari Sundell 71ac256cb5 Moved socket counter to runtime::SocketManager. 2026-05-10 02:11:35 +09:00
rakshasa b05b65bd65 Enable SCGI compression by default and set min size to 1000. 2026-05-09 12:24:24 +02:00
Xirvik 981184574d Reset SCgiTask m_trusted on connection reuse
SCgiTask objects are pre-allocated in a pool (scgi.cc) and reused across
SCGI connections. SCgiTask::open() did not reset m_trusted, so when a
task that had handled an untrusted connection (m_trusted=false) was
reused for a new connection, m_trusted stayed false unless the new
connection explicitly sent UNTRUSTED_CONNECTION=1.

The header parser only set m_trusted=false on value 1 and was a no-op
on value 0 (the comment said "default is trusted, so do nothing") —
which is wrong for a reused task that is no longer in default state.

This caused intermittent rejection of trusted commands (e.g. ruTorrent
calling execute.capture for UID detection) with "Command X is not allowed
for untrusted connections", producing cascading plugin failures and
"ruTorrent cannot determine the UID of rTorrent user" in the web UI.

Fix:
- SCgiTask::open() resets m_trusted=true to default.
- parse_headers explicitly sets m_trusted=true on UNTRUSTED_CONNECTION=0,
  so the value sent on the wire is authoritative regardless of pool
  reuse semantics.

Verified on gb4 with rtorrent 0.16.11 + this fix: 30/30 trusted calls
succeed, 30/30 untrusted correctly blocked, 30/30 trusted-after-untrusted
batch all succeed (previously 70%+ would fail in the same scenario).
2026-05-04 10:06:48 +02:00
fffe 4fec56a243 assert valid read_length 2026-04-24 12:22:34 +02:00
fffe dcf24711ef refactor 2026-04-24 12:22:34 +02:00
fffe 4e3ad1ab62 fix off-by-one in SCgiTask::event_read 2026-04-24 12:22:34 +02:00