בטיחות זיכרון
בטיחות זיכרון היא מצב של הגנה מפני באגי תוכנה שונים ופרצות אבטחה בעת התמודדות עם גישה לזיכרון, כגון גלישת חוצץ ומצביעים משתלשלים (אנ')[1]. לדוגמה, ג'אווה נחשבת בטוחה לזיכרון מכיוון שזיהוי שגיאות זמן הריצה שלה בודק את גבולות המערך והפניות מצביעים.[1] לעומת זאת, C ו- C++ מאפשרות להשתמש במצביעים עם כתובות שרירותיות בזיכרון ללא בדיקה[2] ולכן שימוש בהן עלול לייצר תוכניות שהן לא בטוחות מבחינת זיכרון.[3]
השפעה
[עריכת קוד מקור | עריכה]בשנת 2019, מהנדס אבטחה של מיקרוסופט דיווח כי 70% מכל פרצות האבטחה נגרמות כתוצאה מבעיות בטיחות בזיכרון.[4] בשנת 2020, צוות בגוגל דיווח באופן דומה כי 70% מכל "באגי האבטחה החמורים" ב־Chromium נגרמו מבעיות בטיחות בזיכרון. נקודות תורפה וניצולים רבים אחרים בפרופיל גבוה בתוכנות קריטיות נבעו בסופו של דבר מחוסר בטיחות זיכרון, כולל Heartbleed[5] ובאג ב־sudo שבו ניתנו יותר מדי הרשאות.[6]
גישות
[עריכת קוד מקור | עריכה]בכמה שפות תכנות עליות ישנה בטיחות בזיכרון מובנית בשפה כברירת מחדל[דרוש מקור], אם כי לא לגמרי מכיוון שהן בודקות רק את הקוד שלהן ולא את המערכת איתה הם מקיימים אינטראקציה. ניהול זיכרון אוטומטי בצורה של איסוף אשפה הוא הטכניקה הנפוצה ביותר למניעת חלק מבעיות בטיחות הזיכרון, מכיוון שהיא מונעת שגיאות בטיחות נפוצות בזיכרון (כמו שימוש במצביע אחרי שהוא שוחרר) עבור כל הנתונים שהוקצו בזמן הריצה של השפה.[7] בשילוב עם בדיקת גבולות אוטומטית בכל גישה למערך וללא תמיכה במשחק מתימטי במצביעים בזמן גישה לזיכרון, שפות שיש בהן איסוף זבל מספקות ערבויות חזקות לבטיחות זיכרון (אם כי הערובות עשויות להיות חלשות יותר עבור פעולות ברמה נמוכה המסומנות במפורש כלא בטוחות, כגון שימוש בממשק פונקציה זרה). עם זאת, עלות הביצועים של איסוף האשפה הופכת את השפות הללו ללא מתאימות ליישומים קריטיים לביצועים מסוימים.[1]
עבור שפות המשתמשות בניהול זיכרון ידני (אנ'), בטיחות הזיכרון אינה מובטחת בדרך כלל בזמן הריצה. במקום זאת, בדיקות של בטיחות הזיכרון חייבים להיות מובטחים על ידי המהדר באמצעות ניתוח קוד סטטי והוכחת משפט אוטומטי או מנוהלים בקפידה על ידי המתכנת בזמן הריצה.[7] לדוגמה, שפת התכנות Rust מיישמת מנגנון בדיקת שייכות כדי להבטיח בטיחות זיכרון,[8] בעוד ש־C ו- C++ לא מספקות ערובות בטיחות זיכרון. הכמות המשמעותית של תוכנות שנכתבו ב-C ו-C++ הניעה את הפיתוח של כלי ניתוח סטטי חיצוני כמו Coverity, המציע ניתוח זיכרון סטטי עבור C.[9]
סיווג שגיאות בטיחות בזיכרון
[עריכת קוד מקור | עריכה]סוגים רבים ושונים של שגיאות זיכרון עלולים להתרחש:[10][11]
- מרחבי
- גלישת חוצץ – כתיבה מחוץ לתחום יכולה להשחית את התוכן של אובייקטים סמוכים, או נתונים פנימיים (כמו המידע על גבולות הערימה) או הכתובת שאליה תקפוץ התוכנית בסיום פונקציה.
- קריאת יתר של מאגר – קריאה מחוץ לתחום יכולה לחשוף נתונים רגישים או לעזור לתוקפים לעקוף את הפריסה האקראית של מרחב הכתובות.
- זמני
- שימוש במצביע לאחר ששוחרר – שימוש במצביע משתלשל (אנ') המאחסן את הכתובת של אובייקט שנמחק.
- שחרור כפול של מצביע – קריאות חוזרות ונשנות לשחרר מצביע עשויות לשחרר אובייקט חדש בטרם עת באותה כתובת. גם אם לא נעשה שימוש חוזר באותה הכתובת המדויקת ששוחררה, עלולה להיות השחתת זיכרון אחרת, במיוחד במקצי זיכרון המשתמשים ברשימות זיכרון פנוי (אנ').
- משתנים לא מאותחלים – נעשה שימוש במשתנה שלא הוקצה לו ערך. הוא עשוי להכיל מידע רגיש או ביטים שאינם תקפים עבור הסוג.
- מצביעי פרא קורה כאשר נעשה שימוש במצביע לפני אתחול למצב ידוע כלשהו. הם מראים את אותה התנהגות לא סדירה כמו מצביעים משתלשלים, אם כי יש סיכוי נמוך יותר שהם יישארו ללא זיהוי.
- שחרור לא חוקי של מצביע – ניסיון לשחרר כתובת לא חוקית עלולה להשחית את הערימה.
- שחרור זיכרון לא תואם – כאשר משתמשים במספר מקצי זיכרון, ומשחררים את הזיכרון בעזרת פונקציות של המקצה הלא נכון. (כך למשל אם הזיכרון הוקצה על ידי מקצה זיכרון A, ושוחרר בעזרת פונקציית שחרור זיכרון של מקצה זיכרון B) [12]
תרומה לבאגים
[עריכת קוד מקור | עריכה]בהתאם לשפה ולסביבה, סוגים אחרים של באגים יכולים לתרום לאי בטיחות הזיכרון:
- גלישת מחסנית – מתרחש כאשר התוכנית מנסה להשתמש בכל שטח המחסנית שלה ומעבר לו, בדרך כלל בגלל רקורסיה עמוקה מדי. בדרך כלל עוצר התוכנית תיעצר בשגיאה בעקבות כך, והשחתת זיכרון תמנע, אך פונקציות עם מחסנית גדולה עשויות לעקוף את ההגנות, וייתכן שמערכת ההפעלה לא מכילה הגנות מפני בעיה זו.
- עייפות ערימה – במקרה שבו התוכנית מנסה להקצות יותר זיכרון ממה שיש. בשפות מסוימות, יש לבדוק את המצב הזה באופן ידני לאחר כל הקצאה.
- דליפת זיכרון – אי החזרת זיכרון למקצה עשויה להוות את הבמה למיצוי הערימה (לעיל). בחלק מהשפות ישנה לוגיקה הרצה בזמן הרס של אובייקט (RAII). אי הפעלה של לוגיקה זו עלול להוביל לתוצאות בלתי צפויות,[13][14] אך אינו נחשב בעצמו לשגיאת בטיחות בזיכרון.
- הפניית מצביע Null – הפניית מצביע null תגרום לרוב לחריגה או להפסקת תוכנית ברוב הסביבות, אך עלולה לגרום לשחיתות בליבת מערכת ההפעלה או מערכות ללא הגנת זיכרון או כאשר השימוש במצביע האפס כרוך בהיסט גדול או שלילי. ב-C++, מכיוון ששימוש במצביע null הוא התנהגות לא מוגדרת, אופטימיזציות מהדר עלולות לגרום להסרת בדיקות אחרות, מה שיוביל לפרצות במקומות אחרים בקוד.[15][16]
רשימות מסוימות עשויות לכלול גם מרוץ תהליכים (קריאה/כתיבה במקביל לזיכרון משותף) כחלק מבטיחות הזיכרון. שפת התכנות Rust מונעת סוגים רבים של מרוצי תהליכים מבוססי זיכרון כברירת מחדל, מכיוון שהיא מבטיחה שיש לכל היותר כותב אחד או קורא אחד או יותר. שפות תכנות רבות אחרות, כגון Java, אינן מונעות אוטומטית מרוץ תהליכים מבוססי זיכרון, אך עדיין נחשבות בדרך כלל לשפות "בטוחות לזיכרון". לכן, טיפול במרוצי תהליכים בדרך כלל אינו נחשבת הכרחי כדי ששפה תיחשב בטוחה בזיכרון.
הערות שוליים
[עריכת קוד מקור | עריכה]- ^ 1 2 3 Dhurjati, Dinakar; Kowshik, Sumant; Adve, Vikram; Lattner, Chris (1 בינואר 2003). "Memory safety without runtime checks or garbage collection" (PDF). Proceedings of the 2003 ACM SIGPLAN conference on Language, compiler, and tool for embedded systems (באנגלית). ACM. pp. 69–80. doi:10.1145/780732.780743. ISBN 1581136471. נבדק ב-13 במרץ 2017.
{{cite book}}
: (עזרה) - ^ Koenig, Andrew. "How C Makes It Hard To Check Array Bounds". Dr. Dobb's. נבדק ב-13 במרץ 2017.
{{cite web}}
: (עזרה) - ^ Akritidis, Periklis (ביוני 2011). "Practical memory safety for C" (PDF). Technical Report - University of Cambridge. Computer Laboratory. University of Cambridge, Computer Laboratory. ISSN 1476-2986. UCAM-CL-TR-798. נבדק ב-13 במרץ 2017.
{{cite journal}}
: (עזרה) - ^ "Microsoft: 70 percent of all security bugs are memory safety issues". ZDNET (באנגלית). נבדק ב-21 בספטמבר 2022.
{{cite web}}
: (עזרה) - ^ "CVE-2014-0160". Common Vulnerabilities and Exposures (באנגלית). Mitre. ארכיון מ-24 בינואר 2018. נבדק ב-8 בפברואר 2018.
{{cite web}}
: (עזרה) - ^ Goodin, Dan (4 בפברואר 2020). "Serious flaw that lurked in sudo for 9 years hands over root privileges". Ars Technica (באנגלית אמריקאית).
{{cite web}}
: (עזרה) - ^ 1 2 Crichton, Will. "CS 242: Memory safety". stanford-cs242.github.io. נבדק ב-22 בספטמבר 2022.
{{cite web}}
: (עזרה) - ^ "References". The Rustonomicon (באנגלית). Rust.org. נבדק ב-13 במרץ 2017.
{{cite web}}
: (עזרה) - ^ Bessey, Al; Engler, Dawson; Block, Ken; Chelf, Ben; Chou, Andy; Fulton, Bryan; Hallem, Seth; Henri-Gros, Charles; Kamsky, Asya; McPeak, Scott (1 בפברואר 2010). "A few billion lines of code later". Communications of the ACM. 53 (2): 66–75. doi:10.1145/1646353.1646374.
{{cite journal}}
: (עזרה) - ^ Gv, Naveen. "How to Avoid, Find (and Fix) Memory Errors in your C/C++ Code". Cprogramming.com. נבדק ב-13 במרץ 2017.
{{cite web}}
: (עזרה) - ^ "CWE-633: Weaknesses that Affect Memory". Community Weakness Enumeration (באנגלית). MITRE. נבדק ב-13 במרץ 2017.
{{cite web}}
: (עזרה) - ^ "CWE-762: Mismatched Memory Management Routines". Community Weakness Enumeration (באנגלית). MITRE. נבדק ב-13 במרץ 2017.
{{cite web}}
: (עזרה) - ^ "Destructors - the Rust Reference".
- ^ "Leaking - the Rustonomicon".
- ^ "Security flaws caused by compiler optimizations". www.redhat.com (באנגלית). נבדק ב-2024-06-26.
- ^ "NVD - CVE-2009-1897". nvd.nist.gov. נבדק ב-2024-06-26.