Skip to content

Commit c283206

Browse files
authored
Merge pull request #257 from boostorg/fuzzing
Add fuzzing of `try_scientific_to_int` to CI
2 parents ba5ff7b + bd9eab2 commit c283206

File tree

5 files changed

+328
-1
lines changed

5 files changed

+328
-1
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ jobs:
128128
# https://github.com/llvm/llvm-project/issues/59827: disabled 2b/23 for clang-17 with libstdc++13 in 24.04
129129
- { compiler: clang-17, cxxstd: '11,14,17,20', os: ubuntu-24.04 }
130130
- { compiler: clang-18, cxxstd: '11,14,17,20,23,2c', os: ubuntu-24.04 }
131+
- { name: Run code fuzzer, fuzzing: yes,
132+
compiler: clang-18, cxxstd: '20', os: ubuntu-24.04, variant: debug, link: static }
131133

132134
# libc++
133135
- { compiler: clang-6.0, cxxstd: '11,14', os: ubuntu-22.04, container: 'ubuntu:18.04', stdlib: libc++, install: 'clang-6.0 libc++-dev libc++abi-dev' }
@@ -296,7 +298,8 @@ jobs:
296298
# More entries can be added in the same way, see the B2_ARGS assignment in ci/enforce.sh for the possible keys.
297299
# B2_DEFINES: ${{matrix.defines}}
298300
# Variables set here (to non-empty) will override the top-level environment variables, e.g.
299-
# B2_VARIANT: ${{matrix.variant}}
301+
B2_VARIANT: ${{matrix.variant}}
302+
B2_LINK: ${{matrix.link}}
300303
B2_UBSAN: ${{matrix.ubsan}}
301304
run: source ci/github/install.sh
302305

@@ -368,6 +371,11 @@ jobs:
368371
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
369372
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
370373

374+
- name: Run fuzzing
375+
if: matrix.fuzzing
376+
run: B2_TARGETS="libs/$SELF/fuzzing" ci/build.sh
377+
env: {B2_FLAGS: -a}
378+
371379
windows:
372380
defaults:
373381
run:

fuzzing/Jamfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright (c) 2024 Matt Borland
2+
# Copyright (c) 2025 Alexander Grund
3+
#
4+
# Distributed under the Boost Software License, Version 1.0.
5+
# https://www.boost.org/LICENSE_1_0.txt.
6+
7+
8+
import common ;
9+
import path ;
10+
import python ;
11+
import regex ;
12+
import toolset ;
13+
14+
path-constant HERE : . ;
15+
16+
local all_fuzzers = [ regex.replace-list
17+
[ glob "fuzz_*.cpp" ] : ".cpp" : ""
18+
] ;
19+
20+
if ! [ python.configured ]
21+
{
22+
using python ;
23+
}
24+
25+
.make-corpus-script = $(HERE)/make-corpus.py ;
26+
27+
rule make-corpus ( target : sources + : properties * )
28+
{
29+
RUNNER on $(target) = [ path.native $(.make-corpus-script) ] ;
30+
}
31+
actions make-corpus
32+
{
33+
"$(PYTHON:E=python)" "$(RUNNER)" "$(<)" "$(>)"
34+
}
35+
toolset.flags $(__name__).make-corpus PYTHON <python.interpreter> ;
36+
37+
for local fuzzer in $(all_fuzzers)
38+
{
39+
local fuzz_time = 60 ;
40+
local corpus = /tmp/corpus/$(fuzzer) ;
41+
local min_corpus = /tmp/mincorpus/$(fuzzer) ;
42+
local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ;
43+
local seed_files = [ glob "$(seed_corpus)/*" ] ;
44+
45+
# Create the output corpus directories
46+
make $(corpus) : $(seed_files) : make-corpus : <dependency>$(.make-corpus-script) ;
47+
make $(min_corpus) : : common.MkDir ;
48+
49+
# Build the fuzzer
50+
exe $(fuzzer)
51+
:
52+
$(fuzzer).cpp
53+
: requirements
54+
<debug-symbols>on
55+
<optimization>speed
56+
<address-sanitizer>on
57+
<undefined-sanitizer>norecover
58+
<cxxflags>-fsanitize=fuzzer
59+
<linkflags>-fsanitize=fuzzer
60+
<library>/boost/locale//boost_locale
61+
;
62+
63+
# Run the fuzzer for a short while
64+
run $(fuzzer)
65+
: <testing.arg>"$(corpus) -max_total_time=$(fuzz_time)"
66+
: target-name $(fuzzer)-fuzzing
67+
: requirements
68+
<dependency>$(corpus)
69+
;
70+
71+
# Minimize the corpus
72+
run $(fuzzer)
73+
: <testing.arg>"$(min_corpus) $(corpus) -merge=1"
74+
: target-name $(fuzzer)-minimize-corpus
75+
: requirements
76+
<dependency>$(fuzzer)-fuzzing
77+
<dependency>$(corpus)
78+
<dependency>$(min_corpus)
79+
;
80+
}
81+

fuzzing/fuzz_scientific_to_int.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Copyright (c) 2025 Alexander Grund
3+
//
4+
// Distributed under the Boost Software License, Version 1.0.
5+
// https://www.boost.org/LICENSE_1_0.txt
6+
7+
#include "../src/util/numeric_conversion.hpp"
8+
9+
#include <boost/core/detail/string_view.hpp>
10+
#include <cstdint>
11+
#include <exception>
12+
#include <iostream>
13+
14+
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size)
15+
{
16+
const boost::core::string_view sv{reinterpret_cast<const char*>(data), size};
17+
using boost::locale::util::try_scientific_to_int;
18+
try {
19+
uint8_t u8{};
20+
try_scientific_to_int(sv, u8);
21+
22+
uint16_t u16{};
23+
try_scientific_to_int(sv, u16);
24+
25+
uint32_t u32{};
26+
try_scientific_to_int(sv, u32);
27+
28+
uint8_t u64{};
29+
try_scientific_to_int(sv, u64);
30+
} catch(...) {
31+
std::cerr << "Error with '" << sv << "' (size " << size << ')' << std::endl;
32+
std::terminate();
33+
}
34+
35+
return 0;
36+
}

fuzzing/make-corpus.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/env python
2+
3+
import os
4+
import sys
5+
6+
7+
def get_samples(input_files):
8+
for file_name in input_files:
9+
if not os.path.isfile(file_name):
10+
raise RuntimeError("Not a file: " + file_name)
11+
with open(file_name, 'r') as input_file:
12+
yield from input_file
13+
14+
15+
def process_files(output_folder, input_files):
16+
if not os.path.exists(output_folder):
17+
os.makedirs(output_folder)
18+
19+
for i, sample in enumerate(get_samples(input_files)):
20+
with open(os.path.join(output_folder, str(i) + ".txt"), 'w') as output_file:
21+
output_file.write(sample)
22+
23+
24+
if __name__ == "__main__":
25+
if len(sys.argv) < 3:
26+
print("Usage: python script.py <output_folder> <input_file1> [<input_file2> ...]")
27+
sys.exit(1)
28+
29+
process_files(output_folder=sys.argv[1], input_files=sys.argv[2:])
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
1
2+
1E0
3+
0.1E1
4+
0.01E2
5+
0.001E3
6+
10
7+
10E0
8+
1E1
9+
0.1E2
10+
0.01E3
11+
100
12+
1E2
13+
10E1
14+
0.1E3
15+
0.01E4
16+
123
17+
1.23E2
18+
12.3E1
19+
123E0
20+
0.123E3
21+
0
22+
0E0
23+
0.0E1
24+
0.000E3
25+
255
26+
255E0
27+
2.55E2
28+
25.5E1
29+
0.255E3
30+
50
31+
5E1
32+
0.5E2
33+
0.05E3
34+
450
35+
4.5E2
36+
45E1
37+
0.45E3
38+
-50
39+
-5E1
40+
-0.5E2
41+
-0.05E3
42+
250
43+
2.5E2
44+
25E1
45+
250E0
46+
0.25E3
47+
2.5E+2
48+
-700
49+
-7E2
50+
-70E1
51+
-700E0
52+
-0.7E3
53+
1234
54+
1.234E3
55+
123.4E1
56+
1234E0
57+
12.34E2
58+
0.01234E5
59+
123000
60+
123E3
61+
1.23E5
62+
12.3E4
63+
0.000123E9
64+
-0.0005E0
65+
-0.005E1
66+
-0.05E2
67+
-5E-4
68+
9999
69+
9.999E3
70+
99.99E2
71+
999.9E1
72+
9999E0
73+
1.5
74+
1.5E0
75+
0.15E1
76+
0.015E2
77+
-1
78+
-1E0
79+
-0.1E1
80+
-0.01E2
81+
-10
82+
-10E0
83+
-1E1
84+
-0.1E2
85+
-100
86+
-1E2
87+
-10E1
88+
-100E0
89+
45
90+
4.5E1
91+
0.45E2
92+
5678
93+
5.678E3
94+
56.78E2
95+
567.8E1
96+
5678E0
97+
999000
98+
9.99E5
99+
99.9E4
100+
999E3
101+
9990E2
102+
200
103+
2E2
104+
20E1
105+
200E0
106+
2.0E+2
107+
2147483648
108+
2.147483648E9
109+
4294967296
110+
4.294967296E9
111+
10000000000
112+
1E10
113+
18446744074.073709551615E9
114+
9223372036854775808
115+
9.223372036854775808E18
116+
100000000000
117+
1E11
118+
1000000000000
119+
1E12
120+
1234567890123
121+
1.234567890123E12
122+
9876543210000
123+
9.87654321E12
124+
9999999999999
125+
9.999999999999E12
126+
99999999999999
127+
9.9999999999999E13
128+
100000000000000
129+
1E14
130+
123456789012345
131+
1.23456789012345E14
132+
987654321098765
133+
9.87654321098765E14
134+
1000000000000000
135+
1E15
136+
1844674400000000000
137+
1.8446744E18
138+
9000000000000000000
139+
9E18
140+
9990000000000000000
141+
9.99E18
142+
10000000000000000000
143+
1E19
144+
12345678900000000000
145+
1.23456789E19
146+
9876543210000000000
147+
9.87654321E18
148+
18440000000000000000
149+
1.844E19
150+
900000000000000
151+
9E14
152+
1844674400000000
153+
1.8446744E15
154+
1844674407000000000
155+
1.844674407E18
156+
12300000000000000000
157+
1.23E19
158+
18446744073700000000
159+
1.84467440737E19
160+
9999999999999999999
161+
9.999999999999999999E18
162+
1844674400000000000
163+
1.8446744E15
164+
10.5E18
165+
100.0000E18
166+
5E
167+
15E
168+
225E
169+
5E-
170+
15E-
171+
225E-
172+
5e1
173+
5.5e1

0 commit comments

Comments
 (0)