Skip to content

Add local fuzzing infrastructure and fix library issues#203

Closed
abenso wants to merge 0 commit intomainfrom
feat/local_fuzz
Closed

Add local fuzzing infrastructure and fix library issues#203
abenso wants to merge 0 commit intomainfrom
feat/local_fuzz

Conversation

@abenso
Copy link
Copy Markdown
Collaborator

@abenso abenso commented Aug 7, 2025

Summary

This PR introduces a comprehensive local fuzzing infrastructure and addresses several library issues:

Local Fuzzing Framework: Added local fuzzing capabilities with dedicated fuzzers for base58, base64, bech32, hexutils, and segwit_addr components
Fuzzing Infrastructure: Created analysis tools, configuration management, and automated fuzzing execution scripts
Library Fixes: Resolved various issues in base58.c, bech32.c, and segwit_addr.c libraries
Test Updates: Enhanced bech32 test coverage and validation

Test plan

  • Local fuzzing infrastructure builds and runs successfully
  • All existing tests continue to pass
  • New fuzzing tools can detect and analyze potential issues
  • Library fixes maintain backward compatibility

🤖 Generated with Claude Code

🔗 zboto Link

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive fuzz testing infrastructure with new fuzzers for Bech32, Base58, Base64, hexutils, segwit_addr, bignum, timeutils, and zxformat components.
    • Added scripts for running and analyzing local fuzzing sessions.
    • Added centralized fuzzing configuration and path management modules.
    • Added new build configurations and Makefile targets to support full fuzzing workflows.
  • Bug Fixes

    • Improved error handling and input validation in Bech32 and Base58 encoding/decoding functions.
    • Enhanced input validation and overflow protection in segwit address and Bech32 conversion logic.
    • Added overflow checks and corrected digit parsing in integer string conversion.
    • Fixed type casting issues in base58 decoding and bignum conversion functions.
  • Tests

    • Significantly expanded Bech32 and Bech32m test coverage, including reference vectors, edge cases, and boundary conditions.
    • Added new test suites for case conversion and integer parsing functions.
  • Chores

    • Updated build and formatting rules for better fuzzing support.
    • Centralized directory and configuration management for fuzzing infrastructure.
    • Incremented patch version to 2.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 7, 2025

Walkthrough

This update introduces a comprehensive local fuzzing infrastructure for the ledger-zxlib project. It adds Makefile and CMake integration for building and running fuzzers, new C++ fuzz targets, Python scripts for orchestrating and analyzing fuzzing runs, and centralized configuration modules. Test coverage for Bech32 encoding is significantly expanded, and minor bug fixes and improvements are applied to encoding implementations.

Changes

Cohort / File(s) Change Summary
Build & Fuzzing Integration
.gitignore, Makefile, fuzzing/Makefile, fuzzing/fuzz_local/CMakeLists.txt, fuzzing/FuzzingCommon.cmake
Adds ignore rules for fuzzing artifacts; extends Makefile with proxy fuzzing targets and help; introduces a dedicated Makefile and CMake configuration for building, running, and cleaning fuzzers; updates coverage directory path and instrumentation flags.
Python Fuzzing Infrastructure
fuzzing/fuzz_paths.py, fuzzing/fuzz_local/fuzz_config.py, fuzzing/run_fuzzers.py, fuzzing/analyze_crashes.py, fuzzing/fuzz_local/run_local_fuzz.py, fuzzing/fuzz_local/analyze_local_crashes.py
Adds centralized path and configuration modules; refactors runner and analyzer scripts to use a unified fuzz directory and configuration; introduces local scripts to orchestrate and analyze fuzzing runs; updates CLI arguments and directory handling to unify fuzz directory usage.
Fuzz Targets (C++)
fuzzing/fuzz_local/base58_fuzzer.cpp, fuzzing/fuzz_local/base64_fuzzer.cpp, fuzzing/fuzz_local/bech32_fuzzer.cpp, fuzzing/fuzz_local/hexutils_fuzzer.cpp, fuzzing/fuzz_local/segwit_addr_fuzzer.cpp, fuzzing/fuzz_local/bignum_fuzzer.cpp, fuzzing/fuzz_local/timeutils_fuzzer.cpp, fuzzing/fuzz_local/zxformat_fuzzer.cpp
Adds new C++ fuzzers for base58, base64, bech32, hexutils, segwit address, bignum, timeutils, and zxformat libraries, each implementing the standard LLVM fuzzer entry point with input validation and fuzz testing logic.
Bech32 & Encoding Implementation
src/bech32.c, src/base58.c, src/segwit_addr.c, src/bignum.c, include/zxformat.h
Improves error handling and input validation in bech32 encoding, base58 decoding, segwit address bit conversion, and bignum bit manipulation; refactors string-to-int64 parsing with overflow checks; minor style and type safety adjustments.
Versioning
include/zxversion.h
Increments patch version macro from 1 to 2.
Test Suite Expansion
tests/bech32.cpp, tests/macros.cpp
Adds extensive new test cases for Bech32 and Bech32m encoding, including edge cases, reference vectors, padding modes, fuzzing-related scenarios, and new tests for string-to-int parsing and case conversion functions.

Sequence Diagram(s)

Local Fuzzing Workflow

sequenceDiagram
    participant User
    participant LocalFuzzScript as run_local_fuzz.py
    participant FuzzingRunner as run_fuzzers.py
    participant FuzzerBinaries
    participant CrashAnalyzer as analyze_crashes.py

    User->>LocalFuzzScript: Run local fuzzing script
    LocalFuzzScript->>FuzzingRunner: Invoke with --fuzz-dir, --max-seconds, --jobs
    FuzzingRunner->>FuzzerBinaries: Launch fuzzers in parallel
    FuzzerBinaries-->>FuzzingRunner: Fuzzing results, crashes
    User->>CrashAnalyzer: Run local crash analyzer script
    CrashAnalyzer->>analyze_crashes.py: Invoke with --fuzz-dir
    analyze_crashes.py->>FuzzerBinaries: Analyze crash artifacts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/local_fuzz

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (10)
fuzzing/fuzz_local/analyze_local_crashes.py (4)

28-28: Remove unnecessary f-string prefix.

The string doesn't contain any placeholders, so the f prefix is unnecessary.

-    print(f"🔍 Analyzing ledger-zxlib crashes...")
+    print("🔍 Analyzing ledger-zxlib crashes...")

28-28: Remove unnecessary f-string prefix.

The string doesn't contain any placeholders, so the f prefix is unnecessary.

-    print(f"🔍 Analyzing ledger-zxlib crashes...")
+    print("🔍 Analyzing ledger-zxlib crashes...")

20-26: Add validation for the analyzer script.

Consider checking if the analyzer script exists before attempting to execute it to provide clearer error messages.

+    analyzer_script = os.path.join(fuzzing_dir, "analyze_crashes.py")
+    if not os.path.exists(analyzer_script):
+        print(f"Error: Analyzer script not found at {analyzer_script}")
+        return 1
+
     # Run the common crash analyzer
     cmd = [
         sys.executable,
-        os.path.join(fuzzing_dir, "analyze_crashes.py"),
+        analyzer_script,
         "--fuzz-dir",
         script_dir,
     ]

31-31: Consider enhanced error handling for subprocess call.

The current implementation uses subprocess.call() which handles basic errors, but you might want to catch and handle specific exceptions for better error reporting.

-    return subprocess.call(cmd)
+    try:
+        return subprocess.call(cmd)
+    except (OSError, subprocess.SubprocessError) as e:
+        print(f"Error executing analyzer: {e}")
+        return 1
fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1)

5-5: Remove unused include.

The <iostream> header is included but not used in this fuzzer.

Apply this diff to remove the unused include:

-#include <iostream>
fuzzing/fuzz_local/run_local_fuzz.py (1)

42-42: Remove unnecessary f-string prefix.

The string doesn't contain any format placeholders, so the f prefix is unnecessary.

Apply this diff:

-    print(f"🚀 Starting ledger-zxlib fuzzing...")
+    print("🚀 Starting ledger-zxlib fuzzing...")
fuzzing/Makefile (1)

72-81: Consider using a PYTHON variable for flexibility

Instead of hardcoding python3, consider adding a variable at the top of the Makefile for better flexibility across different environments:

PYTHON ?= python3

Then use $(PYTHON) in the commands:

-	@python3 $(FUZZ_DIR)/run_local_fuzz.py \
+	@$(PYTHON) $(FUZZ_DIR)/run_local_fuzz.py \
fuzzing/fuzz_local/bech32_fuzzer.cpp (1)

34-40: Consider improving HRP diversity in fuzzer input.

The HRP generation uses data[0] + i but data[0] was already consumed as hrp_len. This might reduce the diversity of HRP values tested. Consider using a different byte or a more diverse generation method.

     // Create a simple HRP from the first byte
     char hrp[17] = {0};
     for (int i = 0; i < hrp_len && i < 16; i++) {
         // Use printable ASCII characters for HRP (a-z)
-        hrp[i] = static_cast<char>('a' + (data[0] + i) % 26);
+        hrp[i] = static_cast<char>('a' + (data[1] + i) % 26);  // Use data[1] for more diversity
     }
     hrp[hrp_len] = '\0';
fuzzing/fuzz_paths.py (1)

17-35: Consider adding path validation methods.

While the current implementation is solid, you might consider adding utility methods for path validation or creation in the future.

@classmethod
def ensure_directory_exists(cls, path):
    """Ensure a directory exists, creating it if necessary"""
    import os
    os.makedirs(path, exist_ok=True)
    return path
fuzzing/fuzz_local/base58_fuzzer.cpp (1)

21-28: Use RAII or stack allocation instead of new/delete.

Consider using automatic storage or RAII to avoid manual memory management and potential memory leaks.

-    // Create null-terminated string for decode test
-    char *decode_input = new char[size + 1];
-    memcpy(decode_input, data, size);
-    decode_input[size] = '\0';
+    // Create null-terminated string for decode test
+    std::vector<char> decode_input(size + 1);
+    memcpy(decode_input.data(), data, size);
+    decode_input[size] = '\0';

-    const int decode_result = decode_base58(decode_input, size, decode_output, &decode_outlen);
+    const int decode_result = decode_base58(decode_input.data(), size, decode_output, &decode_outlen);
     (void)decode_result;

-    delete[] decode_input;

Alternatively, for small inputs, you could use stack allocation:

-    // Create null-terminated string for decode test
-    char *decode_input = new char[size + 1];
-    memcpy(decode_input, data, size);
-    decode_input[size] = '\0';
+    // Create null-terminated string for decode test (limit size for stack safety)
+    if (size > 1000) return 0;  // Skip overly large inputs
+    char decode_input[1001];
+    memcpy(decode_input, data, size);
+    decode_input[size] = '\0';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between f3bdfda and e60d429.

📒 Files selected for processing (20)
  • .gitignore (1 hunks)
  • Makefile (2 hunks)
  • fuzzing/Makefile (2 hunks)
  • fuzzing/analyze_crashes.py (5 hunks)
  • fuzzing/fuzz_local/CMakeLists.txt (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/base58_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/base64_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/hexutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_paths.py (1 hunks)
  • fuzzing/run_fuzzers.py (5 hunks)
  • include/zxversion.h (1 hunks)
  • src/base58.c (1 hunks)
  • src/bech32.c (1 hunks)
  • src/segwit_addr.c (2 hunks)
  • tests/bech32.cpp (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/bech32.c (1)
src/segwit_addr.c (2)
  • convert_bits (148-175)
  • bech32_encode (49-83)
fuzzing/fuzz_local/hexutils_fuzzer.cpp (1)
src/hexutils.c (1)
  • parseHexString (38-56)
fuzzing/fuzz_local/bech32_fuzzer.cpp (1)
src/bech32.c (1)
  • bech32EncodeFromBytes (26-58)
fuzzing/fuzz_local/base58_fuzzer.cpp (1)
src/base58.c (3)
  • decode_base58 (42-92)
  • encode_base58 (94-145)
  • encode_base58_clip (147-147)
fuzzing/fuzz_local/fuzz_config.py (1)
fuzzing/run_fuzzers.py (1)
  • FuzzConfig (31-36)
fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1)
src/segwit_addr.c (4)
  • segwit_addr_encode (177-189)
  • segwit_addr_decode (191-208)
  • bech32_encode (49-83)
  • bech32_decode (85-146)
fuzzing/analyze_crashes.py (1)
fuzzing/fuzz_paths.py (1)
  • FuzzPaths (8-35)
fuzzing/run_fuzzers.py (1)
fuzzing/fuzz_paths.py (1)
  • FuzzPaths (8-35)
🪛 Ruff (0.12.2)
fuzzing/fuzz_local/run_local_fuzz.py

42-42: f-string without any placeholders

Remove extraneous f prefix

(F541)

fuzzing/fuzz_local/analyze_local_crashes.py

28-28: f-string without any placeholders

Remove extraneous f prefix

(F541)

🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 16-16: Target body for "help" exceeds allowed length of 5 (20).

(maxbodylength)


[warning] 68-68: Target body for "fuzz" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 107-107: Target body for "format" exceeds allowed length of 5 (13).

(maxbodylength)


[warning] 145-145: Missing required phony target "all"

(minphony)


[warning] 145-145: Missing required phony target "clean"

(minphony)


[warning] 145-145: Missing required phony target "test"

(minphony)


[warning] 124-124: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)

🔇 Additional comments (54)
include/zxversion.h (1)

20-20: Confirm companion version bump across all artifacts

I ran searches for “36.0.1” and “36.0.2” in CMakeLists.txt, package*.json and CHANGELOG.md but didn’t find any references. Please manually verify that all of the following are updated to 36.0.2:

  • CMakeLists.txt (project version)
  • package manifests (package.json, package-lock.json, etc.)
  • CHANGELOG.md entry
src/base58.c (1)

54-54: LGTM! Security improvement for signed char handling.

The explicit cast to unsigned long prevents potential issues with signed char values being sign-extended during comparison, which could bypass the bounds check and lead to buffer overruns when indexing into BASE58TABLE.

.gitignore (1)

11-17: LGTM! Appropriate exclusions for fuzzing infrastructure.

The additions properly exclude generated artifacts from the fuzzing workflow including build outputs, test corpora, execution logs, and coverage reports.

fuzzing/fuzz_local/base64_fuzzer.cpp (2)

11-33: LGTM! Well-implemented LibFuzzer target.

The fuzzer correctly implements:

  • Input validation and size limiting
  • Proper base64 output buffer calculation
  • Appropriate memory management with new[]/delete[]
  • Standard LibFuzzer conventions

27-27: uint16_t casts are safe in the fuzzer
The fuzzer caps input_size at 1024 (well below 65535) and computes
output_size = ((input_size+2)/3)*4 + 1 ⇒ max 1369, also < 65535.
No truncation risk—casting to uint16_t is safe here.

src/bech32.c (2)

45-47: LGTM! Essential error handling addition.

Previously the convert_bits return value was ignored, which could lead to undefined behavior if bit conversion failed. The added error checking properly propagates conversion failures.


53-53: LGTM! Corrected error condition check.

The condition now properly checks for bech32_encode failure. According to the function signature in src/segwit_addr.c (lines 48-82), bech32_encode returns 1 on success and 0 on failure, so !bech32_encode(...) is the correct failure check.

fuzzing/fuzz_local/hexutils_fuzzer.cpp (1)

11-32: LGTM! Effective fuzzer for hex string parsing.

The implementation correctly:

  • Creates null-terminated strings from arbitrary input data
  • Uses appropriate buffer sizes for testing
  • Follows standard LibFuzzer conventions
  • Tests error handling paths by feeding non-hex characters to parseHexString

The fuzzer will effectively test both valid hex parsing and error conditions with malformed input.

fuzzing/fuzz_local/analyze_local_crashes.py (1)

12-31: LGTM! Well-structured crash analyzer wrapper.

The script correctly:

  • Determines its directory location and parent fuzzing directory
  • Changes to the appropriate working directory for execution
  • Constructs the command with proper arguments for the common analyzer
  • Provides clear status output to the user
  • Returns the subprocess exit code appropriately
fuzzing/fuzz_local/bech32_fuzzer.cpp (8)

12-14: LGTM! Appropriate minimum input size validation.

The check for minimum 4 bytes ensures there's enough data to extract the three parameter bytes plus at least one byte of actual input data.


17-19: LGTM! Well-designed parameter extraction.

The parameter extraction effectively uses input bytes to control:

  • HRP length (1-16 characters, reasonable range)
  • Padding flag (0 or 1, correct binary choice)
  • Encoding type (BECH32 or BECH32M, proper enum casting)

26-28: LGTM! Proper input size limiting.

Limiting input size to MAX_INPUT_SIZE prevents excessive memory usage during fuzzing and aligns with the function's documented constraints from src/bech32.c lines 25-57.


35-40: LGTM! Safe HRP construction with proper bounds checking.

The HRP construction:

  • Uses a fixed-size buffer with null termination
  • Bounds checking with i < 16 prevents buffer overflow
  • Generates valid lowercase ASCII characters (a-z) suitable for HRP
  • Ensures null termination at the correct position

42-44: LGTM! Generous buffer sizing prevents overflow issues.

The 256-byte output buffer provides ample space for encoded results, preventing buffer overflow issues during fuzzing while remaining reasonable in size.


47-53: LGTM! Proper fuzzing pattern with result suppression.

The code correctly:

  • Calls the function under test with constructed parameters
  • Suppresses the unused result warning with (void)result
  • Returns 0 unconditionally, which is the standard fuzzing pattern for crash detection

11-54: Well-structured fuzzer implementation.

The fuzzer follows LibFuzzer best practices:

  • Proper input validation and early returns
  • Parameter extraction from input data
  • Reasonable size limits and buffer management
  • Correct extern "C" linkage for the harness function
  • Safe handling of the function under test

42-44: Output buffer size is sufficient.

Based on the implementation in bech32EncodeFromBytes (which requires at most hrp_len + (in_len * 2) + 7 bytes—and in the worst case 16 + 128 + 7 = 151), the 256-byte stack buffer in the fuzzer is more than adequate to cover all inputs (with MAX_INPUT_SIZE = 64 and hrp_len ≤ 16). No change required.

Makefile (4)

4-4: LGTM! Proper .PHONY target additions.

All new fuzzing targets are correctly declared as phony targets to ensure they always execute regardless of file presence.


32-32: LGTM! Format target appropriately extended.

Adding the fuzzing directory to the format target ensures consistent code formatting across the entire project, including the new fuzzing infrastructure.


36-46: LGTM! Clean proxy target implementation.

The fuzzing proxy targets:

  • Use @cd fuzzing && $(MAKE) pattern for clean delegation
  • Preserve make variable inheritance with $(MAKE)
  • Use @ prefix to suppress command echoing for cleaner output
  • Cover all essential fuzzing operations (build, run, crash analysis, clean)

48-54: LGTM! Helpful user guidance.

The help target provides clear instructions about:

  • The relocation of fuzzing commands
  • Two options for accessing fuzzing functionality (proxy vs direct)
  • Specific examples of available commands
fuzzing/fuzz_paths.py (3)

8-15: LGTM! Clean centralized path configuration.

The FuzzPaths class provides a well-organized approach to path management with:

  • Clear constant names that match their purpose
  • Consistent naming convention
  • Logical directory structure for fuzzing operations

17-35: LGTM! Consistent class method implementation.

All class methods follow the same pattern:

  • Use @classmethod decorator appropriately
  • Consistent naming convention with get_*_path format
  • Clear docstrings explaining the return value
  • Consistent path construction using f-strings with class constants

8-35: Excellent centralized path management design.

This module provides a clean, maintainable approach to path configuration across the fuzzing infrastructure. The class-based design with constants and accessor methods promotes consistency and makes path management changes easier to implement.

fuzzing/fuzz_local/base58_fuzzer.cpp (8)

12-14: LGTM! Appropriate zero-size input handling.

Early return for zero-size input prevents unnecessary processing and potential edge case issues.


17-18: LGTM! Reasonable buffer sizing for decode output.

The 256-byte buffer provides adequate space for decoded Base58 output while remaining memory-efficient for fuzzing.


21-28: LGTM! Proper memory management with null termination.

The code correctly:

  • Allocates memory for null-terminated string required by decode_base58
  • Copies input data and adds null terminator
  • Properly deallocates memory with delete[]
  • No memory leak potential

31-32: LGTM! Appropriate buffer sizing for encode output.

The 512-byte buffer provides generous space for Base58 encoded output, which can be larger than the input due to the encoding expansion factor.


25-26: LGTM! Proper fuzzing pattern with result suppression.

All function calls correctly:

  • Capture return values to prevent compiler optimizations
  • Use (void)result to suppress unused variable warnings
  • Follow standard fuzzing practices for crash detection

Also applies to: 34-35, 39-40


38-41: LGTM! Safe conditional testing of encode_base58_clip.

The size check prevents accessing data[0] when the input is empty, and the function call is appropriate for testing the single-byte encoding function.


11-44: Well-structured multi-function fuzzer.

The fuzzer effectively tests all three base58 functions with appropriate input handling:

  • Proper early return for empty input
  • Correct string handling for decode function
  • Safe buffer management for outputs
  • Good coverage of the base58 API surface

17-18: Buffer sizes are sufficient for Base58 operations

Verification shows that in src/base58.c:

  • decode_base58 uses a maximum input size of 164 bytes (MAX_DEC_INPUT_SIZE).
  • encode_base58 allocates an internal buffer of (120 * 138/100 + 1) = 166 bytes (MAX_ENC_INPUT_SIZE).

The fuzzer’s decode_output (256 bytes) and encode_output (512 bytes) both exceed these maximums with ample headroom. No changes are required.

fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (4)

16-31: Excellent parameter extraction and validation.

The fuzzer properly extracts and constrains parameters from the input data:

  • HRP length constrained to 1-16 characters (spec compliant)
  • Witness version limited to 0-16 (correct range)
  • Encoding type properly selected between BECH32/BECH32M
  • Program length correctly bounded to 2-40 bytes per segwit specification

This demonstrates good fuzzing practices with proper input validation.


33-38: LGTM!

The HRP generation logic correctly creates lowercase-only strings within the bech32 specification limits, with proper null termination and bounds checking.


40-53: LGTM!

The segwit address encoding/decoding test is well-structured with appropriate buffer sizes and proper conditional testing (only decode if encode succeeds).


55-74: LGTM!

The bech32 encoding/decoding test properly converts data to 5-bit format and follows good fuzzing practices with appropriate buffer management and conditional testing.

fuzzing/fuzz_local/run_local_fuzz.py (3)

1-18: LGTM!

The script setup is clean with proper shebang, clear documentation, and well-structured argument parsing with reasonable defaults.


20-29: LGTM!

The path resolution and environment setup correctly handles the fuzzing directory structure and ensures proper working directory for log placement.


31-41: LGTM!

The command construction and subprocess execution are properly implemented with good user feedback and environment handling.

Also applies to: 43-46

fuzzing/fuzz_local/CMakeLists.txt (4)

1-22: LGTM!

The CMake configuration is well-structured with appropriate version requirements, C++17 standard, platform-specific handling for macOS, and proper conditional directory creation.


24-44: LGTM!

The path setup and bech32 library configuration correctly organizes related source files and sets up proper include directories for the static library approach.


46-89: LGTM!

The modular library approach with consistent patterns for each component (base58, base64, hexutils) provides excellent separation of concerns and maintainability.


91-92: LGTM!

Reusing the bech32 static library for the segwit_addr fuzzer is an efficient design choice since segwit address functionality depends on bech32 encoding.

fuzzing/fuzz_local/fuzz_config.py (1)

18-26: LGTM!

The fuzzer configuration values are well-tailored for each component type, with appropriate maximum input lengths that reflect the typical use cases and constraints of each encoding format.

src/segwit_addr.c (2)

29-31: LGTM!

The stylistic changes to use explicit uint32_t casts instead of mixed suffixes improve code consistency while maintaining identical functionality.


152-160: Excellent security and robustness improvements.

The changes to convert_bits add crucial input validation and overflow protection:

  • Input validation prevents processing values that exceed the specified bit width
  • max_acc mask prevents accumulator overflow
  • Early return on invalid input improves efficiency
  • These changes are particularly valuable for fuzzing scenarios where malformed inputs are common

The modifications maintain functional correctness while significantly improving defensive programming practices.

tests/bech32.cpp (2)

33-37: Excellent correction to align with BIP173 specification!

The change from expecting success to zxerr_encoding_failed for pad=0 is correct. According to BIP173, when the input byte length isn't a multiple of 5 bits and padding is disabled (pad=0), the conversion should fail. This fix ensures the tests properly validate the implementation's compliance with the specification.


146-730: Outstanding test coverage expansion!

The addition of these comprehensive test cases significantly improves the robustness of the Bech32 encoding implementation. The tests cover:

  • BIP173 compliance and reference vectors
  • Edge cases and buffer boundaries
  • Empty/null input handling
  • Various HRP lengths including the 83-character limit
  • Maximum input sizes and bech32's 90-character output limit
  • Comparison between Bech32 and Bech32m encodings
  • Special patterns (all zeros, all ones, alternating bytes)
  • Verified hex test vectors with expected outputs

The test vectors appear to be generated from the reference implementation (sipa/bech32) and provide excellent coverage for the fuzzing infrastructure being added.

fuzzing/analyze_crashes.py (2)

20-30: Clean refactoring to use centralized path configuration!

The constructor simplification from separate project_root and build_dir parameters to a single fuzz_dir parameter improves the interface consistency. Using FuzzPaths constants for directory names ensures consistency across the fuzzing infrastructure.


217-225: Good CLI simplification!

The change from --project-root and --build-dir arguments to a single mandatory --fuzz-dir argument simplifies the interface and aligns with the centralized directory structure approach used throughout the fuzzing infrastructure.

fuzzing/run_fuzzers.py (2)

42-59: Consistent refactoring with centralized path management!

The constructor changes align perfectly with the analyze_crashes.py refactoring, using the same fuzz_dir approach and FuzzPaths constants. This ensures consistent directory structure across all fuzzing tools.


443-454: Smart addition of config-dir flexibility!

The --config-dir option provides valuable flexibility for projects to specify where their fuzz_config.py is located, while maintaining backward compatibility by defaulting to fuzz_dir. This is particularly useful for the local fuzzing setup where the configuration resides in fuzz_local/.

fuzzing/Makefile (2)

1-92: Excellent fuzzing lifecycle management implementation!

The Makefile provides comprehensive fuzzing support with:

  • Proper clang compiler checking
  • CMake configuration with sanitizers
  • Configurable fuzzing duration and parallelism
  • Crash analysis integration
  • Clean targets for artifacts

The implementation is well-structured and follows Makefile best practices.


99-143: Clean reorganization of Python formatting targets!

The Python formatting section has been well-organized with:

  • Clear target naming (prefixed with format_)
  • Proper dependency on format_install
  • Addition of format_clean for cache cleanup
  • Helpful output messages

The reorganization improves clarity and maintainability.

Comment thread fuzzing/fuzz_local/fuzz_config.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
fuzzing/Makefile (1)

108-121: Recursive make check triggers duplicate installs and extra work

Calling $(MAKE) check inside the format target re-enters the Makefile, causing format_install to run twice and doubling Black installation checks.

Simpler pattern:

-format: format_install
-	@echo "🎨 Formatting Python files with Black..."
-	@if $(MAKE) check; then \
-		echo "✅ All Python files are already properly formatted - no changes needed"; \
-	else \
-		echo "⚙️  Files need formatting - applying changes..."; \
-		...
-fi
+format: format_install
+	@echo "🎨 Formatting Python files with Black..."
+	@if [ -z "$(PYTHON_FILES)" ]; then \
+		echo "⚠️  No Python files found"; \
+	elif black --check --quiet --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES); then \
+		echo "✅ All Python files are already properly formatted - no changes needed"; \
+	else \
+		echo "⚙️  Applying formatting changes…"; \
+		black --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES); \
+	fi

This eliminates the recursive call and keeps installation checks single-pass.

🧹 Nitpick comments (4)
fuzzing/Makefile (4)

31-34: Inconsistent default for FUZZ_TIME between variable and help text

FUZZ_TIME is initialised to 120 seconds (Line 5) but the help message advertises a default of 600 seconds (Line 32). This will confuse users.

-	@echo "  FUZZ_TIME     Fuzzing duration in seconds (default: 600)"
+	@echo "  FUZZ_TIME     Fuzzing duration in seconds (default: 120)"

—or change the variable to 600 to match the documentation.


45-49: Use command -v instead of which for POSIX-portable detection

which clang is not available on all systems and may emit shell-specific output on failure. Prefer the POSIX-portable:

-	@which clang >/dev/null 2>&1 || { \
+	@command -v clang >/dev/null 2>&1 || { \

This avoids portability pitfalls and false positives.


101-104: Avoid reinstalling Black on every invocation

format_install unconditionally runs pip3 install black, which slows down local workflows and pollutes global environments. Install only when Black is absent:

-	@pip3 install black || { echo "❌ Failed to install Black"; exit 1; }
+	@command -v black >/dev/null 2>&1 || \
+	  { echo "🔧 Installing Black Python formatter…"; \
+	    pip3 install --user black || { echo "❌ Failed to install Black"; exit 1; }; }

This skips needless network calls and honours user-level installations.


145-155: Missing conventional phony targets all, clean, test

Static analysis flagged their absence. Even thin wrappers improve discoverability and CI integration:

+.PHONY: all clean test
+
+# Default aggregate
+all: build_fuzz
+
+# Composite cleaner
+clean: fuzz_clean format_clean
+
+# Placeholder for future automated tests
+test:
+	@echo "🧪 No dedicated fuzzing-Makefile tests defined"

Consider adding these to satisfy tooling and user expectations.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between e60d429 and 4e0a1df.

📒 Files selected for processing (7)
  • fuzzing/Makefile (2 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/base58_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • fuzzing/fuzz_local/run_local_fuzz.py
  • fuzzing/fuzz_local/fuzz_config.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp
  • fuzzing/fuzz_local/bech32_fuzzer.cpp
  • fuzzing/fuzz_local/base58_fuzzer.cpp
  • fuzzing/fuzz_local/analyze_local_crashes.py
🧰 Additional context used
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 17-17: Target body for "help" exceeds allowed length of 5 (20).

(maxbodylength)


[warning] 69-69: Target body for "fuzz" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 108-108: Target body for "format" exceeds allowed length of 5 (13).

(maxbodylength)


[warning] 125-125: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 146-146: Missing required phony target "all"

(minphony)


[warning] 146-146: Missing required phony target "clean"

(minphony)


[warning] 146-146: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: lint

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
fuzzing/Makefile (4)

54-66: Capture CMake failures early & keep logs.

If CMake fails the subsequent make still executes because both are chained in the same shell with &&. While && stops on error, the log output is lost when the subshell exits. Consider emitting a visible error and preserving CMake’s log for debugging:

@@ build_fuzz: check_clang
-	@mkdir -p $(FUZZ_BUILD_DIR)
-	@cd $(FUZZ_BUILD_DIR) && \
-		cmake -DCMAKE_C_COMPILER=clang \
+	@mkdir -p $(FUZZ_BUILD_DIR)
+	@cd $(FUZZ_BUILD_DIR) && \
+		cmake -DCMAKE_C_COMPILER=clang \
 			  -DCMAKE_CXX_COMPILER=clang++ \
 			  -DENABLE_FUZZING=ON \
 			  -DENABLE_SANITIZERS=ON \
 			  -DCMAKE_BUILD_TYPE=Debug \
-			  .. && \
-		make -j$(FUZZ_JOBS)
+			  .. | tee build.cmake.log && \
+		make -j$(FUZZ_JOBS)

This preserves build.cmake.log for post-mortem analysis.


85-93: Expose a top-level clean target for CI & developer ergonomics.

Several tooling conventions (including checkmake) expect a generic clean target. You can reuse the existing fuzz_clean and format_clean logic for a single entry point.

+.PHONY: clean
+clean: fuzz_clean format_clean
+	@echo "🧽 Global clean completed"

This also satisfies the static-analysis warnings about the missing clean target.


100-106: Deduplicate installer message & use the chosen Python interpreter for pip.

  1. Two identical “Installing Black Python formatter…” messages are printed (Lines 102 and 104).
  2. Hard-coding pip3 may mismatch the PYTHON interpreter override.
@@ format_install:
-	@echo "🔧 Installing Black Python formatter..."
-	@command -v black >/dev/null 2>&1 || \
-	  { echo "🔧 Installing Black Python formatter…"; \
-	    pip3 install --user black || { echo "❌ Failed to install Black"; exit 1; }; }
+	@echo "🔧 Checking Black installation..."
+	@command -v black >/dev/null 2>&1 || { \
+	    echo "🔧 Installing Black Python formatter…"; \
+	    $(PYTHON) -m pip install --user black || { echo "❌ Failed to install Black"; exit 1; }; }

112-119: Edge-case: extremely large $(PYTHON_FILES) may exceed shell argv limit.

For very large corpora the expanded $(PYTHON_FILES) string can hit /bin/sh argument limits. Mitigate by passing the list via xargs:

-	elif black --check --quiet --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES); then \
+	elif printf '%s\n' $(PYTHON_FILES) | xargs black --check --quiet --line-length $(BLACK_LINE_LENGTH); then \

Same applies where you invoke black for reformatting. Not critical today, but future-proofs the task.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0a1df and 7e12aa0.

📒 Files selected for processing (7)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/base58_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • fuzzing/fuzz_local/run_local_fuzz.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp
  • fuzzing/fuzz_local/analyze_local_crashes.py
  • fuzzing/fuzz_local/fuzz_config.py
  • fuzzing/fuzz_local/base58_fuzzer.cpp
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp
🧰 Additional context used
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 144-144: Missing required phony target "all"

(minphony)


[warning] 144-144: Missing required phony target "clean"

(minphony)


[warning] 144-144: Missing required phony target "test"

(minphony)


[warning] 17-17: Target body for "help" exceeds allowed length of 5 (20).

(maxbodylength)


[warning] 69-69: Target body for "fuzz" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 110-110: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 123-123: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: lint

Comment thread fuzzing/Makefile Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (1)
fuzzing/Makefile (1)

45-51: Add explicit clang++ availability check (same as prior review)

The build uses clang++ (Line 60). Check it early to avoid late failures.

 check_clang:
 	@command -v clang >/dev/null 2>&1 || { \
 		echo "❌ Error: clang not found. Please install clang for fuzzing."; \
 		echo "On macOS: brew install llvm"; \
 		echo "On Ubuntu: apt install clang"; \
 		exit 1; \
 	}
+	@command -v clang++ >/dev/null 2>&1 || { \
+		echo "❌ Error: clang++ not found. Please install the C++ frontend (often in the same package as clang)."; \
+		exit 1; \
+	}
 	@echo "✅ Clang found: $$(clang --version | head -n1)"
🧹 Nitpick comments (5)
fuzzing/fuzz_local/analyze_local_crashes.py (3)

13-16: Avoid duplicate sys.path entries and normalize paths

Insert realpaths only if not already present. This prevents accidental duplication and makes imports more predictable.

-# Add paths to ensure we can import everything
-sys.path.insert(0, current_dir)  # Add fuzz_local to path for fuzz_config
-sys.path.insert(0, fuzzing_dir)  # Add fuzzing to path for analyze_crashes
+# Add paths to ensure we can import everything (dedup + realpath)
+current_dir = os.path.realpath(current_dir)
+fuzzing_dir = os.path.realpath(fuzzing_dir)
+if current_dir not in sys.path:
+    sys.path.insert(0, current_dir)   # Add fuzz_local to path for fuzz_config
+if fuzzing_dir not in sys.path:
+    sys.path.insert(0, fuzzing_dir)   # Add fuzzing to path for analyze_crashes

21-26: Send error messages to stderr

Minor ergonomics: write errors to stderr so tooling can separate status from errors.

-    if not os.path.exists(analyzer_script):
-        print(f"Error: Analyzer script not found at {analyzer_script}")
-        sys.exit(1)
+    if not os.path.exists(analyzer_script):
+        print(f"Error: Analyzer script not found at {analyzer_script}", file=sys.stderr)
+        sys.exit(1)

34-37: Print absolute build directory and handle absolute FUZZ_BUILD_DIR

If FUZZ_BUILD_DIR is already absolute, joining can mislead. Show the resolved absolute path.

-    print(f"Build directory: {os.path.join(current_dir, FUZZ_BUILD_DIR)}")
+    build_dir = FUZZ_BUILD_DIR if os.path.isabs(FUZZ_BUILD_DIR) else os.path.join(current_dir, FUZZ_BUILD_DIR)
+    print(f"Build directory: {os.path.abspath(build_dir)}")
fuzzing/Makefile (2)

20-27: Include build_fuzz_coverage in help for discoverability

Advertise the new coverage build target.

 	@echo "=== FUZZING TARGETS ==="
 	@echo "  build_fuzz    Build fuzzer targets"
+	@echo "  build_fuzz_coverage Build with coverage instrumentation (for reports)"
 	@echo "  fuzz          Run all fuzzers (uses config from fuzz_config.py)"
 	@echo "  fuzz_crash    Analyze any crashes found"
 	@echo "  fuzz_clean    Clean fuzzing artifacts"
 	@echo "  fuzz_report   Generate coverage report"
 	@echo "  fuzz_report_html Generate HTML coverage report"

84-90: Use FUZZ_COVERAGE_DIR consistently in clean

Avoid hardcoding path duplicates; prefer the variable you already defined.

 	@rm -rf $(FUZZ_BUILD_DIR)
 	@rm -rf $(FUZZ_DIR)/corpora
 	@rm -rf $(FUZZ_DIR)/logs
-	@rm -rf $(FUZZ_DIR)/coverage
+	@rm -rf $(FUZZ_COVERAGE_DIR)
 	@echo "✅ Fuzzing artifacts cleaned"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 7e12aa0 and 585a00e.

📒 Files selected for processing (4)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • fuzzing/fuzz_local/run_local_fuzz.py
  • fuzzing/fuzz_local/fuzz_config.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
fuzzing/fuzz_local/analyze_local_crashes.py (2)
fuzzing/analyze_crashes.py (1)
  • main (215-230)
fuzzing/run_fuzzers.py (1)
  • main (437-482)
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 173-173: Missing required phony target "all"

(minphony)


[warning] 173-173: Missing required phony target "clean"

(minphony)


[warning] 173-173: Missing required phony target "test"

(minphony)


[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 139-139: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 152-152: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: lint
🔇 Additional comments (3)
fuzzing/fuzz_local/analyze_local_crashes.py (2)

27-30: LGTM: default --fuzz-dir wiring

Good UX: auto-injects project-local fuzz_dir only when not provided by the user.


1-39: Overall wrapper is solid

Behavior is predictable, import errors are handled, and return codes are propagated. With the small nits above, this looks ready.

fuzzing/Makefile (1)

68-75: LGTM: Fuzz run orchestration

Clear messaging, uses project config, and clean dependency on build_fuzz. Good.

Comment thread fuzzing/Makefile Outdated
Comment thread fuzzing/Makefile Outdated
Comment thread fuzzing/Makefile Outdated
Comment thread fuzzing/Makefile Outdated
Comment thread fuzzing/Makefile Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (6)
fuzzing/Makefile (6)

129-134: Install Black via the current interpreter

Using pip3 directly can hit PATH/env mismatches. Switch to $(PYTHON) -m pip install --user black to ensure the formatter is installed in the same environment the Makefile is using.


15-32: Conventional PHONY targets & checkmake suppression still missing

checkmake continues to warn about maxbodylength and the absence of the all, clean, and test phony targets. Please add the short wrapper targets and, if you intend to keep the long help body, explicitly disable the maxbodylength rule for that block.


45-51: clang++ availability is not verified

build_fuzz unconditionally invokes clang++, but check_clang only checks for clang. Add an explicit probe for the C++ frontend to fail fast with a clear message on systems where it is missing.


59-65: Expose coverage toggle & convenience target

The CMake invocation still lacks -DENABLE_COVERAGE passthrough, and there is no build_fuzz_coverage helper that sets it. Re-introduce the variable forwarding and the convenience target so users can build coverage-instrumented fuzzers without editing the Makefile.


93-105: Missing tool/empty-data guards in fuzz_report

llvm-profdata/llvm-cov availability and the presence of .profraw files are not checked, leading to cryptic failures. Apply the earlier check_llvm_cov_tools target and .profraw guard.


109-121: Mirror the same safeguards in fuzz_report_html

The HTML report target should replicate the tool checks and .profraw existence guard for consistent behaviour.

🧹 Nitpick comments (3)
tests/bech32.cpp (3)

16-23: Add missing headers & drop unused one

std::vector (Line 55) and std::cout are used but their headers are not explicitly included, while <zxformat.h> appears unused.

 #include <hexutils.h>
-#include <zxformat.h>
+#include <vector>
+#include <iostream>

Explicit includes avoid hidden transitive dependencies and keep the compile stable across library updates.


78-80: Use size_t for loop index

The loop compares an int i against size_t (declared_size, sizeof(addr_out)), which can trigger signed/unsigned warnings and UB with very large buffers.

-    for (int i = declared_size; i < sizeof(addr_out); i++) {
+    for (size_t i = declared_size; i < sizeof(addr_out); i++) {

145-148: Consider parameterised / table-driven tests

Many blocks differ only by constant vectors and expected strings. Using INSTANTIATE_TEST_SUITE_P or a simple helper routine would reduce ~700 LOC to a compact table, speeding up compilation and easing future maintenance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 585a00e and db31422.

📒 Files selected for processing (20)
  • .gitignore (1 hunks)
  • Makefile (2 hunks)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/analyze_crashes.py (5 hunks)
  • fuzzing/fuzz_local/CMakeLists.txt (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/base58_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/base64_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/hexutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_paths.py (1 hunks)
  • fuzzing/run_fuzzers.py (5 hunks)
  • include/zxversion.h (1 hunks)
  • src/base58.c (1 hunks)
  • src/bech32.c (1 hunks)
  • src/segwit_addr.c (2 hunks)
  • tests/bech32.cpp (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • fuzzing/fuzz_local/CMakeLists.txt
🚧 Files skipped from review as they are similar to previous changes (17)
  • include/zxversion.h
  • .gitignore
  • fuzzing/fuzz_local/hexutils_fuzzer.cpp
  • Makefile
  • src/bech32.c
  • fuzzing/fuzz_paths.py
  • src/segwit_addr.c
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp
  • fuzzing/fuzz_local/base64_fuzzer.cpp
  • fuzzing/fuzz_local/base58_fuzzer.cpp
  • fuzzing/fuzz_local/run_local_fuzz.py
  • fuzzing/fuzz_local/bech32_fuzzer.cpp
  • fuzzing/fuzz_local/fuzz_config.py
  • fuzzing/fuzz_local/analyze_local_crashes.py
  • src/base58.c
  • fuzzing/analyze_crashes.py
  • fuzzing/run_fuzzers.py
🧰 Additional context used
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 139-139: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 152-152: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 173-173: Missing required phony target "all"

(minphony)


[warning] 173-173: Missing required phony target "clean"

(minphony)


[warning] 173-173: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: lint
🔇 Additional comments (1)
tests/bech32.cpp (1)

346-349: Guard prefix-length math

prefix_len is estimated with integer math that can overshoot the real string length for certain inputs.
Although strncmp stops at a NUL byte, passing a larger n than the actual string length still asks it to read more bytes than exist, which is undefined behaviour on some libc implementations.

Consider:

size_t actual_len = strlen(addr_out_bech32);          // safe upper bound
size_t prefix_len = std::min(actual_len,
                              strlen("bc1") + (data_bits + 4) / 5);
EXPECT_EQ(strncmp(addr_out_bech32, addr_out_bech32m, prefix_len), 0);

or simply compare the fixed “bc1” HRP prefix explicitly.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (6)
fuzzing/Makefile (6)

59-66: ENABLE_COVERAGE knob and convenience target still missing

The CMake invocation does not pass an ENABLE_COVERAGE variable, and the suggested build_fuzz_coverage target is absent, limiting CI/UX flexibility.


144-150: pip3 hard-coded – use the configured interpreter

Re-raise prior note: replace pip3 install … with $(PYTHON) -m pip install … to avoid environment mismatches.


15-36: Missing conventional .PHONY targets and max-body-length suppression

Static analysis still warns about absent all, clean, test phony targets and long help bodies, exactly as mentioned in the previous review. Please add the stub targets (mapping e.g. all: help) and disable maxbodylength where intentional.


42-51: Still no clang++ availability check

Prior feedback asked to verify both clang and clang++. The recipe only checks clang, so build will fail on hosts lacking the C++ front-end.


93-105: No tool checks / .profraw guard in fuzz_report

The target will fail cryptically if llvm-profdata or llvm-cov are missing, or if the coverage directory lacks .profraw files—issues highlighted earlier.


107-136: fuzz_report_html mirrors the same missing guards

HTML report generation repeats the above problems (tool presence, empty .profraw). Please apply the same defensive checks.

🧹 Nitpick comments (1)
fuzzing/Makefile (1)

120-126: Hard-coded fuzzer list limits extensibility

llvm-cov show manually enumerates five fuzzers. Any new target requires editing the Makefile. Consider generating the list dynamically:

-	@llvm-cov show \
-		$(FUZZ_BUILD_DIR)/fuzz-base58 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-base64 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-bech32 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-hexutils \
-		-object $(FUZZ_BUILD_DIR)/fuzz-segwit_addr \
+	@first=true; \
+	for f in $(FUZZ_BUILD_DIR)/fuzz-*; do \
+		if [ $$first = true ]; then \
+			cmd="llvm-cov show $$f"; first=false; \
+		else \
+			cmd="$$cmd -object $$f"; \
+		fi; \
+	done; \
+	$$cmd \
 		-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
 		-format=html \
 		-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \

This scales automatically with future fuzzers.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between db31422 and e09389d.

📒 Files selected for processing (2)
  • Makefile (2 hunks)
  • fuzzing/Makefile (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile
🧰 Additional context used
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 188-188: Missing required phony target "all"

(minphony)


[warning] 188-188: Missing required phony target "clean"

(minphony)


[warning] 188-188: Missing required phony target "test"

(minphony)


[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 154-154: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 167-167: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (c-cpp)

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
include/zxformat.h (2)

160-184: Overflow handling misses INT64_MIN; negative bound incorrect

Parsing “-9223372036854775808” should succeed (INT64_MIN). Current check rejects it because it caps at INT64_MAX only. Use an unsigned accumulator with signed-aware threshold.

-    int64_t value = 0;
-    const int64_t max_value = INT64_MAX;
-    
-    for (const char *s = start; s < end; s++) {
-        int64_t delta = (*s - '0');
-        if (delta >= 0 && delta <= 9) {
-            // Check for overflow before multiplication and addition
-            if (value > (max_value - delta) / 10) {
-                if (error != NULL) {
-                    *error = 1;
-                }
-                return 0;
-            }
-            
-            value = value * 10 + delta;
-        } else {
-            if (error != NULL) {
-                *error = 1;
-                return 0;
-            }
-        }
-    }
-
-    return value * sign;
+    uint64_t value = 0;
+    const uint64_t limit = (sign < 0) ? ((uint64_t)INT64_MAX + 1u) : (uint64_t)INT64_MAX;
+
+    for (const char *s = start; s < end; s++) {
+        uint64_t delta = (uint64_t)(*s - '0');
+        if (delta <= 9u) {
+            // Check for overflow before multiplication and addition
+            if (value > (limit - delta) / 10u) {
+                if (error != NULL) {
+                    *error = 1;
+                }
+                return 0;
+            }
+            value = value * 10u + delta;
+        } else {
+            if (error != NULL) {
+                *error = 1;
+            }
+            return 0;
+        }
+    }
+
+    if (sign < 0) {
+        if (value == ((uint64_t)INT64_MAX + 1u)) {
+            return INT64_MIN;
+        }
+        return -(int64_t)value;
+    }
+    return (int64_t)value;

372-380: Bug: array_to_lowercase calls to_uppercase

This reverses the intended behavior and will uppercase the array instead of lowercasing it.

-    for (uint16_t i = 0; i < inputLen; i++) {
-        to_uppercase(input + i);
-    }
+    for (uint16_t i = 0; i < inputLen; i++) {
+        to_lowercase(input + i);
+    }
♻️ Duplicate comments (7)
fuzzing/fuzz_local/fuzz_config.py (1)

18-29: Avoid fallback class drift; prefer single source of truth

Using a local fallback definition for FuzzConfig risks divergence if the upstream class gains fields or behavior. Prefer importing the canonical class (and failing fast) or moving it to a shared module (e.g., fuzz_paths.py) that both import.

-try:
-    from run_fuzzers import FuzzConfig
-except ImportError:
-    # Fallback if common module is not available
-    class FuzzConfig:
-        def __init__(self, name: str, max_len: int = 17000):
-            self.name = name
-            self.max_len = max_len
+from run_fuzzers import FuzzConfig

Alternatively, relocate FuzzConfig to a shared place (fuzz_paths.py or a new fuzz_types.py) and import it from there in both modules.

fuzzing/Makefile (6)

17-39: Resolve checkmake warnings: disable maxbodylength and add conventional phony targets

Add the conventional all/clean/test targets and disable maxbodylength for verbose targets.

-# Default target
-.PHONY: help
+# Default target
+# checkmake: disable=maxbodylength
+.PHONY: help
 help:
@@
-# Format all Python files
+# checkmake: disable=maxbodylength
 # Format all Python files
@@
-# Check formatting without making changes
+# checkmake: disable=maxbodylength
 # Check formatting without making changes

Add conventional targets (map to existing ones):

.PHONY: all clean test
all: help
clean: fuzz_clean format_clean
test: check

Verification:

  • Run: checkmake fuzzing/Makefile
  • Expected: no maxbodylength for help/format/check and no minphony warnings.
#!/bin/bash
checkmake fuzzing/Makefile || true

Also applies to: 157-166, 170-179


44-51: Also check for clang++ (build uses it)

 check_clang:
 	@command -v clang >/dev/null 2>&1 || { \
 		echo "❌ Error: clang not found. Please install clang for fuzzing."; \
 		echo "On macOS: brew install llvm"; \
 		echo "On Ubuntu: apt install clang"; \
 		exit 1; \
 	}
+	@command -v clang++ >/dev/null 2>&1 || { \
+		echo "❌ Error: clang++ not found. Please install the C++ frontend (often part of the same package)."; \
+		exit 1; \
+	}
 	@echo "✅ Clang found: $$(clang --version | head -n1)"

59-66: Expose ENABLE_COVERAGE knob for CMake

 		cmake -DCMAKE_C_COMPILER=clang \
 			  -DCMAKE_CXX_COMPILER=clang++ \
 			  -DENABLE_FUZZING=ON \
 			  -DENABLE_SANITIZERS=ON \
+			  -DENABLE_COVERAGE=$${ENABLE_COVERAGE:-OFF} \
 			  -DCMAKE_BUILD_TYPE=Debug \
 			  .. && \
 		make -j$$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)

Add a convenience target:

.PHONY: build_fuzz_coverage
build_fuzz_coverage:
	@$(MAKE) ENABLE_COVERAGE=ON build_fuzz

And list it in help:

-	@echo "  build_fuzz    Build fuzzer targets"
+	@echo "  build_fuzz    Build fuzzer targets"
+	@echo "  build_fuzz_coverage Build fuzzers with coverage"

93-106: Guard coverage merge and verify llvm-cov tools

Add tool check target:

.PHONY: check_llvm_cov_tools
check_llvm_cov_tools:
	@command -v llvm-profdata >/dev/null 2>&1 || { echo "❌ llvm-profdata not found"; exit 1; }
	@command -v llvm-cov >/dev/null 2>&1 || { echo "❌ llvm-cov not found"; exit 1; }
	@echo "✅ llvm-cov toolkit found"

Harden fuzz_report:

-.PHONY: fuzz_report
-fuzz_report:
+.PHONY: fuzz_report
+fuzz_report: check_llvm_cov_tools
 	@if [ ! -d "$(FUZZ_COVERAGE_DIR)" ] || [ -z "$$(ls -A $(FUZZ_COVERAGE_DIR) 2>/dev/null)" ]; then \
 		echo "Error: Coverage directory is empty. Please run the fuzzer with ENABLE_COVERAGE flag ON"; \
 		exit 1; \
 	fi
+	@if ! ls -1 "$(FUZZ_COVERAGE_DIR)"/*.profraw >/dev/null 2>&1; then \
+		echo "Error: No .profraw files found in $(FUZZ_COVERAGE_DIR)"; \
+		exit 1; \
+	fi
 	@llvm-profdata merge -sparse $(FUZZ_COVERAGE_DIR)/*.profraw -o $(FUZZ_COVERAGE_DIR)/coverage.profdata
 	@for fuzzer in $(FUZZ_BUILD_DIR)/fuzz-*; do \
 		if [ -x "$$fuzzer" ]; then \
 			echo "Coverage for $$(basename $$fuzzer):"; \
 			llvm-cov report $$fuzzer -instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata; \
 		fi; \
 	done

109-135: Mirror coverage guards and tool checks in HTML report; handle missing fuzzers

Replicate guards and build a dynamic object list to avoid failing when some fuzzers are not built.

-.PHONY: fuzz_report_html
-fuzz_report_html:
+.PHONY: fuzz_report_html
+fuzz_report_html: check_llvm_cov_tools
 	@# Check coverage directory exists and has data
 	@if [ ! -d "$(FUZZ_COVERAGE_DIR)" ] || [ -z "$$(ls -A $(FUZZ_COVERAGE_DIR) 2>/dev/null)" ]; then \
 		echo "Error: Coverage directory is empty. Please run the fuzzer with ENABLE_COVERAGE flag ON"; \
 		exit 1; \
 	fi
+	@if ! ls -1 "$(FUZZ_COVERAGE_DIR)"/*.profraw >/dev/null 2>&1; then \
+		echo "Error: No .profraw files found in $(FUZZ_COVERAGE_DIR)"; \
+		exit 1; \
+	fi
 	@# Merge all profraw files into a single profdata file
 	@llvm-profdata merge -sparse $(FUZZ_COVERAGE_DIR)/*.profraw -o $(FUZZ_COVERAGE_DIR)/coverage.profdata
 	@echo "Generating unified coverage report for all fuzzers..."
 	@rm -rf $(FUZZ_COVERAGE_DIR)/report_html_unified
-	@# Generate HTML report with all fuzzer binaries
-	@llvm-cov show \
-		$(FUZZ_BUILD_DIR)/fuzz-base58 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-base64 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-bech32 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-hexutils \
-		-object $(FUZZ_BUILD_DIR)/fuzz-segwit_addr \
-		-object $(FUZZ_BUILD_DIR)/fuzz-bignum \
-		-object $(FUZZ_BUILD_DIR)/fuzz-zxformat \
-		-object $(FUZZ_BUILD_DIR)/fuzz-timeutils \
-		-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
-		-format=html \
-		-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
-		-show-line-counts-or-regions \
-		-show-instantiations \
-		-show-expansions
+	@# Generate HTML report using all existing fuzzers
+	@FUZZERS=$$(ls -1 $(FUZZ_BUILD_DIR)/fuzz-* 2>/dev/null | tr '\n' ' '); \
+	if [ -z "$$FUZZERS" ]; then \
+		echo "Error: No fuzzers found in $(FUZZ_BUILD_DIR)"; exit 1; \
+	fi; \
+	FIRST=$$(echo $$FUZZERS | awk '{print $$1}'); \
+	OBJECTS=$$(echo $$FUZZERS | awk '{for(i=2;i<=NF;i++) printf "-object %s ", $$i}'); \
+	llvm-cov show $$FIRST $$OBJECTS \
+		-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
+		-format=html \
+		-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
+		-show-line-counts-or-regions \
+		-show-instantiations \
+		-show-expansions

147-153: Install Black with the same interpreter used elsewhere

Use $(PYTHON) -m pip for reliability across environments.

 format_install:
 	@echo "🔧 Installing Black Python formatter..."
-	@command -v black >/dev/null 2>&1 || \
-	  { echo "🔧 Installing Black Python formatter…"; \
-	    pip3 install --user black || { echo "❌ Failed to install Black"; exit 1; }; }
+	@command -v black >/dev/null 2>&1 || { \
+	  echo "🔧 Installing Black Python formatter…"; \
+	  $(PYTHON) -m pip install --user black || { echo "❌ Failed to install Black"; exit 1; }; \
+	}
 	@echo "✅ Black installed successfully"
🧹 Nitpick comments (8)
src/bignum.c (1)

75-81: Shift cast fix is good; consider aligning LE shift with BE logic for clarity and consistency

The explicit cast back to uint8_t is correct and removes potential promotion surprises. For readability and parity with bignumBigEndian_to_bcd, consider using a 16-bit temp during shift+add and deriving carry from the pre-shift MSB.

-        for (uint16_t j = 0; j < bcdOutLen; j++) {
-            uint8_t carry2 = (uint8_t)(bcdOut[bcdOutLen - j - 1] > 127u);
-            bcdOut[bcdOutLen - j - 1] = (uint8_t)(bcdOut[bcdOutLen - j - 1] << 1u);
-            bcdOut[bcdOutLen - j - 1] += carry;
-            carry = carry2;
-        }
+        for (uint16_t j = 0; j < bcdOutLen; j++) {
+            const uint16_t idx = bcdOutLen - j - 1;
+            uint8_t carry2 = (uint8_t)((bcdOut[idx] & 0x80u) != 0);
+            uint16_t temp = ((uint16_t)bcdOut[idx] << 1u) + carry;
+            bcdOut[idx] = (uint8_t)(temp & 0xFFu);
+            carry = carry2;
+        }
fuzzing/fuzz_local/timeutils_fuzzer.cpp (2)

46-55: Add more boundary buffer sizes (including 0) to stress snprintf paths

You already test 50/20/10/1. Add 0-length buffers too, and mirror the same for printTimeSpecialFormat to hit additional edge conditions.

     // Test with smaller buffer sizes
     printTime(buffer, 50, timestamp);
     printTime(buffer, 20, timestamp);
     printTime(buffer, 10, timestamp);
     printTime(buffer, 1, timestamp);
+    printTime(buffer, 0, timestamp);
 ...
-    printTimeSpecialFormat(buffer, sizeof(buffer), timestamp);
-    printTimeSpecialFormat(buffer, 50, timestamp);
+    printTimeSpecialFormat(buffer, sizeof(buffer), timestamp);
+    printTimeSpecialFormat(buffer, 50, timestamp);
+    printTimeSpecialFormat(buffer, 1, timestamp);
+    printTimeSpecialFormat(buffer, 0, timestamp);

63-70: Redundant size check

After the early return on Line 33 for size < 8, the check here is redundant. You can call decodeTime/extractTime unconditionally.

-    // Test decodeTime function
-    if (size >= 8) {
-        timedata_t timedata;
-        decodeTime(&timedata, timestamp);
-        
-        // Also test extractTime
-        extractTime(timestamp, &timedata);
-    }
+    // Test decodeTime / extractTime
+    timedata_t timedata;
+    decodeTime(&timedata, timestamp);
+    extractTime(timestamp, &timedata);
fuzzing/fuzz_local/bignum_fuzzer.cpp (2)

59-72: Exercise zero-length buffers too for stronger boundary coverage

Currently bcd_len and out_len are at least 1, skipping the 0-length corner case. Let the fuzzer hit 0 to stress bounds in print/convert routines.

-        const uint16_t bcd_len = (data[0] % 64) + 1;
-        const uint16_t out_len = (data[1] % 128) + 1;
+        const uint16_t bcd_len = (data[0] % 65);     // 0..64
+        const uint16_t out_len = (data[1] % 129);    // 0..128

Note: Calls with out_len < 4 are expected to return bool_false in bcdprint; that’s desirable coverage.


17-21: Remove unused include

isn’t used.

-#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
fuzzing/fuzz_local/zxformat_fuzzer.cpp (3)

33-36: Avoid early return to improve coverage for small inputs

Returning early for size < 4 blocks exercising str_to_int64 with 3-byte inputs. Drop the guard and rely on the per-branch size checks already present.

-    if (size < 4) {
-        return 0;
-    }
+    // Let per-branch guards decide; this improves coverage for small inputs

108-112: Vary the end pointer to exercise more parse paths

Always using end=str+len limits coverage. Randomize end within the buffer to stress partial parses and early terminations.

-        // Find a reasonable end pointer
-        const char *end = str + str_len;
+        // Vary end pointer within [str, str+str_len]
+        const size_t end_offset = data[1] % (str_len + 1);
+        const char *end = str + end_offset;
         char error = 0;
         str_to_int64(str, end, &error);

17-21: Remove unused header include

isn’t used. Drop it to avoid a warning.

-#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between e09389d and 99f93a8.

📒 Files selected for processing (9)
  • fuzzing/FuzzingCommon.cmake (1 hunks)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/fuzz_local/CMakeLists.txt (1 hunks)
  • fuzzing/fuzz_local/bignum_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/timeutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
  • include/zxformat.h (1 hunks)
  • src/bignum.c (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • fuzzing/FuzzingCommon.cmake
🚧 Files skipped from review as they are similar to previous changes (1)
  • fuzzing/fuzz_local/CMakeLists.txt
🧰 Additional context used
🧬 Code Graph Analysis (3)
fuzzing/fuzz_local/timeutils_fuzzer.cpp (1)
src/timeutils.c (4)
  • printTime (133-142)
  • printTimeSpecialFormat (144-153)
  • decodeTime (131-131)
  • extractTime (81-129)
fuzzing/fuzz_local/fuzz_config.py (1)
fuzzing/run_fuzzers.py (2)
  • run_fuzzers (408-434)
  • FuzzConfig (31-36)
fuzzing/fuzz_local/bignum_fuzzer.cpp (1)
src/bignum.c (4)
  • bignumLittleEndian_to_bcd (53-83)
  • bignumLittleEndian_bcdprint (20-51)
  • bignumBigEndian_to_bcd (119-149)
  • bignumBigEndian_bcdprint (85-117)
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 157-157: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 170-170: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 191-191: Missing required phony target "all"

(minphony)


[warning] 191-191: Missing required phony target "clean"

(minphony)


[warning] 191-191: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: lint
🔇 Additional comments (2)
fuzzing/fuzz_local/fuzz_config.py (1)

9-17: Confirm whether these directory/timeout constants are actually consumed

MAX_SECONDS, FUZZER_JOBS, and local directory constants aren’t referenced in this file. Ensure downstream tooling imports and uses them (e.g., run_local_fuzz.py, run_fuzzers.py). If unused, remove to avoid drift with fuzz_paths.py.

fuzzing/Makefile (1)

13-14: Align formatting scope with fuzz_local (summary mismatch)

The summary says formatting is scoped to fuzz_local, but FUZZING_DIR points to "." (the fuzzing folder). Point it to $(FUZZ_DIR) to avoid formatting unrelated Python files.

-FUZZING_DIR := .
+FUZZING_DIR := $(FUZZ_DIR)

Likely an incorrect or invalid review comment.

Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (1)

87-91: Initialize pageCount and avoid aliasing into unrelated buffer
Passing a pointer into an unrelated buffer risks stale reads if the callee treats it as in/out. Use a local and pass its address.

-        const uint8_t pageIdx = data[0] % 10;
-        uint8_t *pageCount = (uint8_t*)(buffer + 100);
-        
-        pageString(outValue, outValueLen, inValue, pageIdx, pageCount);
+        const uint8_t pageIdx = data[0] % 10;
+        uint8_t pageCount = 0;
+
+        pageString(outValue, outValueLen, inValue, pageIdx, &pageCount);
🧹 Nitpick comments (2)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (2)

32-36: Remove early return to improve coverage for small inputs

You already gate each test by size; this early bail prevents exercising valid subcases (e.g., single-byte branches). Dropping it increases coverage density on small seeds.

-    if (size < 4) {
-        return 0;
-    }

190-191: Confirm API contract for NULL-pointer handling in case conversion APIs
If to_uppercase/to_lowercase and array_* variants don’t guarantee NULL safety, these calls will produce spurious crashes attributable to the harness. Consider gating under a macro for negative testing only.

Potential gating:

-        to_uppercase(NULL);
-        to_lowercase(NULL);
+        #if defined(ZXFORMAT_FUZZ_ALLOW_NULL)
+        to_uppercase(NULL);
+        to_lowercase(NULL);
+        #endif

and

-        array_to_uppercase(NULL, array_len);
-        array_to_lowercase(NULL, array_len);
+        #if defined(ZXFORMAT_FUZZ_ALLOW_NULL)
+        array_to_uppercase(NULL, array_len);
+        array_to_lowercase(NULL, array_len);
+        #endif

Also applies to: 206-207

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 99f93a8 and 4056286.

📒 Files selected for processing (2)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
  • include/zxformat.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • include/zxformat.h
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (c-cpp)

Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
@abenso abenso force-pushed the feat/local_fuzz branch 2 times, most recently from 90db69d to c567ec7 Compare August 8, 2025 18:29
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
fuzzing/Makefile (4)

45-51: Also check clang++ availability
Previous review already pointed this out; still unaddressed.


93-106: Add llvm-tool and .profraw guards to avoid cryptic failures
Same recommendation as earlier review.


107-140: Mirror the coverage guards/tool check for HTML report
Comment previously given remains applicable.


147-152: Install Black via the same interpreter instead of raw pip3
Prior suggestion remains valid.

fuzzing/fuzz_local/zxformat_fuzzer.cpp (3)

88-90: Don’t alias pageCount into unrelated buffer

Use a local uint8_t pageCount variable; current alias risks reading stale data.


126-135: Avoid passing uninitialized BIP32 path elements

Only pass the number of entries actually filled; otherwise UB in bip32_to_str.


234-237: Potential out-of-bounds read: length 501 > actual input

Same concern flagged earlier; copy into bounded local buffer before calling.

🧹 Nitpick comments (1)
fuzzing/Makefile (1)

15-17: Declare conventional .PHONY targets (all, clean, test)

Many CI linters expect them; adds zero cost and silences checkmake warnings.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 4056286 and c567ec7.

📒 Files selected for processing (13)
  • Makefile (2 hunks)
  • fuzzing/FuzzingCommon.cmake (1 hunks)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/fuzz_local/CMakeLists.txt (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/bignum_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/timeutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
  • include/zxformat.h (3 hunks)
  • src/bignum.c (1 hunks)
  • tests/macros.cpp (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • fuzzing/FuzzingCommon.cmake
🚧 Files skipped from review as they are similar to previous changes (7)
  • fuzzing/fuzz_local/run_local_fuzz.py
  • Makefile
  • src/bignum.c
  • fuzzing/fuzz_local/analyze_local_crashes.py
  • fuzzing/fuzz_local/bignum_fuzzer.cpp
  • fuzzing/fuzz_local/CMakeLists.txt
  • fuzzing/fuzz_local/timeutils_fuzzer.cpp
🧰 Additional context used
🧬 Code Graph Analysis (1)
fuzzing/fuzz_local/fuzz_config.py (1)
fuzzing/run_fuzzers.py (2)
  • run_fuzzers (408-434)
  • FuzzConfig (31-36)
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 157-157: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 170-170: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 191-191: Missing required phony target "all"

(minphony)


[warning] 191-191: Missing required phony target "clean"

(minphony)


[warning] 191-191: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (c-cpp)
🔇 Additional comments (2)
include/zxformat.h (1)

398-401: LGTM – correct helper used

array_to_lowercase now calls to_lowercase; fixes previous bug.

tests/macros.cpp (1)

439-506: Additional boundary tests are valuable

New cases for str_to_int64 increase confidence in overflow handling.

Comment thread fuzzing/fuzz_local/fuzz_config.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
fuzzing/Makefile (4)

15-18: Resolve checkmake warnings: add conventional phony targets and suppress maxbodylength

Align with tooling expectations by adding all/clean/test and disabling the verbose-body rule for long targets.

@@
-# Default target
+# Default target
+# Disable body-length validation for verbose targets
+# checkmake: disable=maxbodylength
 .PHONY: help
 help:
@@
+.PHONY: all clean test
+all: help
+clean: fuzz_clean format_clean
+test: check

Run this to confirm warnings are cleared:

#!/bin/bash
checkmake fuzzing/Makefile || true

Also applies to: 192-198, 205-214, 226-231


182-188: Good: use $(PYTHON) -m pip for Black install

This avoids PATH/env mismatches and installs into the active interpreter’s site-packages.


66-73: Expose coverage toggle to CMake and add a convenience target

Coverage reports require instrumentation, but there’s no Make-level way to enable it. Pass ENABLE_COVERAGE through to CMake and add build_fuzz_coverage; also list it in help.

@@ build_fuzz: check_clang
 		@cd $(FUZZ_BUILD_DIR) && \
-		cmake -DCMAKE_C_COMPILER=clang \
+		cmake -DCMAKE_C_COMPILER=clang \
 			  -DCMAKE_CXX_COMPILER=clang++ \
 			  -DENABLE_FUZZING=ON \
 			  -DENABLE_SANITIZERS=ON \
+			  -DENABLE_COVERAGE=$${ENABLE_COVERAGE:-OFF} \
 			  -DCMAKE_BUILD_TYPE=Debug \
 			  .. && \
 		make -j$$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
 	@echo "✅ Fuzzer build completed"
+
+.PHONY: build_fuzz_coverage
+build_fuzz_coverage:
+	@$(MAKE) ENABLE_COVERAGE=ON build_fuzz
@@ help:
 	@echo "=== FUZZING TARGETS ==="
 	@echo "  build_fuzz    Build fuzzer targets"
+	@echo "  build_fuzz_coverage Build fuzzers with coverage enabled"
 	@echo "  fuzz          Run all fuzzers (uses config from fuzz_config.py)"

Also applies to: 18-26


100-123: Deduplicate and harden llvm tool checks; ensure llvm-cov is checked for fuzz_report too

fuzz_report uses llvm-cov but only checks llvm-profdata. Create a shared check target and depend on it from both report targets.

+ .PHONY: check_llvm_cov_tools
+ check_llvm_cov_tools:
+	@command -v llvm-profdata >/dev/null 2>&1 || { \
+		echo "❌ Error: llvm-profdata not found. Please install LLVM tools."; \
+		echo "On macOS: brew install llvm"; \
+		echo "On Ubuntu: apt install llvm"; \
+		exit 1; \
+	}
+	@command -v llvm-cov >/dev/null 2>&1 || { \
+		echo "❌ Error: llvm-cov not found. Please install LLVM tools."; \
+		echo "On macOS: brew install llvm"; \
+		echo "On Ubuntu: apt install llvm"; \
+		exit 1; \
+	}
+
 .PHONY: fuzz_report
-fuzz_report:
+fuzz_report: check_llvm_cov_tools
@@
-	@command -v llvm-profdata >/dev/null 2>&1 || { \
-		echo "❌ Error: llvm-profdata not found. Please install LLVM tools."; \
-		echo "On macOS: brew install llvm"; \
-		echo "On Ubuntu: apt install llvm"; \
-		exit 1; \
-	}
+	@# llvm tool availability checked by dependency
@@
 .PHONY: fuzz_report_html
-fuzz_report_html:
+fuzz_report_html: check_llvm_cov_tools
@@
-	@command -v llvm-profdata >/dev/null 2>&1 || { \
-		echo "❌ Error: llvm-profdata not found. Please install LLVM tools."; \
-		echo "On macOS: brew install llvm"; \
-		echo "On Ubuntu: apt install llvm"; \
-		exit 1; \
-	}
-	@command -v llvm-cov >/dev/null 2>&1 || { \
-		echo "❌ Error: llvm-cov not found. Please install LLVM tools."; \
-		echo "On macOS: brew install llvm"; \
-		echo "On Ubuntu: apt install llvm"; \
-		exit 1; \
-	}
+	@# llvm tool availability checked by dependency

Also applies to: 126-132, 133-144

🧹 Nitpick comments (4)
fuzzing/Makefile (1)

150-169: Avoid hardcoding fuzzer binaries; build the coverage object list dynamically

Hardcoding paths makes maintenance brittle and fails if a target is renamed or missing. Discover fuzzers at runtime and construct PRIMARY/-object lists.

-	@# Generate HTML report with all fuzzer binaries
-	@llvm-cov show \
-		$(FUZZ_BUILD_DIR)/fuzz-base58 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-base64 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-bech32 \
-		-object $(FUZZ_BUILD_DIR)/fuzz-hexutils \
-		-object $(FUZZ_BUILD_DIR)/fuzz-segwit_addr \
-		-object $(FUZZ_BUILD_DIR)/fuzz-bignum \
-		-object $(FUZZ_BUILD_DIR)/fuzz-zxformat \
-		-object $(FUZZ_BUILD_DIR)/fuzz-timeutils \
-		-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
-		-format=html \
-		-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
-		-show-line-counts-or-regions \
-		-show-instantiations \
-		-show-expansions
+	@# Generate HTML report with all discovered fuzzer binaries
+	@PRIMARY=$$(ls -1 $(FUZZ_BUILD_DIR)/fuzz-* 2>/dev/null | head -n1); \
+	if [ -z "$$PRIMARY" ]; then \
+		echo "❌ No fuzzer binaries found in $(FUZZ_BUILD_DIR)"; exit 1; \
+	fi; \
+	OBJECTS=$$(for f in $(FUZZ_BUILD_DIR)/fuzz-*; do \
+		[ "$$f" = "$$PRIMARY" ] && continue; \
+		[ -x "$$f" ] && printf " -object %s" "$$f"; \
+	done); \
+	llvm-cov show $$PRIMARY $$OBJECTS \
+		-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
+		-format=html \
+		-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
+		-show-line-counts-or-regions \
+		-show-instantiations \
+		-show-expansions
tests/bech32.cpp (3)

19-19: Remove unused header

#include <zxformat.h> is not referenced anywhere in this file; drop it to avoid needless dependency bloat.


347-348: prefix_len formula is only an estimate

(sizeof(data) * 8 + 4) / 5 can be off-by-one when pad differs; consider calculating the encoded length from the library or comparing only the common HRP+separator "bc1".


78-80: Loop index type mismatch

for (int i = declared_size; i < sizeof(addr_out); i++) mixes signed int with unsigned size_t. Use size_t for i to silence sign-comparison warnings.

-    for (int i = declared_size; i < sizeof(addr_out); i++) {
+    for (size_t i = declared_size; i < sizeof(addr_out); i++) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c567ec7 and 8d46427.

📒 Files selected for processing (27)
  • .gitignore (1 hunks)
  • Makefile (2 hunks)
  • fuzzing/FuzzingCommon.cmake (1 hunks)
  • fuzzing/Makefile (1 hunks)
  • fuzzing/analyze_crashes.py (5 hunks)
  • fuzzing/fuzz_local/CMakeLists.txt (1 hunks)
  • fuzzing/fuzz_local/analyze_local_crashes.py (1 hunks)
  • fuzzing/fuzz_local/base58_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/base64_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bech32_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/bignum_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/fuzz_config.py (1 hunks)
  • fuzzing/fuzz_local/hexutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/run_local_fuzz.py (1 hunks)
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/timeutils_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
  • fuzzing/fuzz_paths.py (1 hunks)
  • fuzzing/run_fuzzers.py (5 hunks)
  • include/zxformat.h (3 hunks)
  • include/zxversion.h (1 hunks)
  • src/base58.c (1 hunks)
  • src/bech32.c (1 hunks)
  • src/bignum.c (1 hunks)
  • src/segwit_addr.c (2 hunks)
  • tests/bech32.cpp (4 hunks)
  • tests/macros.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (25)
  • .gitignore
  • fuzzing/FuzzingCommon.cmake
  • src/base58.c
  • include/zxversion.h
  • fuzzing/fuzz_local/bech32_fuzzer.cpp
  • src/bignum.c
  • fuzzing/fuzz_local/segwit_addr_fuzzer.cpp
  • fuzzing/fuzz_local/base58_fuzzer.cpp
  • fuzzing/fuzz_local/CMakeLists.txt
  • fuzzing/fuzz_local/run_local_fuzz.py
  • include/zxformat.h
  • fuzzing/fuzz_local/hexutils_fuzzer.cpp
  • tests/macros.cpp
  • src/bech32.c
  • fuzzing/fuzz_local/bignum_fuzzer.cpp
  • Makefile
  • fuzzing/fuzz_local/timeutils_fuzzer.cpp
  • fuzzing/fuzz_local/analyze_local_crashes.py
  • fuzzing/analyze_crashes.py
  • fuzzing/fuzz_paths.py
  • fuzzing/fuzz_local/fuzz_config.py
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp
  • src/segwit_addr.c
  • fuzzing/fuzz_local/base64_fuzzer.cpp
  • fuzzing/run_fuzzers.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/bech32.cpp (1)
src/bech32.c (1)
  • bech32EncodeFromBytes (26-58)
🪛 checkmake (0.2.2)
fuzzing/Makefile

[warning] 17-17: Target body for "help" exceeds allowed length of 5 (21).

(maxbodylength)


[warning] 192-192: Target body for "format" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 205-205: Target body for "check" exceeds allowed length of 5 (9).

(maxbodylength)


[warning] 226-226: Missing required phony target "all"

(minphony)


[warning] 226-226: Missing required phony target "clean"

(minphony)


[warning] 226-226: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (c-cpp)
🔇 Additional comments (2)
fuzzing/Makefile (1)

44-58: Good: clang++ availability is checked and surfaced early

This addresses the late-failing build issue and improves UX with clear guidance.

tests/bech32.cpp (1)

238-244: Empty HRP may violate BIP-173

BIP-173 requires the HRP length to be between 1 and 83 characters. Confirm that accepting an empty HRP is intentional and documented; otherwise this test should expect a failure code.

Comment thread tests/bech32.cpp Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (1)

124-136: Clamp used path length to available entries when calling bip32_to_str
Zero-initializing the path helps, but passing pathLen larger than available copied entries still forces the callee to process zeros, which reduces fidelity. Better to reflect actual available entries.

Apply this refactor:

         const uint8_t pathLen = (data[0] % 5) + 1;
 
         // Initialize all path elements to 0
         memset(path, 0, sizeof(path));
 
-        // Only fill the number of elements we actually have data for
-        for (uint8_t i = 0; i < pathLen && static_cast<size_t>(i) * 4 + 4 <= size - 1; i++) {
-            memcpy(&path[i], data + 1 + static_cast<size_t>(i) * 4, sizeof(uint32_t));
-        }
+        // Determine how many complete 32-bit entries are available after the first byte
+        const size_t availableEntries = (size > 1) ? (size - 1) / 4 : 0;
+        uint8_t usedPathLen = pathLen;
+        if ((size_t)usedPathLen > availableEntries) {
+            usedPathLen = static_cast<uint8_t>(availableEntries);
+        }
+        // Fill only the used entries
+        for (uint8_t i = 0; i < usedPathLen; i++) {
+            memcpy(&path[i], data + 1 + static_cast<size_t>(i) * 4, sizeof(uint32_t));
+        }
 
-        bip32_to_str(bip32Buffer, sizeof(bip32Buffer), path, pathLen);
-        bip32_to_str(bip32Buffer, 10, path, pathLen);
-        bip32_to_str(bip32Buffer, 5, path, pathLen);
+        bip32_to_str(bip32Buffer, sizeof(bip32Buffer), path, usedPathLen);
+        bip32_to_str(bip32Buffer, 10, path, usedPathLen);
+        bip32_to_str(bip32Buffer, 5, path, usedPathLen);
         bip32_to_str(bip32Buffer, sizeof(bip32Buffer), path, 0);
         bip32_to_str(bip32Buffer, sizeof(bip32Buffer), path, 6);
🧹 Nitpick comments (1)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (1)

33-35: Drop the global size guard to maximize coverage

Each test block has its own size checks. The early return excludes small inputs that fuzzers often use to discover edge cases.

Apply this diff to remove the guard:

-    if (size < 4) {
-        return 0;
-    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8d46427 and 51a2206.

📒 Files selected for processing (2)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
  • tests/bech32.cpp (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/bech32.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: Analyze (c-cpp)
🔇 Additional comments (1)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (1)

87-91: Good fix: local, initialized pageCount

Using a local uint8_t and passing its address avoids aliasing into unrelated buffers and prevents stale-data leaks.

Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 51a2206 and a0511a7.

📒 Files selected for processing (1)
  • fuzzing/fuzz_local/zxformat_fuzzer.cpp (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (c-cpp)
🔇 Additional comments (1)
fuzzing/fuzz_local/zxformat_fuzzer.cpp (1)

46-50: Buffer size 1 in fuzz harness may overflow single-byte buffer

In fuzzing/fuzz_local/zxformat_fuzzer.cpp (lines 46–50 and 59–63), the harness calls

int32_to_str(buffer, 1, value);
uint32_to_str(buffer, 1, value);

with a single-byte buffer. While the uint32_to_str tests (tests/macros.cpp) show it returns "Buffer too small" for len == 1, there’s no equivalent coverage for int32_to_str. If either function naively writes a NUL terminator or partial data before checking buffer length, these calls could overflow.

Please verify that both int32_to_str and uint32_to_str pre-check bufferLen and never write when it’s too small. If not, update the fuzzer to either:

  • Skip the len == 1 cases
  • Or use a dedicated 2-byte scratch buffer for those calls

Comment thread fuzzing/fuzz_local/zxformat_fuzzer.cpp Outdated
@abenso abenso closed this Aug 8, 2025
@abenso abenso deleted the feat/local_fuzz branch August 8, 2025 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant