Coverage for /pythoncovmergedfiles/medio/medio/src/json_differential_fuzzer.py: 30%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1###### Coverage stub
2import atexit
3import coverage
4cov = coverage.coverage(data_file='.coverage', cover_pylib=True)
5cov.start()
6# Register an exist handler that will print coverage
7def exit_handler():
8 cov.stop()
9 cov.save()
10atexit.register(exit_handler)
11####### End of coverage stub
12#!/usr/bin/python3
14# Copyright 2020 Google LLC
15#
16# Licensed under the Apache License, Version 2.0 (the "License");
17# you may not use this file except in compliance with the License.
18# You may obtain a copy of the License at
19#
20# http://www.apache.org/licenses/LICENSE-2.0
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS,
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25# See the License for the specific language governing permissions and
26# limitations under the License.
27""" An example native JSON vs uJSON differential fuzzer.
29This fuzzer looks for differences between the built-in json library and the
30native ujson library. The ujson library should be built for coverage (see
31build_install_ujson.sh), and the Python fuzzer should be executed under ASAN.
32As an example:
33 LD_PRELOAD="/usr/lib/llvm-9/lib/clang/9.0.1/lib/linux/libclang_rt.asan-x86_64.so
34 $(python3 -c "import atheris; print(atheris.path())")" python3
35 ./json_differential_fuzzer.py -detect_leaks=0
37This fuzzer has found a bug with inconsistent handling of integers with
38too-high magnitude. uJSON sometimes refuses to process numbers that are too far
39from 0 with "Value is too big!" or the equivalent for values that are too
40negative. However, other times it happily processes them with two's compliment
41mod. As an example, it refuses to parse "-9223372036854775809" (the first
42integer not representable in a 64-bit signed number) with "Value is too small";
43but it will happily parse "-80888888888888888888", a significantly more negative
44number. However, it parses it as -9223372036854775808. The JSON spec
45(https://tools.ietf.org/html/rfc7159#section-6) "allows implementations to set
46limits on the range and precision of numbers accepted", so failing to parse
47values that are too big or too small is techincally fine; however,
48misinterpreting them is not.
49"""
51import atheris
52import sys
54with atheris.instrument_imports():
55 import json
56 import ujson
59@atheris.instrument_func
60def ClearAllIntegers(data):
61 """Used to prevent known bug; sets all integers in data recursively to 0."""
62 if type(data) == int:
63 return 0
64 if type(data) == list:
65 for i in range(0, len(data)):
66 data[i] = ClearAllIntegers(data[i])
67 if type(data) == dict:
68 for k, v in data:
69 data[k] = ClearAllIntegers(v)
70 return data
73@atheris.instrument_func
74def TestOneInput(input_bytes):
75 fdp = atheris.FuzzedDataProvider(input_bytes)
76 original = fdp.ConsumeUnicode(sys.maxsize)
78 try:
79 ujson_data = ujson.loads(original)
80 json_data = json.loads(original)
81 except Exception as e:
82 # It would be interesting to enforce that if one of the libraries throws an
83 # exception, the other does too. However, uJSON accepts many invalid inputs
84 # that are uninteresting, such as "00". So, that is not done.
85 return
87 # Uncomment these lines to ignore the errors described in the docstring of
88 # this file.
89 # json_data = ClearAllIntegers(json_data)
90 # ujson_data = ClearAllIntegers(ujson_data)
92 json_dumped = json.dumps(json_data)
93 ujson_dumped = json.dumps(ujson_data)
95 if json_dumped != ujson_dumped:
96 raise RuntimeError(
97 "Decoding/encoding disagreement!\nInput: %s\nJSON data: %s\nuJSON data: %s\nJSON-dumped: %s\nuJSON-dumped: %s\n"
98 % (original, json_data, ujson_data, json_dumped, ujson_dumped))
101def main():
102 atheris.Setup(sys.argv, TestOneInput)
103 atheris.Fuzz()
106if __name__ == "__main__":
107 main()