Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
# SPDX-License-Identifier: MIT

name: Fuzzing

on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:

permissions:
contents: read

jobs:
fuzz:
name: Fuzz Testing
runs-on: ubuntu-latest

permissions:
contents: read
actions: write

strategy:
fail-fast: false
matrix:
target:
- fuzz_new_from_strings
- fuzz_from_cstrings
- fuzz_pointer_operations
- fuzz_try_from

steps:
- uses: actions/checkout@v4

- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
components: llvm-tools-preview

- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
key: fuzz-${{ matrix.target }}

- name: Install cargo-fuzz
uses: taiki-e/install-action@v2
with:
tool: cargo-fuzz

- name: Run fuzzer
run: cargo fuzz run ${{ matrix.target }} -- -max_total_time=300
env:
RUST_BACKTRACE: 1

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-artifacts-${{ matrix.target }}
path: fuzz/artifacts/${{ matrix.target }}
8 changes: 7 additions & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ SPDX-PackageSupplier = "RAprogramm <andrey.rozanov.vl@gmail.com>"
SPDX-PackageDownloadLocation = "https://github.com/RAprogramm/cstring-array"

[[annotations]]
path = ["Cargo.toml", "Cargo.lock", ".gitignore", ".rustfmt.toml", "REUSE.toml", "codecov.yml", ".config/nextest.toml", "cliff.toml", "CHANGELOG.md", "CONTRIBUTING.md", "SECURITY.md", ".github/ISSUE_TEMPLATE/*.yml", ".github/PULL_REQUEST_TEMPLATE.md"]
path = ["Cargo.toml", "Cargo.lock", ".gitignore", ".rustfmt.toml", "REUSE.toml", "codecov.yml", ".config/nextest.toml", "cliff.toml", "CHANGELOG.md", "CONTRIBUTING.md", "SECURITY.md", ".github/ISSUE_TEMPLATE/*.yml", ".github/PULL_REQUEST_TEMPLATE.md", ".github/workflows/*.yml", "fuzz/Cargo.toml"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 RAprogramm <andrey.rozanov.vl@gmail.com>"
SPDX-License-Identifier = "CC0-1.0"
Expand All @@ -20,3 +20,9 @@ path = "examples/*.rs"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 RAprogramm <andrey.rozanov.vl@gmail.com>"
SPDX-License-Identifier = "MIT"

[[annotations]]
path = "fuzz/fuzz_targets/*.rs"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 RAprogramm <andrey.rozanov.vl@gmail.com>"
SPDX-License-Identifier = "MIT"
4 changes: 4 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
49 changes: 49 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[package]
name = "cstring-array-fuzz"
version = "0.0.0"
publish = false
edition = "2024"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"

[dependencies.cstring-array]
path = ".."

[[bin]]
name = "fuzz_target_1"
path = "fuzz_targets/fuzz_target_1.rs"
test = false
doc = false
bench = false

[[bin]]
name = "fuzz_new_from_strings"
path = "fuzz_targets/fuzz_new_from_strings.rs"
test = false
doc = false
bench = false

[[bin]]
name = "fuzz_from_cstrings"
path = "fuzz_targets/fuzz_from_cstrings.rs"
test = false
doc = false
bench = false

[[bin]]
name = "fuzz_pointer_operations"
path = "fuzz_targets/fuzz_pointer_operations.rs"
test = false
doc = false
bench = false

[[bin]]
name = "fuzz_try_from"
path = "fuzz_targets/fuzz_try_from.rs"
test = false
doc = false
bench = false
22 changes: 22 additions & 0 deletions fuzz/fuzz_targets/fuzz_from_cstrings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
// SPDX-License-Identifier: MIT

#![no_main]

use std::ffi::CString;

use cstring_array::CStringArray;
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
if let Ok(input) = std::str::from_utf8(data) {
let cstrings: Result<Vec<CString>, _> =
input.split('\n').take(10000).map(CString::new).collect();

if let Ok(cstrings) = cstrings {
if !cstrings.is_empty() {
let _ = CStringArray::from_cstrings(cstrings);
}
}
}
});
18 changes: 18 additions & 0 deletions fuzz/fuzz_targets/fuzz_new_from_strings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
// SPDX-License-Identifier: MIT

#![no_main]

use cstring_array::CStringArray;
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
if let Ok(input) = std::str::from_utf8(data) {
let strings: Vec<String> =
input.split('\n').take(10000).map(String::from).collect();

if !strings.is_empty() {
let _ = CStringArray::new(strings);
}
}
});
41 changes: 41 additions & 0 deletions fuzz/fuzz_targets/fuzz_pointer_operations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
// SPDX-License-Identifier: MIT

#![no_main]

use std::ffi::CStr;

use cstring_array::CStringArray;
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
if data.len() < 2 {
return;
}

let num_strings = (data[0] as usize % 100) + 1;
let index = data[1] as usize;

let strings: Vec<String> = (0..num_strings).map(|i| format!("string_{}", i)).collect();

if let Ok(array) = CStringArray::new(strings) {
let _ = array.len();
let _ = array.is_empty();
let ptr = array.as_ptr();

unsafe {
for i in 0..array.len() {
let cstr_ptr = *ptr.offset(i as isize);
if !cstr_ptr.is_null() {
let _ = CStr::from_ptr(cstr_ptr);
}
}
}

let _ = array.get(index);

for s in array.iter() {
let _ = s.to_str();
}
}
});
29 changes: 29 additions & 0 deletions fuzz/fuzz_targets/fuzz_try_from.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
// SPDX-License-Identifier: MIT

#![no_main]

use std::{convert::TryFrom, ffi::CString};

use cstring_array::CStringArray;
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
if let Ok(input) = std::str::from_utf8(data) {
let strings: Vec<&str> = input.split('\n').take(1000).collect();

if !strings.is_empty() {
let _ = CStringArray::try_from(strings.clone());

let owned: Vec<String> = strings.iter().map(|s| s.to_string()).collect();
let _ = CStringArray::try_from(owned);

let cstrings: Result<Vec<CString>, _> =
strings.iter().map(|s| CString::new(*s)).collect();

if let Ok(cstrings) = cstrings {
let _ = CStringArray::try_from(cstrings);
}
}
}
});