Compare commits

..

28 Commits

Author SHA1 Message Date
Silas Mariusz 4c0535b1fa Align the XML-RPC command stack so torrent::Object is not constructed on a 4-byte boundary. 2026-10-04 13:37:01 +02:00
Jari Sundell d59765fda4 Added legacy UTF-8 torrent name and path handling. 2026-10-04 20:36:29 +09:00
Silas Mariusz 66c49d24e5 Use heap allocated buffer for rpc::ExecFile::execute_object() and increase size to 128Kb. 2026-10-04 10:18:58 +02:00
rakshasa 3af0410a6e Restrict parse value to strtoll with restrictions on input. 2026-10-01 10:32:43 +02:00
rakshasa 3c94b03323 Restrict parse value to strtoll with restrictions on input. 2026-10-01 10:32:43 +02:00
rakshasa c5d54fbb97 Restrict parse value to strtoll with restrictions on input. 2026-10-01 10:32:43 +02:00
Silas Mariusz 44d51171e1 Mark the system.file.allocate getter untrusted-safe, because d.open reads it. 2026-09-30 10:55:48 +02:00
noctuum dc916278c2 Do not switch an active download to initial_seed
`confirm_finished` did, and the `input_error` reached `main`.
2026-09-29 10:51:58 +02:00
noctuum 366b936ded Check m_entry against size() in set_entry 2026-09-29 10:18:35 +02:00
noctuum 8bcc4c6e92 Read the unpacked object in the raw string case
A list holding one raw string reached this case and threw on `src`.
2026-09-29 10:00:30 +02:00
noctuum 5421a35349 Remove code that has no remaining user
Three declarations had no definition and the rest had no user left.
2026-09-28 18:40:17 +02:00
noctuum d5ce0984fc Reset the freed pointers in XmlRpc::cleanup
`is_valid()` tests `m_env`, so it stayed true after `cleanup()`.
2026-09-28 18:01:05 +02:00
noctuum a89ce3cd7b Check first against last in parse_object
A command ending in `=` reaches it with an empty range.
2026-09-28 17:34:06 +02:00
noctuum d98a441dc4 Sort the transfer chunks by index
The default comparison ordered the pointers, not the chunk indexes.
2026-09-28 17:11:08 +02:00
noctuum fc6e57c3fa Check pthread_sigmask by its return value
`pthread_sigmask` returns the error number and does not set `errno`.
2026-09-28 16:46:02 +02:00
noctuum 8e23ba8b17 Reject an empty argument list in execute_lua
`lua.execute` with no arguments dereferenced `args.begin()`.
2026-09-28 11:36:53 +02:00
noctuum e656801cfa Limit tab completion to text before the cursor
`substr` takes a count, so the text after the cursor joined the prefix.
2026-09-28 11:00:25 +02:00
noctuum 9f6fd7994e Guard control in the main catch handlers
A start without HOME throws before control is constructed.
2026-09-28 10:46:13 +02:00
noctuum bb77aa644c Stop at max_active in scheduler.simple.removed
The limit was checked once, then every download was resumed.
2026-09-28 10:28:46 +02:00
xirvik 83a5e7687b Sync the session directory after committing the renames
Syncing the files alone does not make the new names survive a crash.
2026-09-25 09:31:08 +02:00
Jari Sundell 910b9b35fc Reorder mutex and atomic variable declarations 2026-09-25 08:52:56 +02:00
xirvik e3051df78c Give SessionManager::m_active its own cache line
Prevents false sharing with the adjacent m_mutex under concurrent access.
2026-09-25 08:52:56 +02:00
xirvik 21287a1355 Make SessionManager::m_active atomic
It is written under m_mutex but read under m_pending_builds_mutex in one place.
2026-09-25 08:52:56 +02:00
xirvik 6471dc181e Guard the download against handlers that erase it mid-close
Track a weak_ptr lifetime handle and make erase ignore re-entrant erase.
2026-09-24 10:22:40 +02:00
xirvik a5c39566c8 Write session files with O_NOFOLLOW and mode 0600
Also make directory_entry::is_file() report the real type so symlinks are skipped.
2026-09-24 09:52:03 +02:00
xirvik 019c16a077 rpc: multicall requires methodName as the struct's first member
Faults clearly instead of the confusing positional parse error it replaces.
2026-09-24 09:20:25 +02:00
xirvik 439d23ce62 Bound JSON-RPC input by size and nesting depth
Enforce the nesting bound in one parse, capping allocation by depth, not input size.
2026-09-23 09:25:25 +02:00
xirvik 83b03c2c1e Range-check global throttle rates before narrowing them to kB.
Multiplying kB back to bytes in unsigned int wrapped 2^32 to 0, which means unlimited.
2026-09-23 09:01:02 +02:00
6 changed files with 36 additions and 15 deletions
+7 -2
View File
@@ -86,8 +86,10 @@ initialize_command_system() {
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
CMD_ANY ("system.file_name.allow_legacy_utf8", [](auto, auto) { return torrent::runtime::client_config()->file_name_allow_legacy_utf8(); });
CMD_ANY_VALUE_V ("system.file_name.allow_legacy_utf8.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_file_name_allow_legacy_utf8(value); });
CMD_ANY ("system.file_status_cache.size", [](auto, auto) { return control->core()->file_status_cache()->size(); });
CMD_ANY_V ("system.file_status_cache.prune", [](auto, auto) { return control->core()->file_status_cache()->prune(); });
@@ -167,10 +169,13 @@ initialize_command_system() {
rpc::rpc.mark_safe("system.time");
rpc::rpc.mark_safe("system.time_seconds");
rpc::rpc.mark_safe("system.time_usec");
rpc::rpc.mark_safe("system.torrent_name.use_sanitized");
rpc::rpc.mark_safe("system.file.allocate");
rpc::rpc.mark_safe("system.file.max_size");
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.file_name.replace_slash");
rpc::rpc.mark_safe("system.file_name.allow_legacy_utf8");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
+1 -1
View File
@@ -108,7 +108,7 @@ public:
static stack_type* from_data(char* data) { return reinterpret_cast<stack_type*>(data); }
char buffer[sizeof(torrent::Object) * max_arguments];
alignas(optimal_alignment) char buffer[sizeof(torrent::Object) * max_arguments];
};
command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
+5 -2
View File
@@ -1,5 +1,7 @@
#include "config.h"
#include <memory>
#include "rpc/exec_file.h"
// #include <cassert>
@@ -81,8 +83,9 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
char* argsBuffer[max_args];
char** argsCurrent = argsBuffer;
// Size of value strings are less than 24.
char valueBuffer[buffer_size+1];
// On the heap: a buffer of buffer_size does not belong on the stack of whichever thread runs the command.
auto valueStorage = std::make_unique<char[]>(buffer_size + 1);
char* valueBuffer = valueStorage.get();
char* valueCurrent = valueBuffer;
if (rawArgs.is_list()) {
+1 -1
View File
@@ -10,7 +10,7 @@ namespace rpc {
class ExecFile {
public:
static constexpr unsigned int max_args = 128;
static constexpr unsigned int buffer_size = 4096;
static constexpr unsigned int buffer_size = 128 * 1024;
static constexpr int flag_expand_tilde = 0x1;
static constexpr int flag_throw = 0x2;
+14 -9
View File
@@ -118,27 +118,32 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
if (base != 0 && base != 8 && base != 10 && base != 16)
throw torrent::input_error("Command::string_to_value_unit(...) received invalid base.");
const char* first = src;
while (parse_is_space(*src))
src++;
if (src[0] == '+')
return src;
return first;
if (src[0] == '-') {
if (base == 8 || base == 16)
return src;
return first;
if (src[1] == '0')
return src;
return first;
}
if (base == 10 && src[0] == '0' && (src[1] >= '0' && src[1] <= '9'))
return first;
char* last{};
errno = 0;
*value = strtoll(src, &last, base);
if (errno == ERANGE)
return src;
return first;
if (last == src) {
*value = 0;
@@ -148,7 +153,7 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
if (strcasecmp(src, "true") == 0) { *value = 1; return src + strlen("true"); }
if (strcasecmp(src, "false") == 0) { *value = 0; return src + strlen("false"); }
return src;
return first;
}
switch (*last) {
@@ -156,15 +161,15 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
case 'B': ++last; break;
case 'k':
case 'K':
if (!value_fits_shifted(*value, 10)) return src; // overflow guard
if (!value_fits_shifted(*value, 10)) return first; // overflow guard
*value = *value << 10; ++last; break;
case 'm':
case 'M':
if (!value_fits_shifted(*value, 20)) return src; // overflow guard
if (!value_fits_shifted(*value, 20)) return first; // overflow guard
*value = *value << 20; ++last; break;
case 'g':
case 'G':
if (!value_fits_shifted(*value, 30)) return src; // overflow guard
if (!value_fits_shifted(*value, 30)) return first; // overflow guard
*value = *value << 30; ++last; break;
// case ' ':
// case '\0': *value = *value * unit; break;
@@ -172,7 +177,7 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
default:
if (*value > std::numeric_limits<int64_t>::max() / unit ||
*value < std::numeric_limits<int64_t>::min() / unit)
return src; // overflow guard
return first; // overflow guard
*value = *value * unit;
break;
+8
View File
@@ -71,10 +71,18 @@ rtorrent_Test_Src_SOURCES = $(rtorrent_Test_Common) \
src/test_command_path.h \
src/test_command_string.cc \
src/test_command_string.h \
src/test_command_throttle.cc \
src/test_command_throttle.h \
src/test_command_tracker.cc \
src/test_command_tracker.h \
src/test_download_list.cc \
src/test_download_list.h \
src/test_input_path_input.cc \
src/test_input_path_input.h \
src/test_session_commit.cc \
src/test_session_commit.h \
src/test_session_storer.cc \
src/test_session_storer.h \
src/test_setup.cc \
src/test_setup.h \
src/test_ui_download_list.cc \