Skip to content

Denial of service when parsing JSON object with keys that have the same hash code #314

@plokhotnyuk

Description

@plokhotnyuk

Sub-quadratic decreasing of throughput when number of JSON object fields (with keys that have the same hash code) is increasing

On contemporary CPUs parsing of such JSON object (with a sequence of 100000 fields like below that is ~1.6Mb) can took more than 100 seconds:

{
"!!sjyehe":null,
"!!sjyeiF":null,
"!!sjyej'":null,
"!!sjyfIe":null,
"!!sjyfJF":null,
...
}

Below are results of the benchmark where size is a number of such fields:

[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)  (value)   Mode  Cnt        Score       Error  Units
[info] ExtractFieldsBenchmark.readArgonaut       1     null  thrpt    5  969947.719 ± 47977.521  ops/s
[info] ExtractFieldsBenchmark.readArgonaut      10     null  thrpt    5  220650.585 ± 27891.347  ops/s
[info] ExtractFieldsBenchmark.readArgonaut     100     null  thrpt    5    7580.773 ±  1298.528  ops/s
[info] ExtractFieldsBenchmark.readArgonaut    1000     null  thrpt    5      71.919 ±     7.111  ops/s
[info] ExtractFieldsBenchmark.readArgonaut   10000     null  thrpt    5       0.444 ±     0.089  ops/s
[info] ExtractFieldsBenchmark.readArgonaut  100000     null  thrpt    5       0.010 ±     0.002  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
  1. Clone jsoniter-scala repo:
git clone https://github.com/plokhotnyuk/jsoniter-scala.git
  1. Enter to the cloned directory and checkout for the specific branch:
cd jsoniter-scala
git checkout argonaut-DoS-by-hashmap-collisions
  1. 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 3 -i 5 .*ExtractFieldsBench.*Argonaut.*'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions