1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024 Ryan Urchick
use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use lang_parsing_substrate::query;
use tree_sitter::Node;
pub struct Exp37C;
impl CertRule for Exp37C {
fn rule_id(&self) -> &'static str {
"EXP37-C"
}
fn description(&self) -> &'static str {
"Call functions with correct arguments"
}
fn severity(&self) -> Severity {
Severity::Medium
}
fn category(&self) -> RuleCategory {
RuleCategory::Rule
}
fn cert_id(&self) -> &'static str {
"EXP37-C"
}
fn scan(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
self.check_node(node, source, violations);
}
}
impl Exp37C {
fn check_node(&self, node: &Node, source: &str, violations: &mut Vec<RuleViolation>) {
for n in query::find_descendants(*node, |_| true) {
// Check for log2() with complex numbers (not creal)
if n.kind() == "call_expression" {
let text = n.utf8_text(source.as_bytes()).unwrap_or("");
// log2() doesn't support complex numbers directly
if text.starts_with("log2(") && !text.contains("creal(") {
// Check if context has complex number declarations
let lines_before = source
.lines()
.take(n.start_position().row + 1)
.collect::<Vec<_>>()
.join("\n");
if lines_before.contains("complex") && lines_before.contains("=") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: n.start_position().row + 1,
column: n.start_position().column + 1,
file_path: String::new(),
message: "log2() does not support complex numbers; use log(x)/log(2) or log2(creal(x))".to_string(),
suggestion: Some("Replace log2(complex) with log(complex)/log(2) or log2(creal(complex))".to_string()),
requires_manual_review: None,
});
}
}
// Check for open() without mode parameter when O_CREAT is used
if text.starts_with("open(") && text.contains("O_CREAT") {
// Count commas to check argument count
let comma_count = text.matches(',').count();
if comma_count < 2 {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: n.start_position().row + 1,
column: n.start_position().column + 1,
file_path: String::new(),
message: "open() with O_CREAT requires mode parameter".to_string(),
suggestion: Some("Add mode_t parameter to open() call".to_string()),
requires_manual_review: None,
});
}
}
}
// Check for old-style function declarations without parameter types
if n.kind() == "declaration" {
let text = n.utf8_text(source.as_bytes()).unwrap_or("");
// Skip variable declarations with initialization (e.g. uint32_t x = MACRO();)
let has_init = (0..n.child_count())
.any(|i| n.child(i).is_some_and(|c| c.kind() == "init_declarator"));
// Pattern: function_name(); with empty parens (K&R style)
if !has_init && text.contains("()") && !text.contains("void") {
// Exclude function pointers and actual function definitions
if !text.contains("(*") && !text.contains("{") {
// Check if it looks like a function declaration
if text.trim().ends_with(");") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: n.start_position().row + 1,
column: n.start_position().column + 1,
file_path: String::new(),
message: "Old-style function declaration without parameter types"
.to_string(),
suggestion: Some(
"Specify parameter types in function declaration".to_string(),
),
requires_manual_review: None,
});
}
}
}
// Check for variadic function declarations without proper header
if text.contains("...") && !text.contains("#include") {
// Check if it's a declaration (not definition)
if text.trim().ends_with(";") && !text.contains("{") {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: n.start_position().row + 1,
column: n.start_position().column + 1,
file_path: String::new(),
message: "Variadic function declaration should use proper header"
.to_string(),
suggestion: Some(
"Include proper header instead of declaring variadic function"
.to_string(),
),
requires_manual_review: None,
});
}
}
// Check for function pointer with wrong signature
if text.contains("(*fp)()") || (text.contains("(*fp)") && text.contains("()")) {
violations.push(RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
line: n.start_position().row + 1,
column: n.start_position().column + 1,
file_path: String::new(),
message: "Function pointer declared without parameter types".to_string(),
suggestion: Some(
"Declare function pointer with explicit parameter types".to_string(),
),
requires_manual_review: None,
});
}
}
}
}
}