blob: bd4d01b365e8c5c9717e92fd0103abc3b02acc56 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright (c) 2025 - Greg Kroah-Hartman <gregkh@linuxfoundation.org>
// Copyright (c) 2025 - Sasha Levin <sashal@kernel.org>
//
// dyad - create a listing of "pairs" of vulnerable:fixed kernels based on a
// specific git SHA that purports to fix an issue. Used in combination
// with 'bippy' to create CVE entries for the Linux kernel. Is VERY
// specific to how the Linux kernel has its stable branches and how it
// labels things.
//
// Usage: dyad --sha1 <git-sha> [--sha1 <git-sha> ...] [--vulnerable <git-sha> ...]
use log::{debug, error};
use owo_colors::{OwoColorize, Stream::Stdout};
use std::env;
extern crate cve_utils;
mod cli;
mod kernel;
mod state;
/// Initialize and configure the logging system
fn initialize_logging(verbose: bool) -> log::LevelFilter {
// Default to error level, but can turn it on based on the command line option
let logging_level = if verbose {
log::LevelFilter::max()
} else {
log::LevelFilter::Error
};
env_logger::builder()
.format_timestamp(None)
.filter_level(logging_level)
.init();
logging_level
}
/// Process fixing SHA1s from command line arguments
fn process_fixing_shas(state: &mut state::DyadState, shas: &[String]) -> bool {
state.git_sha_full.clear(); // Clear any existing values
for git_sha in shas {
if !kernel::process_fixing_sha(state, git_sha) {
error!("Error: The provided git SHA1 '{git_sha}' could not be found in the repository");
return false;
}
}
// Print information about the fixing SHA1s
for (idx, full_id) in state.git_sha_full.iter().enumerate() {
debug!(" Full git id {idx}: '{full_id:?}'");
}
for git_sha in &state.git_sha_full {
println!(
"{} {}",
"# \tgetting vulnerable:fixed pairs for git id"
.if_supports_color(Stdout, |x| x.green()),
git_sha.git_id().if_supports_color(Stdout, |x| x.cyan())
);
}
true
}
/// Process vulnerable SHA1s from command line arguments
fn process_vulnerable_shas(state: &mut state::DyadState, shas: &[String]) -> bool {
for vuln_id in shas {
if !kernel::process_vulnerable_sha(state, vuln_id) {
error!(
"Error: The provided vulnerable git SHA1 '{vuln_id}' could not be found in the repository"
);
return false;
}
}
true
}
/// Process and generate kernel pairs
fn process_kernel_pairs(state: &mut state::DyadState) {
// Find all of the places where each git commit was backported to
kernel::find_fixed_kernels(state);
// Process explicitly provided vulnerable commits
let mut vulnerable_kernels = kernel::add_provided_vulnerabilities(state);
// Derive vulnerabilities from fixing SHAs if needed
kernel::derive_vulnerabilities(state, &mut vulnerable_kernels);
// Find backported vulnerable kernels and winnow the vulnerable set
kernel::process_vulnerable_kernels(state, &vulnerable_kernels);
// Generate kernel pairs from vulnerable and fixed sets
let fixed_pairs = kernel::generate_kernel_pairs(state);
// Filter and sort pairs for consistency
let filtered_pairs = kernel::filter_and_sort_pairs(&fixed_pairs);
// Print the final kernel pairs
kernel::print_kernel_pairs(&filtered_pairs);
}
fn main() {
// Parse command line arguments
let args = cli::parse_args();
let program_name = env!("CARGO_BIN_NAME");
let program_version = env!("CARGO_PKG_VERSION");
// Handle version request
if args.version {
println!("{program_name} version: {program_version}");
std::process::exit(0);
}
// Verify we have at least one SHA1
if args.sha1.is_empty() {
println!("Error: At least one --sha1 value is required\n");
std::process::exit(1);
}
// Initialize logging system
initialize_logging(args.verbose);
// Debug message will only be seen after logger is initialized
debug!("{args:#?}");
// Initialize state
let mut state = state::DyadState::new();
state::validate_env_vars(&mut state);
// output our version for the record to make things easier to track over time
println!(
"{} {} {} {}",
"#".if_supports_color(Stdout, |x| x.green()),
program_name.if_supports_color(Stdout, |x| x.purple()),
"version:".if_supports_color(Stdout, |x| x.green()),
program_version.if_supports_color(Stdout, |x| x.cyan())
);
// Process fixing SHA1s
if !process_fixing_shas(&mut state, &args.sha1) {
std::process::exit(1);
}
// Process vulnerable SHA1s
if !process_vulnerable_shas(&mut state, &args.vulnerable) {
std::process::exit(1);
}
// Process and generate kernel pairs
process_kernel_pairs(&mut state);
}