Skip to content

Commit c567ec7

Browse files
committed
fixes
1 parent db31422 commit c567ec7

13 files changed

+694
-57
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ fuzz_crash:
4545
fuzz_clean:
4646
@cd fuzzing && $(MAKE) fuzz_clean
4747

48+
fuzz_report:
49+
@cd fuzzing && $(MAKE) fuzz_report
50+
51+
fuzz_report_html:
52+
@cd fuzzing && $(MAKE) fuzz_report_html
53+
4854
fuzz_help:
4955
@echo "Fuzzing targets for ledger-zxlib:"
5056
@echo ""

fuzzing/FuzzingCommon.cmake

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ endif()
4848
if(ENABLE_COVERAGE)
4949
message(STATUS "Coverage enabled")
5050

51-
# Create coverage directory in project fuzz folder
52-
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage")
51+
# Create coverage directory in project folder
52+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/coverage")
5353

5454
# Add coverage instrumentation
55-
string(APPEND CMAKE_C_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
56-
string(APPEND CMAKE_CXX_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
57-
string(APPEND CMAKE_LINKER_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
55+
string(APPEND CMAKE_C_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
56+
string(APPEND CMAKE_CXX_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
57+
string(APPEND CMAKE_LINKER_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
5858
endif()
5959

6060
# Sanitizers configuration

fuzzing/Makefile

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,39 @@ fuzz_report:
104104
fi; \
105105
done
106106

107-
# Generate HTML coverage report
107+
# Generate unified HTML coverage report for all fuzzers
108108
.PHONY: fuzz_report_html
109109
fuzz_report_html:
110+
@# Check coverage directory exists and has data
110111
@if [ ! -d "$(FUZZ_COVERAGE_DIR)" ] || [ -z "$$(ls -A $(FUZZ_COVERAGE_DIR) 2>/dev/null)" ]; then \
111112
echo "Error: Coverage directory is empty. Please run the fuzzer with ENABLE_COVERAGE flag ON"; \
112113
exit 1; \
113114
fi
115+
@# Merge all profraw files into a single profdata file
114116
@llvm-profdata merge -sparse $(FUZZ_COVERAGE_DIR)/*.profraw -o $(FUZZ_COVERAGE_DIR)/coverage.profdata
115-
@for fuzzer in $(FUZZ_BUILD_DIR)/fuzz-*; do \
116-
if [ -x "$$fuzzer" ]; then \
117-
llvm-cov show $$fuzzer -instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
118-
-format=html -output-dir=$(FUZZ_COVERAGE_DIR)/report_html_$$(basename $$fuzzer); \
119-
fi; \
120-
done
121-
@echo "HTML coverage reports generated in $(FUZZ_COVERAGE_DIR)/report_html_*"
117+
@echo "Generating unified coverage report for all fuzzers..."
118+
@rm -rf $(FUZZ_COVERAGE_DIR)/report_html_unified
119+
@# Generate HTML report with all fuzzer binaries
120+
@llvm-cov show \
121+
$(FUZZ_BUILD_DIR)/fuzz-base58 \
122+
-object $(FUZZ_BUILD_DIR)/fuzz-base64 \
123+
-object $(FUZZ_BUILD_DIR)/fuzz-bech32 \
124+
-object $(FUZZ_BUILD_DIR)/fuzz-hexutils \
125+
-object $(FUZZ_BUILD_DIR)/fuzz-segwit_addr \
126+
-object $(FUZZ_BUILD_DIR)/fuzz-bignum \
127+
-object $(FUZZ_BUILD_DIR)/fuzz-zxformat \
128+
-object $(FUZZ_BUILD_DIR)/fuzz-timeutils \
129+
-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
130+
-format=html \
131+
-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
132+
-show-line-counts-or-regions \
133+
-show-instantiations \
134+
-show-expansions
135+
@echo "Unified HTML coverage report generated in $(FUZZ_COVERAGE_DIR)/report_html_unified"
136+
@# Open report in default browser (cross-platform)
137+
@open $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html 2>/dev/null || \
138+
xdg-open $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html 2>/dev/null || \
139+
echo "Please open manually: $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html"
122140

123141
# === PYTHON FORMATTING TARGETS ===
124142

fuzzing/fuzz_local/CMakeLists.txt

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,49 @@ target_include_directories(zxlib_hexutils PUBLIC
8989
add_fuzz_target(hexutils hexutils_fuzzer.cpp zxlib_hexutils)
9090

9191
# Create library for segwit_addr (reuse existing bech32 sources)
92-
add_fuzz_target(segwit_addr segwit_addr_fuzzer.cpp zxlib_bech32)
92+
add_fuzz_target(segwit_addr segwit_addr_fuzzer.cpp zxlib_bech32)
93+
94+
# Find source files for bignum
95+
file(GLOB BIGNUM_SOURCES
96+
"${ZXLIB_SRC_DIR}/bignum.c"
97+
)
98+
99+
# Create library for bignum
100+
add_library(zxlib_bignum STATIC ${BIGNUM_SOURCES})
101+
target_include_directories(zxlib_bignum PUBLIC
102+
${ZXLIB_INCLUDE_DIR}
103+
${ZXLIB_SRC_DIR}
104+
)
105+
106+
# Add fuzz target for bignum
107+
add_fuzz_target(bignum bignum_fuzzer.cpp zxlib_bignum)
108+
109+
# Find source files for zxformat
110+
file(GLOB ZXFORMAT_SOURCES
111+
"${ZXLIB_SRC_DIR}/zxformat.c"
112+
)
113+
114+
# Create library for zxformat
115+
add_library(zxlib_zxformat STATIC ${ZXFORMAT_SOURCES})
116+
target_include_directories(zxlib_zxformat PUBLIC
117+
${ZXLIB_INCLUDE_DIR}
118+
${ZXLIB_SRC_DIR}
119+
)
120+
121+
# Add fuzz target for zxformat
122+
add_fuzz_target(zxformat zxformat_fuzzer.cpp zxlib_zxformat)
123+
124+
# Find source files for timeutils
125+
file(GLOB TIMEUTILS_SOURCES
126+
"${ZXLIB_SRC_DIR}/timeutils.c"
127+
)
128+
129+
# Create library for timeutils
130+
add_library(zxlib_timeutils STATIC ${TIMEUTILS_SOURCES})
131+
target_include_directories(zxlib_timeutils PUBLIC
132+
${ZXLIB_INCLUDE_DIR}
133+
${ZXLIB_SRC_DIR}
134+
)
135+
136+
# Add fuzz target for timeutils
137+
add_fuzz_target(timeutils timeutils_fuzzer.cpp zxlib_timeutils)

fuzzing/fuzz_local/analyze_local_crashes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,26 @@
1717
try:
1818
# Import configuration from fuzz_config
1919
from fuzz_config import FUZZ_BUILD_DIR
20-
20+
2121
# Check if analyzer script exists
2222
analyzer_script = os.path.join(fuzzing_dir, "analyze_crashes.py")
2323
if not os.path.exists(analyzer_script):
2424
print(f"Error: Analyzer script not found at {analyzer_script}")
2525
sys.exit(1)
26-
26+
2727
# Override default arguments to point to this project root
2828
if "--fuzz-dir" not in sys.argv:
2929
sys.argv.extend(["--fuzz-dir", current_dir])
30-
30+
3131
# Import and run the common crash analyzer
3232
from analyze_crashes import main
33-
33+
3434
print("🔍 Analyzing ledger-zxlib fuzzer crashes...")
3535
print(f"Working directory: {current_dir}")
3636
print(f"Build directory: {os.path.join(current_dir, FUZZ_BUILD_DIR)}")
37-
37+
3838
sys.exit(main())
39-
39+
4040
except ImportError as e:
4141
print(f"Error: Cannot import required module: {e}")
4242
print("Make sure ../analyze_crashes.py and fuzz_config.py exist")

fuzzing/fuzz_local/bignum_fuzzer.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*******************************************************************************
2+
* (c) 2025 Zondax AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
********************************************************************************/
16+
17+
#include <cassert>
18+
#include <cstddef>
19+
#include <cstdint>
20+
#include <cstring>
21+
22+
#ifdef __cplusplus
23+
extern "C" {
24+
#endif
25+
26+
#include "bignum.h"
27+
28+
#ifdef __cplusplus
29+
}
30+
#endif
31+
32+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
33+
if (size < 2) {
34+
return 0;
35+
}
36+
37+
// Test bignumLittleEndian_to_bcd and bignumLittleEndian_bcdprint
38+
uint8_t bcd_buffer[128];
39+
char print_buffer[256];
40+
41+
// Limit input size to avoid too large allocations
42+
const size_t input_size = (size > 64) ? 64 : size;
43+
44+
// Test little endian BCD conversion
45+
memset(bcd_buffer, 0, sizeof(bcd_buffer));
46+
bignumLittleEndian_to_bcd(bcd_buffer, sizeof(bcd_buffer), data, input_size);
47+
48+
// Test BCD to string printing
49+
bignumLittleEndian_bcdprint(print_buffer, sizeof(print_buffer), bcd_buffer, sizeof(bcd_buffer));
50+
51+
// Test big endian BCD conversion
52+
memset(bcd_buffer, 0, sizeof(bcd_buffer));
53+
bignumBigEndian_to_bcd(bcd_buffer, sizeof(bcd_buffer), data, input_size);
54+
55+
// Test BCD to string printing for big endian
56+
bignumBigEndian_bcdprint(print_buffer, sizeof(print_buffer), bcd_buffer, sizeof(bcd_buffer));
57+
58+
// Test with various buffer sizes
59+
if (size > 4) {
60+
const uint16_t bcd_len = (data[0] % 64) + 1;
61+
const uint16_t out_len = (data[1] % 128) + 1;
62+
63+
if (bcd_len <= sizeof(bcd_buffer) && out_len <= sizeof(print_buffer)) {
64+
memset(bcd_buffer, 0, bcd_len);
65+
bignumLittleEndian_to_bcd(bcd_buffer, bcd_len, data + 2, input_size - 2);
66+
bignumLittleEndian_bcdprint(print_buffer, out_len, bcd_buffer, bcd_len);
67+
68+
memset(bcd_buffer, 0, bcd_len);
69+
bignumBigEndian_to_bcd(bcd_buffer, bcd_len, data + 2, input_size - 2);
70+
bignumBigEndian_bcdprint(print_buffer, out_len, bcd_buffer, bcd_len);
71+
}
72+
}
73+
74+
return 0;
75+
}

fuzzing/fuzz_local/fuzz_config.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,7 @@
1818
# Add parent directory to path to import FuzzConfig
1919
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
2020

21-
try:
22-
from run_fuzzers import FuzzConfig
23-
except ImportError:
24-
# Fallback if common module is not available
25-
class FuzzConfig:
26-
def __init__(self, name: str, max_len: int = 17000):
27-
self.name = name
28-
self.max_len = max_len
21+
from run_fuzzers import FuzzConfig
2922

3023

3124
def get_fuzzer_configs():
@@ -36,6 +29,9 @@ def get_fuzzer_configs():
3629
FuzzConfig(name="base64", max_len=1024), # base64 fuzzer for encoding
3730
FuzzConfig(name="hexutils", max_len=512), # hexutils fuzzer for hex string parsing
3831
FuzzConfig(name="segwit_addr", max_len=256), # segwit address fuzzer
32+
FuzzConfig(name="bignum", max_len=256), # bignum fuzzer for BCD conversion
33+
FuzzConfig(name="zxformat", max_len=512), # zxformat fuzzer for string formatting
34+
FuzzConfig(name="timeutils", max_len=256), # timeutils fuzzer for time operations
3935
]
4036

4137

fuzzing/fuzz_local/run_local_fuzz.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@
1717
try:
1818
from run_fuzzers import main
1919
from fuzz_config import MAX_SECONDS, FUZZER_JOBS
20-
20+
2121
# Override default arguments to point to this project root
2222
if "--fuzz-dir" not in sys.argv:
2323
sys.argv.extend(["--fuzz-dir", current_dir])
24-
24+
2525
# Override max-seconds if not provided
2626
if "--max-seconds" not in sys.argv:
2727
sys.argv.extend(["--max-seconds", str(MAX_SECONDS)])
28-
28+
2929
# Override jobs if not provided
3030
if "--jobs" not in sys.argv:
3131
sys.argv.extend(["--jobs", str(FUZZER_JOBS)])
32-
32+
3333
# Run the common fuzzer
3434
sys.exit(main())
35-
35+
3636
except ImportError as e:
3737
print(f"Error: Cannot import required module: {e}")
3838
print("Make sure ../run_fuzzers.py and fuzz_config.py exist")
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*******************************************************************************
2+
* (c) 2025 Zondax AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
********************************************************************************/
16+
17+
#include <cassert>
18+
#include <cstddef>
19+
#include <cstdint>
20+
#include <cstring>
21+
22+
#ifdef __cplusplus
23+
extern "C" {
24+
#endif
25+
26+
#include "timeutils.h"
27+
28+
#ifdef __cplusplus
29+
}
30+
#endif
31+
32+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
33+
if (size < 8) {
34+
return 0;
35+
}
36+
37+
char buffer[256];
38+
39+
// Test printTime with various timestamps
40+
uint64_t timestamp;
41+
memcpy(&timestamp, data, sizeof(uint64_t));
42+
43+
// Test printTime function
44+
printTime(buffer, sizeof(buffer), timestamp);
45+
46+
// Test with smaller buffer sizes
47+
printTime(buffer, 50, timestamp);
48+
printTime(buffer, 20, timestamp);
49+
printTime(buffer, 10, timestamp);
50+
printTime(buffer, 1, timestamp);
51+
52+
// Test printTimeSpecialFormat if available
53+
printTimeSpecialFormat(buffer, sizeof(buffer), timestamp);
54+
printTimeSpecialFormat(buffer, 50, timestamp);
55+
56+
// Test month functions
57+
if (size > 0) {
58+
const uint8_t month = data[0] % 13; // 0-12
59+
const char *monthStr = getMonth(month);
60+
(void)monthStr; // Use to avoid warning
61+
}
62+
63+
// Test decodeTime function
64+
if (size >= 8) {
65+
timedata_t timedata;
66+
decodeTime(&timedata, timestamp);
67+
68+
// Also test extractTime
69+
extractTime(timestamp, &timedata);
70+
}
71+
72+
return 0;
73+
}

0 commit comments

Comments
 (0)