mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-05 13:49:21 +00:00
Bound JSON-RPC input by size and nesting depth
Enforce the nesting bound in one parse, capping allocation by depth, not input size.
This commit is contained in:
+39
-1
@@ -2,8 +2,10 @@
|
||||
|
||||
#include "rpc/jsonrpc.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
@@ -223,13 +225,49 @@ handle_notification(const json& request) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using json_input_adapter = decltype(nlohmann::detail::input_adapter(std::declval<const char*>(), std::declval<const char*>()));
|
||||
using json_dom_parser = nlohmann::detail::json_sax_dom_parser<json, json_input_adapter>;
|
||||
|
||||
class json_depth_limited_parser : public json_dom_parser {
|
||||
public:
|
||||
explicit json_depth_limited_parser(json& root) : json_dom_parser(root) {}
|
||||
|
||||
bool start_object(std::size_t length) { return enter() && json_dom_parser::start_object(length); }
|
||||
bool start_array(std::size_t length) { return enter() && json_dom_parser::start_array(length); }
|
||||
|
||||
bool end_object() { m_depth--; return json_dom_parser::end_object(); }
|
||||
bool end_array() { m_depth--; return json_dom_parser::end_array(); }
|
||||
|
||||
private:
|
||||
bool enter() { return ++m_depth <= max_json_depth; }
|
||||
|
||||
uint32_t m_depth{0};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool
|
||||
JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
||||
json response;
|
||||
json body;
|
||||
|
||||
if (length > m_size_limit) {
|
||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "content size exceeds maximum RPC limit", nullptr).dump();
|
||||
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
try {
|
||||
body = json::parse(in_buffer, in_buffer + length);
|
||||
json_depth_limited_parser handler(body);
|
||||
|
||||
if (!json::sax_parse(in_buffer, in_buffer + length, &handler)) {
|
||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "maximum nesting depth exceeded", nullptr).dump();
|
||||
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
switch (body.type()) {
|
||||
case json::value_t::object: {
|
||||
if (!body.contains("id")) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class JsonRpc {
|
||||
@@ -17,6 +19,11 @@ public:
|
||||
bool process(const char* in_buffer, uint32_t length, slot_write callback);
|
||||
|
||||
void insert_command(const char* name, const char* parm, const char* doc) {};
|
||||
|
||||
void set_size_limit(uint64_t size) { m_size_limit = size; }
|
||||
|
||||
private:
|
||||
uint64_t m_size_limit{SCgiTask::max_content_size};
|
||||
};
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
@@ -176,6 +176,7 @@ RpcManager::set_size_limit(uint64_t size) {
|
||||
throw torrent::input_error("XMLRPC size limit is too small to hold a request.");
|
||||
|
||||
m_xmlrpc.set_size_limit(size);
|
||||
m_jsonrpc.set_size_limit(size);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user