Skip to content

Commit 62d7a95

Browse files
committed
Increase fuzz test iterations to 100 and add comprehensive edge case testing
Changes: - Increase fuzz iterations from 10 to 100 (10x) in CI workflow and CMakeLists.txt - Add fuzz_edge_cases() with 40+ targeted test cases covering: - Boundary values (all zeros, all 0xFFFF) - Zone ID edge cases (empty, long, special chars, with CIDR/port) - CIDR edge cases (0, 128, invalid values) - Port edge cases (0, 65535, out of range) - Bracket mismatches and edge cases - Compression edge cases (multiple ::, triple :) - IPv4-embedded edge cases (invalid octets, wrong positions) - Malformed inputs (empty, special chars, whitespace) - Mixed notation stress tests (all features combined) - Case sensitivity tests - Enhance random string generation with more relevant characters: brackets, slashes, percent, underscores, hyphens, spaces, tabs, newlines - Run edge case tests before random fuzzing for deterministic coverage This provides better edge case coverage and helps identify parser vulnerabilities.
1 parent e699345 commit 62d7a95

File tree

3 files changed

+134
-4
lines changed

3 files changed

+134
-4
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
run: |
4343
cd build
4444
./bin/ipv6-test
45-
./bin/ipv6-fuzz 10
45+
./bin/ipv6-fuzz 100
4646
4747
- name: Generate Coverage Report
4848
run: |
@@ -62,7 +62,7 @@ jobs:
6262
run: |
6363
cd build
6464
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --error-exitcode=1 --suppressions=../valgrind.supp ./bin/ipv6-test
65-
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --error-exitcode=1 --suppressions=../valgrind.supp ./bin/ipv6-fuzz 10
65+
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --error-exitcode=1 --suppressions=../valgrind.supp ./bin/ipv6-fuzz 100
6666
6767
cpp_build:
6868
runs-on: ${{ matrix.os }}

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ if (NOT IPV6_PARSE_LIBRARY_ONLY)
129129
endif ()
130130
enable_testing()
131131
add_test(NAME verification COMMAND ipv6-test)
132-
add_test(NAME fuzz COMMAND ipv6-fuzz 10)
132+
add_test(NAME fuzz COMMAND ipv6-fuzz 100)
133133
add_test(NAME extended COMMAND ipv6-test-extended)
134134
endif ()
135135

fuzz.c

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
#define NUM_ITERATIONS 1000
3434

3535
char* generate_random_string(int length) {
36-
const char* chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:.";
36+
// Include all relevant characters for IPv6/IPv4 parsing including edge case chars
37+
const char* chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:.[]/%_-@#! \t\n";
3738
char* str = (char*)malloc((length + 1) * sizeof(char));
3839
for (int i = 0; i < length; i++) {
3940
int index = rand() % (int)strlen(chars);
@@ -149,6 +150,131 @@ void fuzz_ipv6_compare(int num_iterations) {
149150
printf("\n");
150151
}
151152

153+
void fuzz_edge_cases(void) {
154+
printf("Testing edge cases:\n");
155+
156+
// Edge case test inputs
157+
const char* edge_cases[] = {
158+
// Boundary values
159+
"0:0:0:0:0:0:0:0",
160+
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
161+
162+
// Zone ID edge cases
163+
"fe80::1%", // Empty zone ID
164+
"fe80::1%123456789012345", // Long zone ID (15 chars - max)
165+
"fe80::1%1234567890123456", // Too long zone ID (16 chars)
166+
"fe80::1%eth-0", // Zone ID with hyphen
167+
"fe80::1%eth_0", // Zone ID with underscore
168+
"fe80::1%0", // Numeric zone ID
169+
"[fe80::1%eth0]:8080", // Zone ID with port
170+
"fe80::1/64%eth0", // Zone ID with CIDR
171+
"[fe80::1/64%eth0]:8080", // Zone ID with CIDR and port
172+
173+
// CIDR edge cases
174+
"::/0", // Minimum CIDR
175+
"::1/128", // Maximum CIDR
176+
"::1/129", // Invalid CIDR (too large)
177+
"::1/999", // Invalid CIDR (way too large)
178+
"::1/-1", // Negative CIDR
179+
180+
// Port edge cases
181+
"[::1]:0", // Port 0
182+
"[::1]:65535", // Maximum port
183+
"[::1]:65536", // Port too large
184+
"[::1]:99999", // Port way too large
185+
"[::1]:-1", // Negative port
186+
187+
// Bracket edge cases
188+
"[::1", // Missing closing bracket
189+
"::1]", // Missing opening bracket
190+
"[[::1]]", // Double brackets
191+
"[::1]:8080", // Correct brackets with port
192+
"::1:8080", // No brackets (invalid for port)
193+
194+
// Compression edge cases
195+
":::", // Triple colon
196+
"1::2::3", // Multiple compressions
197+
"::1::2", // Multiple compressions
198+
"1:2:3:4:5:6:7:8:9", // Too many components
199+
200+
// IPv4-embedded edge cases
201+
"::ffff:256.0.0.1", // IPv4 octet too large
202+
"::ffff:1.2.3", // IPv4 too few octets
203+
"::ffff:1.2.3.4.5", // IPv4 too many octets
204+
"::192.168.1.1", // IPv4 embed without ffff prefix
205+
"1:2:3:4:5:6:192.168.1.1", // IPv4 embed at correct position
206+
"192.168.1.1:8080", // IPv4 with port (no brackets)
207+
208+
// Malformed inputs
209+
"", // Empty string
210+
":", // Single colon
211+
"::", // Double colon only
212+
"g::1", // Invalid hex character
213+
"12345::1", // Component too long
214+
" ::1 ", // Leading/trailing spaces
215+
"::1\n", // Newline in input
216+
"::1\t", // Tab in input
217+
218+
// Very long inputs
219+
"1111:2222:3333:4444:5555:6666:7777:8888:9999:aaaa:bbbb:cccc",
220+
221+
// Mixed notation stress tests
222+
"[::ffff:192.168.1.1/96]:8080", // IPv4-embed + CIDR + port
223+
"[2001:db8::1%eth0/64]:443", // Full combo: address + zone + CIDR + port
224+
225+
// Special characters
226+
"::1#comment", // Hash character
227+
"::1@host", // At symbol
228+
"::1!invalid", // Exclamation mark
229+
230+
// Case sensitivity (should be case-insensitive)
231+
"FFFF::1", // Uppercase
232+
"FfFf::aAbB", // Mixed case
233+
234+
// Leading zeros
235+
"0001:0002:0003::1", // Leading zeros in components
236+
237+
NULL
238+
};
239+
240+
int test_num = 0;
241+
int passed = 0;
242+
int failed = 0;
243+
244+
for (int i = 0; edge_cases[i] != NULL; i++) {
245+
test_num++;
246+
printf("Test %d: '%s'\n", test_num, edge_cases[i]);
247+
248+
ipv6_address_full_t addr;
249+
memset(&addr, 0, sizeof(ipv6_address_full_t));
250+
251+
bool result = ipv6_from_str(edge_cases[i], strlen(edge_cases[i]), &addr);
252+
253+
printf(" Result: %s\n", result ? "VALID" : "INVALID");
254+
255+
if (result) {
256+
// Try to convert back to string
257+
char output[IPV6_STRING_SIZE];
258+
size_t len = ipv6_to_str(&addr, output, sizeof(output));
259+
if (len > 0) {
260+
printf(" Round-trip: %s\n", output);
261+
passed++;
262+
} else {
263+
printf(" Round-trip failed\n");
264+
failed++;
265+
}
266+
} else {
267+
// Invalid input - this is expected for many edge cases
268+
passed++;
269+
}
270+
271+
printf("---\n");
272+
}
273+
274+
printf("Edge case testing complete: %d tests, %d passed, %d failed\n\n",
275+
test_num, passed, failed);
276+
}
277+
152278
int main(int argc, const char **argv) {
153279
int num_iterations = NUM_ITERATIONS;
154280

@@ -164,6 +290,10 @@ int main(int argc, const char **argv) {
164290
printf("Running fuzz tests with %d iterations...\n", num_iterations);
165291
srand((unsigned int)time(NULL));
166292

293+
// Run targeted edge case tests first
294+
fuzz_edge_cases();
295+
296+
// Then run random fuzz tests
167297
fuzz_ipv6_from_str(num_iterations);
168298
fuzz_ipv6_to_str(num_iterations);
169299
fuzz_ipv6_compare(num_iterations);

0 commit comments

Comments
 (0)