Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/httpserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>

/** Maximum size (in bytes) of an HTTP request body we will accept. The RPC
* interface never needs anywhere near MAX_SIZE; cap it to bound memory use
* from a single connection. */
static const size_t MAX_HTTP_REQUEST_SIZE = 10 * 1024 * 1024;

/** HTTP request work item */
class HTTPWorkItem : public HTTPClosure
{
Expand Down Expand Up @@ -419,7 +424,7 @@ bool InitHTTPServer()
}

evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
evhttp_set_max_body_size(http, MAX_SIZE);
evhttp_set_max_body_size(http, MAX_HTTP_REQUEST_SIZE);
evhttp_set_gencb(http, http_request_cb, NULL);

if (!HTTPBindAddresses(http)) {
Expand Down
7 changes: 7 additions & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,15 @@ static UniValue JSONRPCExecOne(const UniValue& req)
return rpc_result;
}

// Maximum number of requests permitted in a single JSON-RPC batch, to guard
// against a single connection queuing an unbounded amount of work.
static const size_t MAX_BATCH_SIZE = 100;

std::string JSONRPCExecBatch(const UniValue& vReq)
{
if (vReq.size() > MAX_BATCH_SIZE)
throw JSONRPCError(RPC_INVALID_REQUEST, "Batch request size exceeds maximum");

UniValue ret(UniValue::VARR);
for (size_t reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
Expand Down
8 changes: 8 additions & 0 deletions src/univalue/lib/univalue_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

using namespace std;

// Maximum nesting depth of objects/arrays accepted by the parser, to guard
// against stack exhaustion from deeply-nested untrusted JSON input.
static const size_t MAX_JSON_DEPTH = 512;

static bool json_isdigit(int ch)
{
return ((ch >= '0') && (ch <= '9'));
Expand Down Expand Up @@ -315,13 +319,17 @@ bool UniValue::read(const char *raw, size_t size)
setObject();
else
setArray();
if (stack.size() >= MAX_JSON_DEPTH)
return false;
stack.push_back(this);
} else {
UniValue tmpVal(utyp);
UniValue *top = stack.back();
top->values.push_back(tmpVal);

UniValue *newTop = &(top->values.back());
if (stack.size() >= MAX_JSON_DEPTH)
return false;
stack.push_back(newTop);
}

Expand Down
12 changes: 8 additions & 4 deletions src/utilstrencodings.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef BITCOIN_UTILSTRENCODINGS_H
#define BITCOIN_UTILSTRENCODINGS_H

#include <algorithm>
#include <stdint.h>
#include <string>
#include <vector>
Expand Down Expand Up @@ -113,16 +114,19 @@ std::string FormatParagraph(const std::string& in, size_t width = 79, size_t ind

/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
* Takes time proportional to the longer of the two arguments, so that the
* comparison length does not depend on which argument is shorter (the previous
* implementation looped a.size() times and indexed b[i%b.size()], leaking the
* relative lengths through timing).
*/
template <typename T>
bool TimingResistantEqual(const T& a, const T& b)
{
if (b.size() == 0) return a.size() == 0;
size_t accumulator = a.size() ^ b.size();
for (size_t i = 0; i < a.size(); i++)
accumulator |= a[i] ^ b[i%b.size()];
size_t n = std::max(a.size(), b.size());
for (size_t i = 0; i < n; i++)
accumulator |= (size_t)((i < a.size() ? a[i] : 0) ^ (i < b.size() ? b[i] : 0));
return accumulator == 0;
}

Expand Down
Loading