use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::{find_containing_function, get_node_text};
use tree_sitter::Node;
pub struct Con31C;
impl Con31C {
#[allow(dead_code)]
pub fn new() -> Self {
Con31C
}
fn is_mutex_destroy_function(func_name: &str) -> bool {
matches!(
func_name,
"mtx_destroy" | "pthread_mutex_destroy" | "DeleteCriticalSection"
)
}
fn is_thread_function(&self, node: &Node, source: &str) -> bool {
if let Some(func_node) = find_containing_function(node) {
let func_name_node = func_node.child_by_field_name("declarator");
if let Some(func_name_node) = func_name_node {
let func_name = get_node_text(&func_name_node, source);
if func_name.contains("main") {
return false;
}
return !func_name.contains("main");
}
}
false
}
fn check_function(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "call_expression" {
if let Some(func_node) = child.child_by_field_name("function") {
let func_name = get_node_text(&func_node, source);
if Self::is_mutex_destroy_function(&func_name) {
if self.is_thread_function(&child, source) {
let start = child.start_position();
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: Severity::Medium,
file_path: String::new(),
line: start.row + 1,
column: start.column + 1,
message: format!(
"Mutex destroyed in thread function with '{}()'. \
Destroying a mutex while other threads may still be using it causes undefined behavior. \
Destroy mutexes only after all threads have finished (e.g., after thread joins in main()).",
func_name
),
suggestion: Some(
"Move mutex destruction to main() after all thread joins complete".to_string()
),
..Default::default()
});
}
}
}
}
self.check_function(&child, source, violations);
}
}
}
impl CertRule for Con31C {
fn rule_id(&self) -> &'static str {
"CON31-C"
}
fn description(&self) -> &'static str {
"Do not destroy a mutex while it is locked"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"CON31-C"
}
fn check(&self, root: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_function(root, source, &mut violations);
violations
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rule_id() {
let rule = Con31C::new();
assert_eq!(rule.rule_id(), "CON31-C");
}
#[test]
fn test_description() {
let rule = Con31C::new();
assert_eq!(
rule.description(),
"Do not destroy a mutex while it is locked"
);
}
}