spray / spray-json

A lightweight, clean and simple JSON implementation in Scala
Apache License 2.0
974 stars 190 forks source link

CVE-2018-18853 Denial of service when parsing a JSON object with an unexpected field that has a big number #278

Closed plokhotnyuk closed 5 years ago

plokhotnyuk commented 6 years ago

Sub-quadratic decreasing of throughput when length of the JSON object is increasing

On contemporary CPUs parsing of such JSON object with an additional field that has of 1000000 decimal digits (~1Mb) can took more than 14 seconds:

[info] REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
[info] why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
[info] experiments, perform baseline and negative tests that provide experimental control, make sure
[info] the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
[info] Do not assume the numbers tell you what you want them to tell.
[info] Benchmark                          (size)   Mode  Cnt        Score        Error  Units
[info] ExtractFieldsBenchmark.readSpray        1  thrpt    5  2295616.947 ± 156189.629  ops/s
[info] ExtractFieldsBenchmark.readSpray       10  thrpt    5  2259250.907 ±  73227.696  ops/s
[info] ExtractFieldsBenchmark.readSpray      100  thrpt    5   604608.260 ±   2896.206  ops/s
[info] ExtractFieldsBenchmark.readSpray     1000  thrpt    5    36803.177 ±   2190.794  ops/s
[info] ExtractFieldsBenchmark.readSpray    10000  thrpt    5      558.418 ±     32.686  ops/s
[info] ExtractFieldsBenchmark.readSpray   100000  thrpt    5        6.466 ±      0.199  ops/s
[info] ExtractFieldsBenchmark.readSpray  1000000  thrpt    5        0.071 ±      0.004  ops/s

Reproducible Test Case

To run that benchmarks on your JDK:

  1. Install latest version of sbt and/or ensure that it already installed properly:

    sbt about
  2. Clone jsoniter-scala repo:

    git clone https://github.com/plokhotnyuk/jsoniter-scala.git
  3. Enter to the cloned directory and checkout for the specific branch:

    cd jsoniter-scala
    git checkout spray-json-DoS-by-a-big-number
  4. Run benchmarks using a path parameter to your JDK:

    sbt -no-colors 'jsoniter-scala-benchmark/jmh:run -jvm /usr/lib/jvm/jdk-11/bin/java -wi 2 -i 5 .*ExtractFieldsBench.*Spray.*'