کد غیرقابل دسترسی
در برنامهنویسی کامپیوتر، کد غیرقابل دسترسی بخشی از کد منبع یک برنامه است که هرگز نمیتواند اجرا شود، زیرا مسیر جریان کنترل به کد از طریق بقیه برنامه وجود ندارد. کد غیرقابل دسترس گاهی اوقات کد مرده نیز نامیده میشود، اگرچه کد مرده نیز ممکن است به کدی که اجرا میشود مراجعه کند اما بر خروجی یک برنامه تأثیری ندارد.
بهطور کلی کد غیرقابل دسترسی به دلایل مختلف نامطلوب میباشد از جمله:
- حافظه غیرضروری را اشغال میکند.
- باعث ذخیره غیرضروری دستورالعملها در حافظه CPU میشود که همچنین محل دادهها را کاهش میدهد.
- از منظر نگهداری برنامه، زمان و تلاش ممکن است صرف حفظ و مستندسازی یک قطعه کد شود که در واقع غیرقابل دسترسی است و از این رو هرگز اجرا نمیشود.
با این حال، کد غیرقابل دسترس میتواند برخی از کاربردهای مشروع داشته باشد، مانند ارائه یک کتابخانه از توابع برای تماس یا پرش به صورت دستی از طریق اشکال یاب در حالی که برنامه پس از نقطه توقف متوقف میشود. این به ویژه برای بررسی و چاپ وضعیت داخلی کاربرد دارد. حتی ممکن است معقول باشد که چنین کدی در نسخه نهایی که به مشتریان ارسال میشود ضمیمه شود، و یک برنامهنویس بتواند یک نسخه اشکال زدایی را به نسخه فعلی مشتری اضافه کند، در مواقعی که یک اشکال در نمونه در حال اجرا باشد. این متفاوت از توابع اشکال زدایی است که تنها در طول توسعه استفاده میشوند و فراموش میشوند که در نسخه نهایی حذف شوند.
علل
وجود کد غیرقابل دسترسی میتواند به علت عوامل مختلفی باشد. از جمله:
- اشتباهات برنامهنویسی در شاخههای شرطی پیچیده
- یک دستآورد از تحولات داخلی انجام شده توسط بهینهسازی کامپایلر
- تست ناقص یک برنامه جدید یا اصلاح شده که قادر به تست کدهای غیرقابل دسترس نبودهاست.
- کد منسوخ که یک برنامهنویس فراموش کردهاست آن را حذف کند.
- کد استفاده نشدهاست که یک برنامهنویس تصمیم گرفت آن را حذف نکند؛ زیرا با کد عملکردی متصل بودهاست.
- کد شرطی که مشروط به چیزی باشد که نائل نشود، زیرا دادههای ورودی فعلی هرگز باعث نمیشود که کد اجرا شود.
- کد منسوخ پیچیده که عمداً نگهداری و حفظ شده، اما غیرقابل دسترس میباشد تا بتواند در صورت نیاز دوباره احیا شود.
- ساختارهای اشکال زدایی و کد توسعه قبلی که هنوز از یک برنامه حذف نشدهاند.
در پنج مورد اخیر، کد غیرقابل دسترسی به عنوان بخشی از یک میراث در برنامه باقی ماندهاست، یعنی کدی است که زمانی مفید بوده اما دیگر مورد استفاده یا مورد نیاز نیست. با این حال، کد غیرقابل دسترس ممکن است بخشی از اجزای پیچیده (کتابخانه، ماژول یا روال) باشد، که در آن کد در ارتباط با دادههای ورودی مختلف همچنان مفید است یا در شرایطی که در محیط زمان فعلی برآورده نشدهاست، بنابراین کد مربوطه را غیرقابل دسترسی میکند، اما میتواند در سایر محیطهای زمان اجرا، که مؤلفه آن توسعه یافتهاست، رخ دهد.
یک مثال از چنین کدهای غیرقابل دسترسی ممکن است اجرای یک تابع printf در یک کتابخانه زمان اجرا کامپایلر باشد، که شامل کد پیچیده برای پردازش تمام آرگومانهای رشته ممکن است، که فقط یک زیر مجموعه کوچک از آن در واقع در برنامه استفاده میشود. بدون کامپایل مجدد کتابخانه زمان اجرا، کامپایلرها بهطور معمول نمیتوانند بخش کد استفاده نشده را در زمان کامپایل حذف کنند.
مثالها
قطعه کد زیر به زبان C را در نظر بگیرید:
int foo (int X, int Y)
{
return X + Y;
int Z = X * Y;
}
تعریف int Z = X * Y هرگز به عنوان نتیجه تابع برگردانده نمیشود؛ بنابراین، تعریف Z را میتوان از بین برد.
اشکال goto fail
یک نمونه از کد غیرقابل دسترسی اشکال SSL/TLS اپل که بهطور رسمی با عنوان CVE-2014-1266 شناخته شدهاست که به دلیل عدم دسترسی به کد، حاوی نقص امنیتی بزرگی شده بود، و بهطور غیررسمی با نام "اشکال goto fail" شناخته شدهاست. بخش کد مربوطه را در زیر مشاهده میکنید:
static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
uint8_t *signature, UInt16 signatureLen)
{
OSStatus err;
...
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
...
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
return err;
}
در اینجا دو فراخوان موفق goto fail
وجود دارد. در ساختار نحو زبان C دومین فراخوان بدون شرط است و از این رو همیشه بررسی نهایی(final
check) را از قلم میاندازد. در نتیجه بعد از عملیات موفقیتآمیز به روزرسانی SHA1، مقدار خطا(err
) مقدار موفقیتآمیز بودن عملیات را بازمیگرداند و تأیید امضا هرگز شکست را اعلام نخواهد کرد چون بررسی نهایی حذف شدهاست.
در نتیجه، کد غیرقابل دسترسی، فراخوانی به تابع نهایی است که باید قابل دسترسی میبود. چندین روش برنامهنویسی خوب وجود دارد که میتواند از رخ دادن این اشکال جلوگیری کند، نظیر بازبینی کد، استفاده مناسب از فاصله گذاری یا آکولاد گذاری و تجزیه و تحلیل همراه با آزمون کد. با استفاده از کامپایلر کلنگ و استفاده از قابلیت Weverything
که شامل تجزیه و تحلیل کد غیرقابل دسترسی است، میتوانیم از این اشکال با خبر شویم.
تجزیه وتحلیل
تشخیص کدهای غیرقابل دسترسی گونهای ازتحلیل ایستا است و شامل انجام تجزیه و تحلیل جریان کنترل برای پیدا کردن کدی است که هرگز بدون در نظر گرفتن مقادیر متغیرها و سایر شرایط در زمان اجرا، اجرا خواهد شد. در برخی از زبانها (مثلاً جاوا) برخی از کدهای غیرقابل دسترسی صریحاً مجاز نیستند. بهینهسازی که کد غیرقابل دسترس را حذف میکند به عنوان حذف کد مرده شناخته میشود.
کد ممکن است در نتیجهٔ تحولات داخلی انجام شده توسط بهینهسازی کامپایلر غیرقابل دسترس شود (به عنوان مثال از بین بردن عبارات مشترک).
در عمل پیچیدگی تجزیه و تحلیل انجام شده تأثیر قابل توجهی بر مقدار کدهای غیرقابل دسترسی که شناسایی شدهاست، دارد. به عنوان مثال یک تجزیه و تحلیل ساده کد زیر نشان میدهد که کد درون گزاره شرطی زیر غیرقابل دسترسی است.
int N = 2 + 1;
if (N == 4)
{
/* unreachable */
}
عدم دسترسی در مقابل پروفایلینگ
در بعضی موارد، رویکرد عملی ممکن است ترکیبی از معیارهای غیرقابل دستیابی ساده و استفاده از پروفایل برای رسیدگی به موارد پیچیدهتر باشد. پروفایل بهطور کلی نمیتواند چیزی دربارهٔ عدم دسترسی یک قطعه کد را اثبات کند، اما ممکن است یک اکتشافی خوب برای پیدا کردن کدهای غیرقابل دسترس باشد. هنگامی که قطعه کد مشکوک یافت میشود، روشهای دیگری مانند ابزار قدرتمند تجزیه و تحلیل کد یا حتی تجزیه و تحلیل به صورت دستی میتواند مورد استفاده قرار گیرد تا تصمیم بگیرد کد واقعاً در دسترس نیست.
جستارهای وابسته
منابع
- ↑ Debray, Saumya K.; Evans, William; Muth, Robert; De Sutter, Bjorn (1 March 2000). "Compiler techniques for code compaction". ACM Transactions on Programming Languages and Systems. 22 (2): 378–415. doi:10.1145/349214.349233.
- ↑ Adam Langley (2014). "Apple's SSL/TLS bug".
- ↑ Arie van Deursen (2014). "Learning from Apple's #gotofail Security Bug".
- ↑ "sslKeyExchange.c - Source code for support for key exchange and server key exchange".
- ↑ "Java Language Specification".
- Appel, A. W. 1998 Modern Compiler Implementation in Java. Cambridge University Press.
- Muchnick S. S. 1997 Advanced Compiler Design and Implementation. Morgan Kaufmann.