أمان تطبيقات Rust: أهم 10 ثغرات للمبتدئين
\n\nتُعتبر Rust واحدة من أكثر لغات البرمجة أمانًا بفضل نظام الملكية (ownership) والكتابة الصارمة. ومع ذلك، حتى في Rust يمكن ارتكاب أخطاء، خاصة عند استخدام كتل unsafe أو التعامل غير الصحيح مع المكتبات الخارجية. في هذه المقالة، سنستعرض أهم 10 ثغرات يواجهها المطورون المبتدئون في Rust، ونوضح كيفية تجنبها.
1. الاستخدام غير الآمن لكود unsafe
\n\nالميزة الرئيسية لـ Rust هي أمان الذاكرة بدون جامع القمامة. ومع ذلك، فإن الكلمة المفتاحية unsafe تعطل جزءًا من فحوصات المترجم. غالبًا ما يسيء المبتدئون استخدامها دون فهم العواقب.
// خطير: إلغاء مرجع المؤشر الخام بدون فحص\nfn dangerous() {\n let ptr = 0x12345 as *const i32;\n unsafe {\n println!("القيمة: {}", *ptr); // قد يسبب خطأ segfault\n }\n}\n\n// آمن: استخدام طرق موثوقة\nfn safe() {\n let x = 42;\n let ptr = &x as *const i32;\n unsafe {\n if !ptr.is_null() {\n println!("القيمة: {}", *ptr);\n }\n }\n}\n\nتوصية: استخدم unsafe فقط عندما يكون ذلك ضروريًا حقًا، وتحقق دائمًا من أن المؤشرات ليست فارغة (null).
2. تسرب الذاكرة عبر Box::leak وحلقات Rc
\n\nعلى الرغم من أن Rust يمنع معظم تسربات الذاكرة، إلا أن المراجع الدائرية مع Rc أو RefCell يمكن أن تؤدي إلى تسرب. كما أن Box::leak خطير لأنه "ينسى" الذاكرة عمدًا.
use std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug)]\nstruct Node {\n value: i32,\n next: Option<Rc<RefCell<Node>>>,\n}\n\nfn create_cycle() {\n let a = Rc::new(RefCell::new(Node { value: 1, next: None }));\n let b = Rc::new(RefCell::new(Node { value: 2, next: None }));\n \n // إنشاء حلقة: a -> b -> a\n a.borrow_mut().next = Some(b.clone());\n b.borrow_mut().next = Some(a.clone());\n // لن يتم تحرير الذاكرة أبدًا!\n}\n\nالحل: استخدم Weak<T> لكسر المراجع الدائرية أو أعد النظر في البنية.
3. سباقات البيانات (Data Races) في الكود متعدد الخيوط
\n\nيضمن Rust عدم وجود سباقات بيانات في الكود الآمن، ولكن مع الاستخدام غير الصحيح لـ unsafe أو traits Send/Sync يمكن أن تظهر مشاكل.
use std::thread;\nuse std::sync::Arc;\nuse std::sync::Mutex;\n\n// خطير: مشاركة حالة قابلة للتغيير بدون مزامنة\nfn data_race_example() {\n let mut counter = 0;\n let mut handles = vec![];\n \n for _ in 0..10 {\n handles.push(thread::spawn(|| {\n counter += 1; // خطأ في الترجمة: cannot borrow\n }));\n }\n}\n\n// آمن: استخدام Mutex\nfn safe_mutex() {\n let counter = Arc::new(Mutex::new(0));\n let mut handles = vec![];\n \n for _ in 0..10 {\n let counter = Arc::clone(&counter);\n handles.push(thread::spawn(move || {\n let mut num = counter.lock().unwrap();\n *num += 1;\n }));\n }\n}\n\n4. تجاوز سعة المخزن المؤقت (Buffer Overflow)
\n\nفي Rust، من غير المحتمل حدوث تجاوز سعة المخزن المؤقت في الكود الآمن، ولكنه ممكن عند استخدام unsafe أو العمل مع FFI (واجهة الوظائف الخارجية).
// خطير: نسخ غير آمن إلى المخزن المؤقت\nfn unsafe_copy(src: &[u8], dst: &mut [u8]) {\n unsafe {\n std::ptr::copy_nonoverlapping(\n src.as_ptr(),\n dst.as_mut_ptr(),\n src.len() // إذا كان src.len() > dst.len() - تجاوز!\n );\n }\n}\n\n// آمن: استخدام slice::copy_from_slice\nfn safe_copy(src: &[u8], dst: &mut [u8]) {\n if src.len() <= dst.len