rust_memory_safety_examples/lib.rs
1//! # Rust Memory Safety Examples
2//!
3//! Educational examples demonstrating memory-safe programming patterns in Rust
4//! for financial systems and critical infrastructure.
5//!
6//! ## Purpose
7//!
8//! This library provides clear, documented examples of how Rust's ownership system
9//! prevents common memory safety vulnerabilities that affect C/C++ systems.
10//!
11//! ## Comparative Examples
12//!
13//! Each module includes:
14//! - Vulnerable C/C++ code patterns (commented examples)
15//! - Safe Rust equivalents
16//! - Explanations of how Rust prevents the vulnerability
17//! - Real-world CVE references
18//!
19//! ## Alignment with Federal Guidance
20//!
21//! These examples align with 2024 CISA/FBI guidance recommending memory-safe
22//! languages for critical infrastructure to eliminate 70% of security vulnerabilities.
23
24pub mod buffer_overflow_prevention;
25pub mod use_after_free_prevention;
26pub mod data_race_prevention;
27
28/// Module demonstrating buffer overflow prevention
29pub mod buffer_overflow {
30 //! Buffer overflow prevention through bounds checking
31 //!
32 //! In C/C++, buffer overflows are a major security vulnerability.
33 //! Rust prevents these at compile time and runtime.
34
35 /// Safe array access - Rust prevents buffer overflows
36 pub fn safe_array_access() {
37 let data = vec![1, 2, 3, 4, 5];
38
39 // Safe access with bounds checking
40 if let Some(&value) = data.get(10) {
41 println!("Value: {}", value);
42 } else {
43 println!("Index out of bounds - safely handled!");
44 }
45
46 // Panics instead of undefined behavior (can be caught)
47 // let value = data[10]; // Would panic with clear error message
48 }
49
50 /// Safe string handling - no buffer overflows
51 pub fn safe_string_handling() {
52 let mut buffer = String::new();
53
54 // Rust automatically resizes, no fixed buffer overflow
55 for i in 0..1000 {
56 buffer.push_str(&format!("Item {}, ", i));
57 }
58
59 println!("Buffer safely holds {} bytes", buffer.len());
60 }
61
62 /// Comparing C vs Rust buffer handling
63 pub fn compare_c_vs_rust() {
64 // C code (UNSAFE):
65 // char buffer[10];
66 // strcpy(buffer, "This is way too long"); // Buffer overflow!
67
68 // Rust equivalent (SAFE):
69 let buffer = "This is way too long".to_string();
70 let truncated: String = buffer.chars().take(10).collect();
71
72 println!("C: Buffer overflow vulnerability");
73 println!("Rust: Safe truncation - {}", truncated);
74 }
75}
76
77/// Module demonstrating use-after-free prevention
78pub mod use_after_free {
79 //! Use-after-free prevention through ownership
80 //!
81 //! Use-after-free is impossible in safe Rust due to the ownership system.
82
83 /// Ownership prevents use-after-free
84 pub fn ownership_prevents_uaf() {
85 let data = vec![1, 2, 3, 4, 5];
86
87 // Transfer ownership
88 let owned_data = data;
89 // data is now invalid
90
91 // This would not compile:
92 // println!("{:?}", data); // ERROR: value used after move
93
94 println!("Rust prevents use-after-free at compile time");
95 println!("Data safely owned: {:?}", owned_data);
96 }
97
98 /// Borrowing prevents dangling references
99 pub fn borrowing_prevents_dangling() {
100 let data = vec![1, 2, 3, 4, 5];
101
102 // Borrow the data
103 let reference = &data;
104
105 // Can't drop data while reference exists
106 // drop(data); // ERROR: cannot move out of `data` because it is borrowed
107
108 println!("Reference is valid: {:?}", reference);
109 // data dropped here, after reference is done
110 }
111
112 /// Comparing C vs Rust lifetime management
113 pub fn compare_c_vs_rust() {
114 // C code (UNSAFE):
115 // int* ptr;
116 // {
117 // int x = 42;
118 // ptr = &x;
119 // }
120 // printf("%d", *ptr); // Use-after-free!
121
122 // Rust equivalent (SAFE - won't compile):
123 /*
124 let ptr: &i32;
125 {
126 let x = 42;
127 ptr = &x; // ERROR: `x` does not live long enough
128 }
129 */
130
131 println!("C: Use-after-free vulnerability");
132 println!("Rust: Compile-time prevention of dangling pointers");
133 }
134}
135
136/// Module demonstrating data race prevention
137///
138/// Data race prevention through ownership and type system
139///
140/// Rust prevents data races at compile time through the type system.
141pub mod data_race {
142 use std::sync::{Arc, Mutex};
143 use std::thread;
144
145 /// Arc and Mutex for safe concurrent access
146 pub fn safe_concurrent_access() {
147 let counter = Arc::new(Mutex::new(0));
148 let mut handles = vec![];
149
150 for _ in 0..10 {
151 let counter_clone = Arc::clone(&counter);
152 let handle = thread::spawn(move || {
153 let mut num = counter_clone.lock().unwrap();
154 *num += 1;
155 });
156 handles.push(handle);
157 }
158
159 for handle in handles {
160 handle.join().unwrap();
161 }
162
163 println!("Final count (safe): {}", *counter.lock().unwrap());
164 println!("No data races possible!");
165 }
166
167 /// Send and Sync traits prevent data races
168 pub fn type_system_prevents_races() {
169 // This would not compile (Rc is not Send):
170 // use std::rc::Rc;
171 // let data = Rc::new(vec![1, 2, 3]);
172 // thread::spawn(move || {
173 // println!("{:?}", data); // ERROR: Rc cannot be sent between threads
174 // });
175
176 println!("Rust's type system prevents data races at compile time");
177 }
178
179 /// Comparing C vs Rust concurrency
180 pub fn compare_c_vs_rust() {
181 // C code (UNSAFE):
182 // int counter = 0;
183 // // Multiple threads incrementing counter without synchronization
184 // // Result: Data race, undefined behavior
185
186 // Rust equivalent (SAFE):
187 safe_concurrent_access();
188
189 println!("\nC: Data races cause undefined behavior");
190 println!("Rust: Compile-time prevention of data races");
191 }
192}
193
194/// Module demonstrating integer overflow protection
195pub mod integer_overflow {
196 //! Integer overflow detection and prevention
197
198 /// Checked arithmetic prevents silent overflows
199 pub fn checked_arithmetic() {
200 let a: u32 = 4_000_000_000;
201 let b: u32 = 1_000_000_000;
202
203 // Silent overflow in C (undefined behavior)
204 // In Rust debug mode: panics
205 // In Rust release mode with checked_add: returns None
206
207 match a.checked_add(b) {
208 Some(result) => println!("Result: {}", result),
209 None => println!("Overflow detected and handled safely!"),
210 }
211 }
212
213 /// Saturating arithmetic for financial calculations
214 pub fn saturating_arithmetic() {
215 let balance: u32 = 1000;
216 let withdrawal: u32 = 2000;
217
218 // Saturating subtraction (clamps at 0)
219 let new_balance = balance.saturating_sub(withdrawal);
220
221 println!("Balance after withdrawal: {} (saturated)", new_balance);
222 }
223}
224
225/// Module demonstrating null pointer dereference prevention
226pub mod null_pointer {
227 //! Null pointer prevention through Option<T>
228
229 /// Option<T> eliminates null pointer dereferences
230 pub fn option_prevents_null() {
231 fn find_user(id: u32) -> Option<String> {
232 if id == 1 {
233 Some("Alice".to_string())
234 } else {
235 None
236 }
237 }
238
239 // Must explicitly handle None case
240 match find_user(1) {
241 Some(name) => println!("Found user: {}", name),
242 None => println!("User not found"),
243 }
244
245 // Can't accidentally dereference null
246 // let name = find_user(99); // Type is Option<String>
247 // println!("{}", name); // ERROR: can't print Option directly
248 }
249
250 /// Comparing C vs Rust null handling
251 pub fn compare_c_vs_rust() {
252 // C code (UNSAFE):
253 // char* ptr = find_user(99); // Returns NULL
254 // printf("%s", ptr); // Null pointer dereference!
255
256 // Rust equivalent (SAFE):
257 option_prevents_null();
258
259 println!("\nC: Null pointer dereferences cause crashes");
260 println!("Rust: Option<T> forces handling of null cases");
261 }
262}
263
264/// Module demonstrating double-free prevention
265pub mod double_free {
266 //! Double-free prevention through ownership
267 //!
268 //! Double-free errors (freeing the same memory twice) are impossible in Rust
269 //! because the ownership system ensures memory is freed exactly once.
270
271 /// Ownership ensures single free
272 pub fn ownership_prevents_double_free() {
273 let data = vec![1, 2, 3, 4, 5];
274
275 // Data is automatically freed when it goes out of scope
276 // Trying to manually free twice would not compile:
277 // drop(data);
278 // drop(data); // ERROR: use of moved value
279
280 println!("Rust prevents double-free through ownership");
281 println!("Data will be freed exactly once: {:?}", data);
282 } // data freed here automatically
283
284 /// Box demonstrates single ownership
285 pub fn box_single_ownership() {
286 let boxed_value = Box::new(42);
287
288 // Transfer ownership
289 let moved_box = boxed_value;
290
291 // This would not compile:
292 // drop(boxed_value); // ERROR: value used after move
293
294 println!("Boxed value freed exactly once: {}", moved_box);
295 } // moved_box freed here
296
297 /// Comparing C vs Rust memory management
298 pub fn compare_c_vs_rust() {
299 // C code (UNSAFE):
300 // int* ptr = malloc(sizeof(int));
301 // free(ptr);
302 // free(ptr); // Double-free!
303
304 // Rust equivalent (SAFE - won't compile):
305 ownership_prevents_double_free();
306
307 println!("\nC: Double-free vulnerabilities");
308 println!("Rust: Compile-time prevention of double-free");
309 }
310}
311
312/// Module demonstrating uninitialized memory prevention
313pub mod uninitialized_memory {
314 //! Uninitialized memory prevention through initialization requirements
315
316 /// All variables must be initialized
317 pub fn initialization_required() {
318 // This would not compile:
319 // let x: i32;
320 // println!("{}", x); // ERROR: use of possibly uninitialized variable
321
322 // Must initialize
323 let x: i32 = 42;
324 println!("Value is always initialized: {}", x);
325 }
326
327 /// Uninitialized array prevention
328 pub fn array_initialization() {
329 // C code (UNSAFE):
330 // int arr[100];
331 // printf("%d", arr[0]); // Reading uninitialized memory!
332
333 // Rust equivalent (SAFE):
334 let arr = vec![0; 100]; // Initialized to zero
335 println!("Array element (initialized): {}", arr[0]);
336
337 // Or must explicitly initialize each element
338 let arr2: Vec<i32> = (0..100).map(|i| i * 2).collect();
339 println!("Array with values: {} elements", arr2.len());
340 }
341
342 /// Struct initialization must be complete
343 pub fn struct_initialization() {
344 struct User {
345 id: u32,
346 name: String,
347 email: String,
348 }
349
350 // This would not compile:
351 // let user = User {
352 // id: 1,
353 // name: "Alice".to_string(),
354 // // ERROR: missing field `email`
355 // };
356
357 // Must initialize all fields
358 let user = User {
359 id: 1,
360 name: "Alice".to_string(),
361 email: "alice@example.com".to_string(),
362 };
363
364 println!("User struct fully initialized: {}", user.name);
365 }
366
367 /// Comparing C vs Rust initialization
368 pub fn compare_c_vs_rust() {
369 println!("C: Uninitialized memory contains garbage values");
370 println!("Rust: Compiler enforces initialization before use");
371
372 initialization_required();
373 array_initialization();
374 struct_initialization();
375 }
376}
377
378/// Module demonstrating memory leak prevention with RAII
379///
380/// Memory leak prevention through RAII (Resource Acquisition Is Initialization)
381pub mod memory_leak {
382 use std::fs::File;
383 use std::io::Write;
384
385 /// RAII ensures resources are cleaned up
386 pub fn raii_file_handling() {
387 // File automatically closed when it goes out of scope
388 {
389 let mut file = File::create("/tmp/test.txt").ok();
390 if let Some(ref mut f) = file {
391 let _ = f.write_all(b"Hello, RAII!");
392 }
393 // File automatically closed here
394 }
395
396 println!("File handle automatically closed (RAII)");
397 }
398
399 /// Drop trait for custom cleanup
400 pub fn drop_trait_cleanup() {
401 struct DatabaseConnection {
402 id: u32,
403 }
404
405 impl Drop for DatabaseConnection {
406 fn drop(&mut self) {
407 println!("Closing database connection: {}", self.id);
408 }
409 }
410
411 {
412 let _conn = DatabaseConnection { id: 1 };
413 println!("Database connection open");
414 // Automatically cleaned up at end of scope
415 }
416
417 println!("Connection automatically closed via Drop trait");
418 }
419
420 /// Comparing C vs Rust resource management
421 pub fn compare_c_vs_rust() {
422 // C code (RISK):
423 // FILE* f = fopen("test.txt", "w");
424 // // Forgot to call fclose(f) - memory/resource leak!
425
426 // Rust equivalent (SAFE):
427 raii_file_handling();
428
429 println!("\nC: Easy to forget resource cleanup (leaks)");
430 println!("Rust: RAII ensures automatic cleanup");
431 }
432}
433
434/// Module demonstrating type confusion prevention
435pub mod type_confusion {
436 //! Type confusion prevention through strong typing
437
438 /// Strong typing prevents type confusion
439 pub fn strong_typing_prevents_confusion() {
440 let integer: i32 = 42;
441 let float: f64 = 3.14;
442
443 // This would not compile:
444 // let result = integer + float; // ERROR: mismatched types
445
446 // Must explicitly convert
447 let result = integer as f64 + float;
448 println!("Explicit conversion required: {}", result);
449 }
450
451 /// Newtype pattern for type safety
452 pub fn newtype_pattern() {
453 struct UserId(u32);
454 struct ProductId(u32);
455
456 let user = UserId(123);
457 let product = ProductId(456);
458
459 // This would not compile:
460 // if user == product { } // ERROR: mismatched types
461
462 println!("NewType pattern prevents mixing different ID types");
463 println!("User ID: {}, Product ID: {}", user.0, product.0);
464 }
465
466 /// Enum prevents invalid states
467 pub fn enum_prevents_invalid_states() {
468 enum ConnectionState {
469 Disconnected,
470 Connecting,
471 Connected { session_id: String },
472 Error { message: String },
473 }
474
475 let state = ConnectionState::Connected {
476 session_id: "abc123".to_string(),
477 };
478
479 // Can't access session_id unless in Connected state
480 match state {
481 ConnectionState::Connected { session_id } => {
482 println!("Connected with session: {}", session_id);
483 }
484 _ => println!("Not connected"),
485 }
486 }
487
488 /// Comparing C vs Rust type safety
489 pub fn compare_c_vs_rust() {
490 println!("C: Type confusion through casting and unions");
491 println!("Rust: Strong type system prevents confusion at compile time");
492
493 strong_typing_prevents_confusion();
494 newtype_pattern();
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 #[test]
503 fn test_buffer_overflow_examples() {
504 buffer_overflow::safe_array_access();
505 buffer_overflow::safe_string_handling();
506 buffer_overflow::compare_c_vs_rust();
507 }
508
509 #[test]
510 fn test_use_after_free_examples() {
511 use_after_free::ownership_prevents_uaf();
512 use_after_free::borrowing_prevents_dangling();
513 use_after_free::compare_c_vs_rust();
514 }
515
516 #[test]
517 fn test_data_race_examples() {
518 data_race::safe_concurrent_access();
519 data_race::type_system_prevents_races();
520 data_race::compare_c_vs_rust();
521 }
522
523 #[test]
524 fn test_integer_overflow_examples() {
525 integer_overflow::checked_arithmetic();
526 integer_overflow::saturating_arithmetic();
527 }
528
529 #[test]
530 fn test_null_pointer_examples() {
531 null_pointer::option_prevents_null();
532 null_pointer::compare_c_vs_rust();
533 }
534
535 #[test]
536 fn test_double_free_examples() {
537 double_free::ownership_prevents_double_free();
538 double_free::box_single_ownership();
539 double_free::compare_c_vs_rust();
540 }
541
542 #[test]
543 fn test_uninitialized_memory_examples() {
544 uninitialized_memory::initialization_required();
545 uninitialized_memory::array_initialization();
546 uninitialized_memory::struct_initialization();
547 uninitialized_memory::compare_c_vs_rust();
548 }
549
550 #[test]
551 fn test_memory_leak_examples() {
552 memory_leak::raii_file_handling();
553 memory_leak::drop_trait_cleanup();
554 memory_leak::compare_c_vs_rust();
555 }
556
557 #[test]
558 fn test_type_confusion_examples() {
559 type_confusion::strong_typing_prevents_confusion();
560 type_confusion::newtype_pattern();
561 type_confusion::enum_prevents_invalid_states();
562 type_confusion::compare_c_vs_rust();
563 }
564}