فا

‫ برنامه نويسي امن با زبان C –قوانين مديريت حافظه

IRCAR201011077
 
 
عنصر اصلي در برنامه نويسي امن با زبان هاي مختلف برنامه نويسي، مستند سازي خوب و استفاده از استانداردهاي قابل اجرا است. استانداردهاي كدنويسي، برنامه نويسان را ترغيب به پيروي از مجموعه اي متحدالشكل از قوانين و راهنماييها مي كند كه بر اساس نيازمندي هاي پروژه و سازمان تعيين شده است، نه بر اساس سلايق و مهارت هاي مختلف برنامه نويسان. به محض تعيين استانداردهاي مذكور، مي توان از آن به عنوان معياري براي ارزيابي كدهاي منبع، چه به صورت دستي و چه به صورت اتوماتيك استفاده كرد.
از استانداردهاي معروف در اين زمينه مي توان به استانداردCERT براي كدنويسي امن اشاره كرد كه يك سري از قوانين و پيشنهادات را براي كد نويسي امن با زبان هاي برنامه نويسي C، C++ و جاوا ارائه مي دهد. هدف از اين قوانين و پيشنهادات، حذف عادت هاي كدنويسي ناامن و رفتارهاي تعريف نشده است كه منجر به آسيب پذيري هاي قابل سوءاستفاده مي شود. به كارگيري استانداردهاي مذكور منجر به توليد سيستم هاي با كيفيت بالاتر مي شود كه در برابر حملات بالقوه، پايدارتر و مقاوم تر هستند.
در مقاله "آشنايي با استاندارد CERT براي برنامه نويسي امن"، كليات استاندارد CERT در زمينه مزبور را توضيح داديم و در سري مقاله هاي برنامه نويسي امن به زبان C به صورت تخصصي تر شيوه برنامه نويسي امن با اين زبان را مورد بررسي قرار مي دهيم. قابل ذكر است كه در اين استاندارد 89 قانون و 134 پيشنهاد براي برنامه نويسي امن با زبان C ارائه شده است كه در اين سري مقالات، مهمترين آنها را كه در سطح يك قرار دارند، شرح خواهيم داد. براي كسب اطلاعات بيشتر در مورد سطح بندي قوانين و پيشنهادات به مقاله "آشنايي با استاندارد CERT براي برنامه نويسي امن" مراجعه فرماييد. در مقاله قبلي در مورد پيشنهادات سطح اول ارائه شده در مورد مديريت حافظه صحبت كرديم و در مقاله حاضر به قوانين ارائه شده سطح اول در مورد مديريت حافظه خواهيم پرداخت.
 
 
مديريت حافظه
 
مديريت حافظه پويا يكي از منابع متداول ايجاد حفره هاي برنامه نويسي است كه منجر به آسيب پذيري هاي امنيتي مي شود. تصميم گيري در مورد اينكه حافظه پويا چگونه اختصاص داده شود، استفاده و آزاد شود، باري بر روي دوش برنامه نويسان است. مديريت حافظه ضعيف مي‌تواند منجر به مشكلات امنيتي همچون سرريز بافر heap، اشاره‌گرهاي بدون مرجع و خطاهاي دو بار آزاد كردن حافظه شود. از ديدگاه برنامه نويسان، مديريت حافظه، اختصاص حافظه، خواندن از و نوشتن در حافظه و آزاد كردن حافظه است.
در زير مهمترين قوانين برنامه نويسي امن براي پيشگيري از بروز آسيب پذيري‌هاي مربوط به مديريت حافظه، بر اساس استاندارد CERT آورده شده است.
 
قوانين مديريت حافظه
 
26. MEM30-C – به حافظه هاي آزاد شده مراجعه نكنيد.
با توجه به ISO/IEC 9899-1999 رفتار برنامه اي كه از يك اشاره گر استفاده مي كند كه حافظه ارجاعي به آن توسط توابع free() يا realloc() آزاد سازي شده، تعريف نشده است. خواندن از اشاره‌گري كه حافظه مربوط به آن آزاده شده، تعريف نشده است، زيرا مقدار اشاره گر غير قابل تعيين بوده و مي تواند يك مقدار اشتباه و غلط انداز را نمايش دهد. همچنين اين كار در برخي از موارد منجر به مشكلات سخت افزاري مي شود.
در واقع استفاده از حافظه بعد از آزاد سازي آن، منجر به تخريب ساختمان داده‌هايي مي‌شود كه براي مديريت Heap مورد استفاده قرار مي‌گيرند. ارجاع به حافظه‌هاي آزادشده به عنوان مشكل "اشاره‌گرهاي معلق" شناخته مي‌شود و دسترسي به اين نوع اشاره‌گرها منجر به آسيب‌پذيري‌ هاي قابل سوءاستفاده مي‌شود.
زماني كه حافظه‌اي آزاد مي‌شود، ممكن است محتواي آن دست نخورده بماند و قابل دسترسي باشد زيرا حافظه مذكور به صلاحديد مدير حافظه پاك شده و يا مقداردهي مجدد مي شود. داده موجود در حافظه پاك شده ممكن است به نظر صحيح برسد، با اين وحود هر لحظه امكان تغيير غيرمنتظره آن وجود دارد كه منجر به رفتار نامشخص برنامه خواهد شد. به همين دليل بسيار مهم است كه تضمين شود، در حافظه آزاد شده چيزي نوشته و يا از آن چيزي خوانده نمي شود.
در زير يك نمونه برنامه كه اين قانون را نقض مي كند مشاهده مي كنيد:
 
 
اين قطعه برنامه به صورت زير اصلاح مي شود:
  
27. MEM31-C – تنها يك بار حافظه هاي پويا را آزاد كنيد.
 
چندين بار آزاد كردن حافظه داراي پيامدهاي مشابه با دسترسي به حافظه اي است كه قبلاً آزاد شده است. اولاً خواند اشاره گر به حافظه اي كه آزاد شده، تعريف نشده است زيرا مقدار اشاره گر غير قابل تعيين است و مي تواند گمراه كننده باشد. ثانياً انجام چنين كاري ممكن است منجر به گير انداختن سخت افزار شود. زماني كه خواندن يك اشاره گر آزاد شده منجر به از كار افتادن برنامه نشود، ممكن است ساختارهاي داده زيربنايي كه مديريت heap را به عهده دارند، به صورتي تخريب شوند كه منجر به بروز آسيب‌پذيري هاي امنيتي در برنامه شوند. به اين نوع مشكلات آسيب‌پذيري‌هاي آزادسازي مجدد (double-free) گفته مي‌شود. در عمل، اين نوع آسيب‌پذيري‌ها در صورتي كه مورد سوءاستفاده قرار گيرند منجر به اجراي كد دلخواه توسط هكر مي شوند.
براي حذف آسيب‌پذيري هاي آزادسازي مجدد، بايد تضمين شود كه حافظه هاي پويا تنها يك بار آزاد مي شوند. برنامه نويسان بايد در آزاد كردن حافظه ها در دستورات شرطي و يا حلقه ها بسيار محتاط عمل كنند، زيرا در صورتي كه حافظه ها به صورت نادرست آزاد شوند مي توانند منجر به آسيب‌پذيري آزاد سازي مجدد شوند. همچنين استفاده نادرست از realloc() مي تواند منجر به آسيب‌پذيري هاي آزادسازي مجدد شود. براي كسب اطلاعات بيشتر در مورد اين موضوع به MEM04 مراجعه شود.
در زير كدي را مشاهده مي كنيد كه در آن امكان دارد حافظه اختصاص داده شده دوبار، يك بار در شرط if و يك بار نيز در بدنه برنامه آزاد شود.

 

 
در زير كد اصلاح شده را مشاهده مي كنيد:
 
 
 
28. MEM32-C – خطاهاي تخصيص حافظه را شناسايي كرده و آنها را برطرف كنيد.
 
مقادير بازگشتي از روتين هاي تخصيص حافظه نشان دهنده شكست يا موفقيت اختصاص حافظه است. با توجه به C99، توابع calloc()، malloc() و realloc()  در صورتي كه تخصيص حافظه به درستي انجام نشود، مقدار null را بر مي گردانند. برنامه نويسان نبايد تصور كنند كه heap برنامه نامحدود است و در برخي موارد تخصيص حافظه ناموفق به علت پر شدن Heap بر اثر استفاده نامحدود پردازه ها از آن اتفاق مي افتد. شكست در شناسايي و مديريت صحيح خطاهاي تخصيص حافظه مي تواند منجر به رفتارهاي غير قابل پيش بيني و ناخواسته برنامه شود. در نتيجه، بسيار ضروري است كه وضعيت نهايي روتين مديريت حافظه مشخص شده و خطاها به درستي و مطابق با استاندارد برنامه‌نويسي امن مديريت شوند. جدول زير نشان دهنده خروجي توابع مديريت حافظه است:
 
 
29. MEM34-C – تنها حافظه هاي پويا را آزاد كنيد.
آزاد كردن حافظه اي كه به صورت پويا تخصيص داده نشده است، مي تواند منجر به خطاهاي جدي مشابه آنهايي كه در قانون 31 توضيح داده شد، شود. نتايج مخصوص اين خطا بستگي به جزئيات پياده سازي دارد، ممكن است هيچ اتفاقي نيفتد و ممكن است برنامه به طرز غير منتظره اي پايان يابد! با صرف نظر از پياده سازي، از تابع free() براي هيچ چيزي به جز اشاره‌گري كه توسط يك تابع تخصيص حافظه پويا همچون malloc() و calloc() و يا realloc() برگردانده شده است، استفاده نكنيد.
وضعيت مشابهي در مورد تابع realloc() پيش مي آيد زماني كه از آن براي متغيري كه به صورت پويا حافظه دهي نشده است، استفاده مي شود. تابع realloc() براي تغيير اندازه حافظه پويا استفاده مي شود.
در زير نمونه كدي را مشاهده مي كنيد كه از قانون مذكور سرپيچي كرده است:
 
 
در اينجا نيز كد اصلاح شده را مشاهده مي كنيد:
 
 

نظرات

بدون نظر
شما برای نظر دادن باید وارد شوید

نوشته

 
تاریخ ایجاد: 18 مرداد 1393

دسته‌ها

امتیاز

امتیاز شما
تعداد امتیازها: 0