בטיחות זיכרון

בטיחות זיכרון היא מצב של הגנה מפני באגי תוכנה שונים ופרצות אבטחה בעת התמודדות עם גישה לזיכרון, כגון גלישת חוצץ ומצביעים משתלשלים . לדוגמה, ג'אווה נחשבת בטוחה לזיכרון מכיוון שזיהוי שגיאות זמן הריצה שלה בודק את גבולות המערך והפניות מצביעים. לעומת זאת, C ו- C++ מאפשרות להשתמש במצביעים עם כתובות שרירותיות בזיכרון ללא בדיקה ולכן שימוש בהן עלול לייצר תוכניות שהן לא בטוחות מבחינת זיכרון.

 השפעה 
בשנת 2019, מהנדס אבטחה של מיקרוסופט דיווח כי 70% מכל פרצות האבטחה נגרמות כתוצאה מבעיות בטיחות בזיכרון. בשנת 2020, צוות בגוגל דיווח באופן דומה כי 70% מכל "באגי האבטחה החמורים" ב־Chromium נגרמו מבעיות בטיחות בזיכרון. נקודות תורפה וניצולים רבים אחרים בפרופיל גבוה בתוכנות קריטיות נבעו בסופו של דבר מחוסר בטיחות זיכרון, כולל Heartbleed ובאג ב־sudo שבו ניתנו יותר מדי הרשאות.

 גישות 
בכמה שפות תכנות עליות ישנה בטיחות בזיכרון מובנית בשפה כברירת מחדל, אם כי לא לגמרי מכיוון שהן בודקות רק את הקוד שלהן ולא את המערכת איתה הם מקיימים אינטראקציה. ניהול זיכרון אוטומטי בצורה של איסוף אשפה הוא הטכניקה הנפוצה ביותר למניעת חלק מבעיות בטיחות הזיכרון, מכיוון שהיא מונעת שגיאות בטיחות נפוצות בזיכרון (כמו שימוש במצביע אחרי שהוא שוחרר) עבור כל הנתונים שהוקצו בזמן הריצה של השפה. בשילוב עם בדיקת גבולות אוטומטית בכל גישה למערך וללא תמיכה במשחק מתימטי במצביעים בזמן גישה לזיכרון, שפות שיש בהן איסוף זבל מספקות ערבויות חזקות לבטיחות זיכרון (אם כי הערובות עשויות להיות חלשות יותר עבור פעולות ברמה נמוכה המסומנות במפורש כלא בטוחות, כגון שימוש בממשק פונקציה זרה). עם זאת, עלות הביצועים של איסוף האשפה הופכת את השפות הללו ללא מתאימות ליישומים קריטיים לביצועים מסוימים.

עבור שפות המשתמשות בניהול זיכרון ידני , בטיחות הזיכרון אינה מובטחת בדרך כלל בזמן הריצה. במקום זאת, בדיקות של בטיחות הזיכרון חייבים להיות מובטחים על ידי המהדר באמצעות ניתוח קוד סטטי והוכחת משפט אוטומטי או מנוהלים בקפידה על ידי המתכנת בזמן הריצה. לדוגמה, שפת התכנות Rust מיישמת מנגנון בדיקת שייכות כדי להבטיח בטיחות זיכרון, בעוד ש־C ו- C++ לא מספקות ערובות בטיחות זיכרון. הכמות המשמעותית של תוכנות שנכתבו ב-C ו-C++ הניעה את הפיתוח של כלי ניתוח סטטי חיצוני כמו Coverity, המציע ניתוח זיכרון סטטי עבור C.

 סיווג שגיאות בטיחות בזיכרון 
סוגים רבים ושונים של שגיאות זיכרון עלולים להתרחש:
 מרחבי
 גלישת חוצץ – כתיבה מחוץ לתחום יכולה להשחית את התוכן של אובייקטים סמוכים, או נתונים פנימיים (כמו המידע על גבולות הערימה) או הכתובת שאליה תקפוץ התוכנית בסיום פונקציה.
 קריאת יתר של מאגר – קריאה מחוץ לתחום יכולה לחשוף נתונים רגישים או לעזור לתוקפים לעקוף את הפריסה האקראית של מרחב הכתובות.
 זמני
 שימוש במצביע לאחר ששוחרר – שימוש במצביע משתלשל  המאחסן את הכתובת של אובייקט שנמחק.
 שחרור כפול של מצביע – קריאות חוזרות ונשנות לשחרר מצביע עשויות לשחרר אובייקט חדש בטרם עת באותה כתובת. גם אם לא נעשה שימוש חוזר באותה הכתובת המדויקת ששוחררה, עלולה להיות השחתת זיכרון אחרת, במיוחד במקצי זיכרון המשתמשים ברשימות זיכרון פנוי .
 משתנים לא מאותחלים – נעשה שימוש במשתנה שלא הוקצה לו ערך. הוא עשוי להכיל מידע רגיש או ביטים שאינם תקפים עבור הסוג.
 מצביעי פרא קורה כאשר נעשה שימוש במצביע לפני אתחול למצב ידוע כלשהו. הם מראים את אותה התנהגות לא סדירה כמו מצביעים משתלשלים, אם כי יש סיכוי נמוך יותר שהם יישארו ללא זיהוי.
 שחרור לא חוקי של מצביע – ניסיון לשחרר כתובת לא חוקית עלולה להשחית את הערימה.
 שחרור זיכרון לא תואם – כאשר משתמשים במספר מקצי זיכרון, ומשחררים את הזיכרון בעזרת פונקציות של המקצה הלא נכון. (כך למשל אם הזיכרון הוקצה על ידי מקצה זיכרון A, ושוחרר בעזרת פונקציית שחרור זיכרון של מקצה זיכרון B) 

 תרומה לבאגים 
בהתאם לשפה ולסביבה, סוגים אחרים של באגים יכולים לתרום לאי בטיחות הזיכרון:
 גלישת מחסנית – מתרחש כאשר התוכנית מנסה להשתמש בכל שטח המחסנית שלה ומעבר לו, בדרך כלל בגלל רקורסיה עמוקה מדי. בדרך כלל עוצר התוכנית תיעצר בשגיאה בעקבות כך, והשחתת זיכרון תמנע, אך פונקציות עם מחסנית גדולה עשויות לעקוף את ההגנות, וייתכן שמערכת ההפעלה לא מכילה הגנות מפני בעיה זו.
 עייפות ערימה – במקרה שבו התוכנית מנסה להקצות יותר זיכרון ממה שיש. בשפות מסוימות, יש לבדוק את המצב הזה באופן ידני לאחר כל הקצאה.
 דליפת זיכרון – אי החזרת זיכרון למקצה עשויה להוות את הבמה למיצוי הערימה (לעיל). בחלק מהשפות ישנה לוגיקה הרצה בזמן הרס של אובייקט (RAII). אי הפעלה של לוגיקה זו עלול להוביל לתוצאות בלתי צפויות, אך אינו נחשב בעצמו לשגיאת בטיחות בזיכרון.
 הפניית מצביע Null – הפניית מצביע null תגרום לרוב לחריגה או להפסקת תוכנית ברוב הסביבות, אך עלולה לגרום לשחיתות בליבת מערכת ההפעלה או מערכות ללא הגנת זיכרון או כאשר השימוש במצביע האפס כרוך בהיסט גדול או שלילי. ב-C++, מכיוון ששימוש במצביע null הוא התנהגות לא מוגדרת, אופטימיזציות מהדר עלולות לגרום להסרת בדיקות אחרות, מה שיוביל לפרצות במקומות אחרים בקוד.

רשימות מסוימות עשויות לכלול גם מרוץ תהליכים (קריאה/כתיבה במקביל לזיכרון משותף) כחלק מבטיחות הזיכרון. שפת התכנות Rust מונעת סוגים רבים של מרוצי תהליכים מבוססי זיכרון כברירת מחדל, מכיוון שהיא מבטיחה שיש לכל היותר כותב אחד או קורא אחד או יותר. שפות תכנות רבות אחרות, כגון Java, אינן מונעות אוטומטית מרוץ תהליכים מבוססי זיכרון, אך עדיין נחשבות בדרך כלל לשפות "בטוחות לזיכרון". לכן, טיפול במרוצי תהליכים בדרך כלל אינו נחשבת הכרחי כדי ששפה תיחשב בטוחה בזיכרון.

 הערות שוליים 

קטגוריה:פרצות אבטחה
קטגוריה:באגים