scylladb / scylladb

NoSQL data store using the seastar framework, compatible with Apache Cassandra
http://scylladb.com
GNU Affero General Public License v3.0
13.23k stars 1.26k forks source link

toJson() of decimal type doesn't use exponents so can produce huge output #8002

Open nyh opened 3 years ago

nyh commented 3 years ago

Scylla's implementation of toJson() for the decimal type (the variable-precision number type) mistakenly avoids using exponents in its representation, although exponents are perfectly legal in JSON's number format. So for the number -1.23E-12 it prints -0.00000000000123, and for the number 1e100 it prints 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000. For these example numbers, the representation is just silly and a bit wasteful - but for much larger numbers, the result can be huge, and most likely cause a failure to allocate the buffer to build it. For example, the perfectly legal (though not very useful?) number 1e1000000000 needs a billion bytes for its result. So for example the test

def test_tojson_decimal_high_mantissa(cql, table1):
    p = unique_key_int()
    stmt = cql.prepare(f"INSERT INTO {table1} (p, dec) VALUES ({p}, ?)")
    cql.execute(stmt, [Decimal('1e1000000000')])
    cql.execute(f"SELECT toJson(dec) from {table1} where p = {p}")

Fails with the error:

E   cassandra.FunctionFailure: Error from server: code=1400 [User Defined Function failure] message="execution of tojson failed: Failed execution of function system.tojson: std::bad_alloc (std::bad_alloc)"

We have an xfailing reproducing cql-pytest: test_json.py::test_tojson_decimal_high_mantissa.

nyh commented 1 year ago

Similar to #6794, this issue can be used as a DoS attack against Scylla, where sending a short request can cause Scylla to do arbitrarily-large allocations, potentially std::bad_alloc, and arbitrarily-long scheduler stalls and CPU usage.

This can be an important risk in the future, when we add multi-tenancy (where one tenant can use this attack to DoS a different tenant) . For today's single-tenant installations it is only a serious problem in very specific applications which use a combination of the "decimal" type and the "tojson" function, and sets the decimal column from unchecked user input.

It is possible that we have similar problems in other places which print or calculate with the "decimal" type, not just in to toJson().