padlock_source/
concurrency.rs1use padlock_core::ir::{AccessPattern, StructLayout};
7
8use crate::SourceLanguage;
9
10pub fn annotate_concurrency(layout: &mut StructLayout, language: &SourceLanguage) {
16 for field in &mut layout.fields {
17 let ty_name = match &field.ty {
18 padlock_core::ir::TypeInfo::Primitive { name, .. }
19 | padlock_core::ir::TypeInfo::Opaque { name, .. } => name.clone(),
20 _ => continue,
21 };
22
23 if is_concurrent_type(&ty_name, language) {
24 let is_atomic = is_atomic_type(&ty_name, language);
25 if matches!(field.access, AccessPattern::Unknown) {
26 field.access = AccessPattern::Concurrent {
30 guard: Some(field.name.clone()),
31 is_atomic,
32 is_annotated: false,
33 };
34 }
35 } else if is_read_mostly_type(&ty_name, language)
36 && matches!(field.access, AccessPattern::Unknown)
37 {
38 field.access = AccessPattern::ReadMostly;
39 }
40 }
41}
42
43pub fn annotate_custom_types(layout: &mut StructLayout, custom_types: &[String]) {
49 if custom_types.is_empty() {
50 return;
51 }
52 for field in &mut layout.fields {
53 if !matches!(field.access, AccessPattern::Unknown) {
54 continue; }
56 let ty_name = match &field.ty {
57 padlock_core::ir::TypeInfo::Primitive { name, .. }
58 | padlock_core::ir::TypeInfo::Opaque { name, .. } => name.clone(),
59 _ => continue,
60 };
61 if custom_types.iter().any(|ct| ty_name.contains(ct.as_str())) {
62 field.access = AccessPattern::Concurrent {
63 guard: Some(field.name.clone()),
64 is_atomic: false,
65 is_annotated: false,
66 };
67 }
68 }
69}
70
71pub fn has_concurrent_fields(layout: &StructLayout) -> bool {
73 layout
74 .fields
75 .iter()
76 .any(|f| matches!(f.access, AccessPattern::Concurrent { .. }))
77}
78
79fn is_concurrent_type(name: &str, lang: &SourceLanguage) -> bool {
80 match lang {
81 SourceLanguage::Rust => {
82 name.starts_with("Mutex")
83 || name.starts_with("RwLock")
84 || name.starts_with("Arc")
85 || name.contains("Atomic")
86 || name.starts_with("Condvar")
87 || name.starts_with("Once")
88 }
89 SourceLanguage::C | SourceLanguage::Cpp => {
90 name.contains("mutex")
91 || name.contains("atomic")
92 || name.contains("spinlock")
93 || name.contains("critical_section")
94 || name.contains("pthread_mutex")
95 }
96 SourceLanguage::Go => {
97 name == "sync.Mutex"
98 || name == "sync.RWMutex"
99 || name == "Mutex"
100 || name == "RWMutex"
101 || name.contains("atomic")
102 }
103 SourceLanguage::Zig => {
104 name.contains("Mutex")
105 || name.contains("RwLock")
106 || name.contains("atomic.Value")
107 || name.contains("Atomic")
108 }
109 }
110}
111
112fn is_atomic_type(name: &str, lang: &SourceLanguage) -> bool {
113 match lang {
114 SourceLanguage::Rust => name.contains("Atomic"),
115 SourceLanguage::C | SourceLanguage::Cpp => name.contains("atomic"),
116 SourceLanguage::Go => name.contains("atomic"),
117 SourceLanguage::Zig => name.contains("atomic.Value") || name.contains("Atomic"),
118 }
119}
120
121fn is_read_mostly_type(name: &str, lang: &SourceLanguage) -> bool {
122 match lang {
123 SourceLanguage::Rust => name.starts_with("RwLock"),
124 SourceLanguage::C | SourceLanguage::Cpp => {
125 name.contains("rwlock") || name.contains("shared_mutex")
126 }
127 SourceLanguage::Go => name == "sync.RWMutex" || name == "RWMutex",
128 SourceLanguage::Zig => name.contains("RwLock"),
129 }
130}
131
132#[cfg(test)]
135mod tests {
136 use super::*;
137 use padlock_core::arch::X86_64_SYSV;
138 use padlock_core::ir::{AccessPattern, Field, StructLayout, TypeInfo};
139
140 fn field_with_type(name: &str, ty_name: &str) -> Field {
141 Field {
142 name: name.into(),
143 ty: TypeInfo::Primitive {
144 name: ty_name.into(),
145 size: 8,
146 align: 8,
147 },
148 offset: 0,
149 size: 8,
150 align: 8,
151 source_file: None,
152 source_line: None,
153 access: AccessPattern::Unknown,
154 }
155 }
156
157 fn layout_with_fields(fields: Vec<Field>) -> StructLayout {
158 StructLayout {
159 name: "T".into(),
160 total_size: 64,
161 align: 8,
162 fields,
163 source_file: None,
164 source_line: None,
165 arch: &X86_64_SYSV,
166 is_packed: false,
167 is_union: false,
168 is_repr_rust: false,
169 suppressed_findings: Vec::new(),
170 uncertain_fields: Vec::new(),
171 }
172 }
173
174 #[test]
175 fn rust_mutex_field_is_annotated() {
176 let mut layout = layout_with_fields(vec![field_with_type("counter", "Mutex")]);
177 annotate_concurrency(&mut layout, &SourceLanguage::Rust);
178 assert!(matches!(
179 layout.fields[0].access,
180 AccessPattern::Concurrent { .. }
181 ));
182 }
183
184 #[test]
185 fn rust_atomic_is_atomic() {
186 let mut layout = layout_with_fields(vec![field_with_type("count", "AtomicU64")]);
187 annotate_concurrency(&mut layout, &SourceLanguage::Rust);
188 if let AccessPattern::Concurrent { is_atomic, .. } = &layout.fields[0].access {
189 assert!(is_atomic);
190 } else {
191 panic!("expected Concurrent");
192 }
193 }
194
195 #[test]
196 fn cpp_mutex_annotated() {
197 let mut layout = layout_with_fields(vec![field_with_type("mu", "std::mutex")]);
198 annotate_concurrency(&mut layout, &SourceLanguage::Cpp);
199 assert!(has_concurrent_fields(&layout));
200 }
201
202 #[test]
203 fn unknown_field_stays_unknown() {
204 let mut layout = layout_with_fields(vec![field_with_type("x", "int")]);
205 annotate_concurrency(&mut layout, &SourceLanguage::C);
206 assert!(matches!(layout.fields[0].access, AccessPattern::Unknown));
207 }
208
209 #[test]
210 fn has_concurrent_fields_false_when_none() {
211 let layout = layout_with_fields(vec![field_with_type("x", "int")]);
212 assert!(!has_concurrent_fields(&layout));
213 }
214}