גלישת חוצץ

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

תיאור

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

הסכנות שבגלישת חוצץ

מרגע שהתרחשה גלישת חוצץ, מהלך התוכנית "איננו מוגדר", כלומר לא ניתן להבטיח דבר לגבי המשך ריצת התוכנית: היא עשויה לקרוס, לרוץ באופן תקין, להמשיך לרוץ ולתת תוצאות שגויות, או לגרום נזק.
לעיתים קרובות מאוחסן המידע הדרוש להבטחת ריצתה התקינה של תוכנית בסמוך לחוצצים בה היא משתמשת. גלישת חוצץ עשויה לגרום למחיקת מידע זה והחלפתו במידע אחר. פרט לפגיעה בריצתה התקינה של התוכנית, הדבר עלול לסייע לוירוס מחשב "להשתלט" על ריצת התוכנית ולהריץ קטעי קוד זדוניים וזאת על ידי כתיבה על אזור בזיכרון המכיל קוד תוכנה שמתבצע. גלישת חוצץ הגורמת לקריסה של התוכנית, ללא הרצת קוד זדוני, מכונה "מניעת שירות" (Denial of Service).
במקרה שהחוצץ נמצא במחסנית קריאות, הזיכרון שמוקצה לחוצץ נמצא בסמיכות לתא הזיכרון שמכיל את כתובת הזיכרון שאליה יש לחזור לאחר סיום הפונקציה, לצורך המשכה התקין של התוכנית. אם ידוע כי בתוכנית קיים כשל של גלישת חוצץ, ניתן לעיתים לגרום לכך שתוכנו של תא זיכרון זה ישתנה, והוא יצביע על כתובתו של קטע קוד זדוני. סוג זה של גלישת חוצץ נקרא גם "גלישת חוצץ במחסנית" (Stack buffer overflow).
כאשר החוצץ נמצא בערימה, הוא לרוב אינו נמצא בסמיכות לכתובות חזרה כל שהן, ולכן באופן כללי ניתן לומר כי קשה יותר לנצל גלישת חוצץ בערימה לטובת הרצת קוד זדוני מאשר במחסנית. סוג זה של גלישת חוצץ נקרא גלישת ערימה (Heap overflow).
על ידי ניצול של כל אחת משתי הפרצות, ניתן לשתול סוס טרויאני בקבצים שאינם קובצי הרצה, המכילים מידע בלבד.

מניעת גלישות חוצץ

לתוכנות שנכתבות ונבדקות ביסודיות יש סיכוי נמוך יותר להיפגע מגלישת חוצץ, פשוט על ידי ווידוא שהתוכנה בודקת את הקלט ומוודאת שאינו גדול יותר מהחוצץ שמוקצב לו.
במהדרים מתקדמים ובמערכות הפעלה מתקדמות קיימת הגנה מובנית מפני ניצול של גלישת חוצץ להרצת קוד זדוני (אם כי לרוב לא ממניעת שירות). דוגמה אחת להגנה כזו היא שתילה של מספר אקראי בין המשתנים של פונקציה מסוימת לכתובת החזרה שלה. מערכת ההפעלה יכולה לבדוק את הערך לפני תחילת ריצת הפונקציה ולפני החזרה שלה. מכיוון שערך זה משתנה בכל ריצה של התוכנית, אין דרך לחזות אותו מראש. אם כתובת החזרה של הפונקציה תידרס, הערך האקראי יידרס גם כן, ומערכת ההפעלה תוכל להתריע על גלישת חוצץ. שיטה זו נקראת קנרית המחסנית (Stack Canary).
טכניקה אחרת, שאיננה מונעת גלישת חוצץ אך מקשה על ניצולה להרצת קוד זדוני, היא רנדומיזציה של מרחב הכתובות (ASLR - Address Space Layout Randomization), וכמקרה פרטי רנדומיזציה של המחסנית. מימוש נפוץ לזה הוא הגדרה אקראית של כתובת תחילת המחסנית בזמן עליית התוכנית. אקראיות זו מקשה על התוקף להעריך את המיקום המדויק של כתובות חזרה מקריאה לפונקציה או של החוצץ המכיל את הקוד הזדוני, למשל. עם זאת, קיימות טכניקות להתגבר על המנגנון, וביניהן "הצפה" של מרחב הכתובות בקוד זדוני, כך לא יהיה צורך לנחש מיקום מדויק שלו.
טכניקה נוספת היא מניעה בחומרה של הרצה של מידע (במעבדי אינטל המנגנון נקרא DEP - Data Execution Prevention) - כך שגם היכולת לשנות כתובת חזרה מפונקציה לא תאפשר הרצה של קוד שרירותי כלשהו. טכניקה זאת לא מגנה מפני הרצה של קוד קיים בסדר שונה מהסדר אליו התכוון המתכנת. כמו כן, תוקף עשוי להתגבר על המנגנון בעזרת טכניקה שנקראת Return-oriented programming (או Return to libc).
למרות שקיימות שיטות רבות למניעה של ניצול גלישת חוצצים להרצת קוד זדוני, לא קיימת כיום הגנה מוחלטת מניצול כזה.