use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use crate::utility::cert_c::ast_utils::get_node_text;
use tree_sitter::Node;
pub struct Int36C;
impl Int36C {
#[allow(dead_code)]
pub fn new() -> Self {
Self
}
fn is_pointer_type(&self, type_text: &str) -> bool {
type_text.contains('*')
}
fn is_safe_pointer_integer_type(&self, type_text: &str) -> bool {
type_text.contains("uintptr_t") || type_text.contains("intptr_t")
}
fn is_integer_type(&self, type_text: &str) -> bool {
let integer_types = [
"int",
"unsigned",
"long",
"short",
"char",
"size_t",
"ptrdiff_t",
];
integer_types.iter().any(|&t| {
type_text.contains(t)
&& !type_text.contains("uintptr_t")
&& !type_text.contains("intptr_t")
})
}
fn is_zero_constant(&self, node: &Node, source: &str) -> bool {
let text = get_node_text(node, source).trim();
text == "0" || text == "NULL" || text == "nullptr"
}
fn check_pointer_to_integer_cast(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if let (Some(type_node), Some(value_node)) = (
node.child_by_field_name("type"),
node.child_by_field_name("value"),
) {
let type_text = get_node_text(&type_node, source);
let value_text = get_node_text(&value_node, source);
let cast_to_pointer = type_text.contains('*');
let is_dereferenced = value_text.contains("->") || value_text.contains('[');
let has_address_of = value_text.starts_with('&')
|| (value_node.kind() == "pointer_expression" && value_text.starts_with('&'));
let appears_to_be_pointer = has_address_of
|| value_text == "NULL"
|| (!is_dereferenced && value_text.contains("ptr"))
|| (cast_to_pointer && is_dereferenced);
if self.is_integer_type(type_text)
&& !self.is_safe_pointer_integer_type(type_text)
&& !self.is_pointer_type(type_text)
{
if appears_to_be_pointer {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Unsafe pointer-to-integer conversion to '{}' (use uintptr_t or intptr_t for portable pointer storage)",
type_text.trim()
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Use uintptr_t or intptr_t for safe pointer-to-integer conversions".to_string()
),
..Default::default()
});
}
}
}
}
fn check_integer_to_pointer_cast(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if let (Some(type_node), Some(value_node)) = (
node.child_by_field_name("type"),
node.child_by_field_name("value"),
) {
let type_text = get_node_text(&type_node, source);
if self.is_pointer_type(type_text) {
if self.is_zero_constant(&value_node, source) {
return;
}
let value_text = get_node_text(&value_node, source);
if !self.is_safe_pointer_integer_type(&value_text) {
if value_node.kind() == "number_literal"
|| value_node.kind() == "binary_expression"
{
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Unsafe integer-to-pointer conversion from '{}' (may result in misalignment or invalid pointer)",
value_text.trim()
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Use uintptr_t/intptr_t for integer values, or ensure value is obtained from a valid pointer".to_string()
),
..Default::default()
});
}
}
}
}
}
fn check_pointer_initialization(
&self,
node: &Node,
source: &str,
violations: &mut Vec<RuleViolation>,
) {
if node.kind() == "init_declarator" {
if let (Some(declarator), Some(value)) = (
node.child_by_field_name("declarator"),
node.child_by_field_name("value"),
) {
let declarator_text = get_node_text(&declarator, source);
if declarator_text.contains('*') {
if self.is_zero_constant(&value, source) {
return;
}
if value.kind() == "number_literal" {
let value_text = get_node_text(&value, source);
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: format!(
"Pointer '{}' initialized with integer constant '{}' (unknown alignment/validity)",
declarator_text.trim(),
value_text.trim()
),
file_path: String::new(),
line: node.start_position().row + 1,
column: node.start_position().column + 1,
suggestion: Some(
"Use NULL for null pointers, or derive pointer from valid object/array".to_string()
),
..Default::default()
});
}
}
}
}
}
}
impl CertRule for Int36C {
fn rule_id(&self) -> &'static str {
"INT36-C"
}
fn description(&self) -> &'static str {
"Converting a pointer to integer or integer to pointer"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"INT36-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
self.check_node(node, source, &mut violations);
violations
}
}
impl Int36C {
fn check_node(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
match node.kind() {
"cast_expression" => {
self.check_pointer_to_integer_cast(node, source, violations);
self.check_integer_to_pointer_cast(node, source, violations);
}
"init_declarator" => {
self.check_pointer_initialization(node, source, violations);
}
_ => {}
}
for i in 0..node.child_count() {
if let Some(child) = node.child(i) {
self.check_node(&child, source, violations);
}
}
}
}