BinaryVision

Author Archive

אבטחה נמצאת בפרטים הקטנים

by on ינו.31, 2010, under כללי

הפוסט של היום לא עוסק בהקשחת שרת או בתכנות נכון והגנתי, אלא בדבר חשוב יותר משניהם, תכנון.
נתקלתי לאחרונה ב"פרצה" די מבדרת. הפרצה אומנם לא מאפשרת לי להשתלט על שרת, או אפילו לפגוע במשתמשים, אבל היא בהחלט מאפשרת גישה לדבר ששווה כסף, או לפחות יכול להיות מעצבן: כתובות אימייל של קבוצה ממוקדת נושא, במקרה הזה, אנשי טכנולוגיה.
למי שלא מכיר, האתר sourceforge הוא האתר הכי גדול (או לפחות אחד מההכי גדולים) לפרוייקטים קוד פתוח. באתר יש כ 2 מיליון משתמשים רשומים וכ 230,000 פרוייקטים. בנוסף, ישנם Mailing lists לכל הפרוייקטים, ובזה נתמקד.

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

נתבונן רגע לעומק במעמקי ה"קוד" של העמוד, נתבונן לדוגמה בלינק שנתתי קודם מארכיון המיילים של Firebird: נבחר את המייל השלישי, זה נשלח ע"י "Adriano dos Santos Fernandes <adrianosf@gm…>". אנחנו רואים שהכתובת שלו מתחילה ב gm, (הערה: כולם משתמשים כיום ב gmail ככה שלא קשה לנחש שהכתובת האמיתית שלו היא gmail.com, למרות שהיא לא הייתה חייבת להיות, וזה יעבוד כמובן גם על כתובות של שרתי מייל שאנחנו לא מכירים) אבל אם נסתכל לעומק בלינק שמקשר לפוסט (בעמודה topic), נראה שם משהו מעניין, לדוגמה, במקרה שלנו, הלינק הוא:
http://sourceforge.net/mailarchive/forum.php?thread_name=4B65B1DC.5020407%40gmail.com&forum_name=firebird-devel
הפלא ופלא, השדה thread_name מסתיים ב gmail.com, מכך נסיק ששרת המייל ששלח את המייל הוא gmail.com ולכן הכתובת היא adrianosf ~-at-~ gmail.com.
נסתכל בשורה הבאה, מייל מאת Alexander Peshkoff <peshkoff@ma…>, אם נסתכל בלינק בעמודה topic, (לינק: http://sourceforge.net/mailarchive/forum.php?thread_name=201001311914.57707.peshkoff%40mail.ru&forum_name=firebird-devel) נראה שאותו שדה מסתיים הפעם ב @mail.ru, והרי זה לא מפתיע שזה מתאים לאימייל שלו שמתחיל ב: peshkoff@ma ולכן האימייל המלא שלו הוא peskoff ~-at-~ mail.ru.

החדים מביניכם בטח שמו לב שבמקרה השני, הלינק הכיל את כל הכתובת, ובמקרה הראשון לא. זה מתחבר לממה שאמרתי קודם, השדה הזה לא עקבי מספיק, (או לפחות לא מצאתי את החוקיות), ולכן זה לא עובד ב 100% מהמקרים, אבל ברוב המקרים שבדקתי, השדה הזה אכן מסתיים בשרת האיימייל ששלח את המייל, גם אם זה לא בהכרח השרת שיקבל את המיילים, זה בהחלט יכול לעזור. לדוגמה, לפעמים ב gmail הסיומת של השדה הזה תיהיה mail.google.com, אמנם אי אפשר לשלוח מייל ל adrianosf@mail.google.com (מהדוגמה הקודמת), אבל אפשר בהחלט לתרגם את זה בצורה קלה (הרי אם זה נפוץ מספיק אז אפשר לתרגם ידנית ובקלות להחליף את כל השדות).
זה גם מאוד פשוט לגנוב את המיילים האלה, לדוגמה, השורה הפשוטה הבאה שמתמשת ב wget וב sed (אמנם לא הכי נקי שאפשר, אבל החלט עושה את העבודה):

wget -q -O - http://sourceforge.net/mailarchive/forum.php?forum_name=firebird-devel | sed -n -e 's/^.*<td width="50%"><a href="[^&]*%40\([^&]*\)[^"]*".*$/@\1/p' -e s'/^.*&lt;\([^&]*\)...&gt;.*/\1/p'

לוקחת עמוד ומחלצת ממנו את כל השרתים בשורה אחת (מתחילים ב @), ואת המייל (עד הקטע המצונזר) בשורה אחריה. להוסיף את התוצאות לdb פשוט (לדוגמה sqlite), ולכתוב סקריפט פרל קצר שיעבור בכל העמודים של כל ה ML ב sourceforge ויריץ עליהם את הסינון sed הזה, גם לא לוקח הרבה זמן. בקיצור, אנחנו יכולים, בקלות יחסית (עדיין צריך לזכור לא להציף את sourceforge ולהשתמש בפרוקסי\בוטנט) לקחת את כתובות האימייל של 2 מיליון אנשים טכנלוגיים (אולי יותר אולי פחות) ולהשתמש בהם בפרסום ממוקד באינטרנט.

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

1 Comment :, , , more...

למה מתייחסים לSQL שונה?!

by on יול.14, 2009, under כללי

הקדמה
קיימים הרבה מאוד סוגים של פרצות אבטחה, לדוגמא XSS, אני יכול להבין למה XSS קורה, הרי לפעמים יש מקרי קצה שבהם רוצים לתת למתכנת יכול לכתוב html, או לחלופין, קשה לבקר (לא באמת, אבל נניח) כל כתיבה שאנחנו מבצעים ולכן אפשר לפספס ובטעות לכתוב מחרוזת ששכחנו לעשות לה escaping.
אותו דבר אני יכול להגיד על buffer overflow (גם לא באמת, אבל נניח) וישנן עוד הרבה פרצות שעליהן אפשר להגיד אותו דבר (לפעמים המצב עגום יותר ולפעמים פחות).
אבל במקרה של sql injection אני פשוט לא מבין איך זה עדיין קיים. אבל לא על זה מדובר בכתבה, אלא על הבעיתיות והקלת הראש שאנשים מייחסים לשרת sql שלהם.

תיאור הבעיה הנפוצה
כידוע Sql injection קורה כאשר המשתמש מצליח להשפיע על המחרוזת המכילה את השאילתא שאנחנו מריצים על ה db כך שהשאילתא תבצע פעולה שלא רצינו שתקרה.
אוקיי, זו פרצה נחמדה, טריק מגניב, רעיון יפה, אבל למה זה עדיין קורה? הרי יש שתי פתרונות מאוד פשוטים למניעת הבעיה!

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

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

אז איך זה שאנשים עדיין לא עושים את זה? מעבר לבינתי.

הבעיה האמיתית ופתרון בשבילה
הפתרונות שהצגתי כמובן נפוצים ומשתמשים בהם בכל מיני מקומות, והם הדרך לטיפול בבעיה, אבל לא מעט פעמים נתקלים בכל זאת בפרצות. אבל זה בהחלט לא מספיק!
הבעיה האמיתית היא, איך בכלל הגענו למצב שבו כל מה שאנחנו צריכים זה לקרוא מידע מ"טבלאת הידיעות של האתר" ובכל זאת יש לנו הרשאות כתיבה\קריאה ל\מטבלאת המשתמשים?
כל איש אבטחה יודע שהדבר הראשון שעושים זה "הפרדת סמכויות" כי בסופו של דבר, לא משנה כמה ה firewall שלך חוסם כל תעבורה וכל התוכנות שאתה משתמש בהן לא פגיעות, אם ל guest account שלך יש הרשאות root, יהיה לך גהנום בשרת 🙂
בכל שרת sql בתחום יש שמות משתמש וססמאות, יתרה מכך, לכל שרת יש תמיכה באפשרויות הגבלת גישה מתקדמות.
אז למה אנשים לא משתמשים ב user אחד בשביל לקרוא\לכתוב מהטבלאת משתמשים, ביוזר אחר בשביל לקרוא מידע וביוזר אחרון בשביל לכתוב מידע? (כאשר מידע = הכל חוץ מטבלאות משתמשים) או אפילו אם צריך לפצל את זה עוד יותר (לדוגמא מידע אישי רגיש יהיה מפוצל מהבלוג של האתר).

ההנחה שלי היא: עצלנות.

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

אני מקווה שתפיקו לקחים ותיישמו, כי בסופו של דבר, זה השרת שלכם.

הערה חשובה:
אני יודע שלא תמיד יש לכם גישה ליצירת גישות (לדוגמא שרת חינמי) אבל כל עוד אתם משלמים על השרת, זכותכם לדרוש כמה יוזרים שתרצו! ככה שזה לא תירוץ!!!

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

23 Comments :, , , , more...

פלאפון פתוח זה טוב לפרטיות

by on מאי.29, 2009, under כללי

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

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

אין כרטיס קול בפלאפונים
בעקרון, בכל הפלאפונים "הפשוטים" אין כרטיס קול, ועד כמה שאני יודע (דרוש אימות), גם בפלאפונים המתקדמים יותר, כמו N95, Iphone ודומיהם.
למה הם עושים את זה? שמעתי כל מיני שמועות, על כך שהפרוטוקול של GSM (שכמובן סגור תחת NDA) מכיל פסקה הדורשת חיבור ישיר בין ה GSM לבין מערכות השמע (ככה"נ לבקשת האח הגדול), אם זה אכן המצב, GSM הוא התגלמות הרשע, אם לא, אז GSM עדיין דפוק מהסיבות שהסבירו מקודם. יכול להיות שמדובר בחיסכון בעלויות ובמשקל של המכשיר, ואם כן, זה תירוץ מתקבל אבל עדיין יוצר בעיה רצינית.
דבר זה גורם, שגם אם המכשיר כבוי, או גם אם המשתמש היה רוצה, הוא *לא* יכול לכבות את החיבור בין ה GSM לבין אביזרי השמע, מה שאומר ש*תמיד* אפשר לצוטט לו, וזה לא בשליטתו, למען האמת, גם אם היה לפלאפונים כרטיס קול, בגלל שהם סגורים לא היה למשתמש שליטה מלאה עליהם, ולא היה אפשר לוודא שאכן החיבור סגור.
אז בעצם, אפילו עם כל מאפייני האבטחה החלשים של ה GSM, אם היה לנו שליטה על מה קורה בפלאפון, לא היה אפשר להאזין לנו, אבל כמו שאנחנו רואים, אין זה המצב.

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

מה אפשר לעשות?
אם יש לכן קצת ידע באלקטרוניקה, אפשר לעשות "eavesdrop-proofing" לפלאפון ע"י הוספת מתג שאחראי על ניתוק המיקרופון של הפלאפון ברמת החומרה, זה כמובן יבטל את האחריות, ועוד הרבה בלאגנים, אבל זה הפתרון הכי טוב!
למען האמת, הפתרון הכי טוב הוא לקנות פלאפון פתוח, ואולי ללחוץ על ייצור של עוד כאלה. בכלל, למוקו יש עוד שימושים מעניינים שאני אכתוב עליהם בעתיד, אם מישהו מעוניין: openmoko לקריאה נוספת על האופן מוקו.

אז בקיצור, תמיד תניחו שמאזינים לכם, וגם בישיבות עסקיות עם מידע עסקי חשוב, מומלץ לכבות את הפלאפון, כי בתאכלס, כל אחד יכול להאזין לכם, בקלות…

8 Comments :, , , more...

הסטיקרים הוזמנו

by on מאי.19, 2009, under כללי

אתם תראו אותם בכנס… שווה לבוא 🙂

יום טוב.

10 Comments :, , more...

תכנות בטוח ב Windows

by on מאי.15, 2009, under כללי

מי שעוקב אחרי חדשות טכנולוגיות ברשת, בוודאי שם לב ל http://www.theregister.co.uk/2009/05/15/microsoft_banishes_memcpy/
אז כן, אחרי שמיקרוסופט הגבילה (מנעה?) את השימוש בפונקציות strcpy, strcat ודומיהן, הגיע הזמן של memcpy.

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

אז בעצם, מה שמיקרוסופט אומרת, זה שהמפתחים בפלטפורמה שלהם לא יודעים לבדוק דברים טריויאלים כמו אם יש לנו מספיק מקום להעתיק, אז היא מכריחה אותם לבדוק?
אני מאמין שבקרוב נמצא ברשת קוד כזה:

#define memcpy(a,b,c) memcpy_s(a,b,c,c)

שהוא פשוט האק לגרום ל memcpy_s כמו שאנשים רגילים… מי שלא רוצה לעבוד בטוח, לא יעבוד בטוח, מתכנת לא מנוסה, יעשה שטויות.
מה היה רע ב warning בקומפילציה?

שלא תבינו אותי לא נכון, אני חושב שנכון לכתוב בטוח, אבל אני לא בטוח שלהכין את הקרקע לקופים שלא שעושים שטויות היא הדרך הכי טובה.
מה בקשר ל pritnf? הם גם מונעים את השימוש בזה? צריך לחשוש שאיזה אידיוט יכניס לשם מחרוזת… אולי צריך לכתוב
printf_s שאחראית להקפיץ שאלה בקומפילציה עבור כל printf וככה לוודא שהמתכנת וידא שהוא לא עשה טעות.

יש לי אלטרנטיבה אחרת, תשכרו מתכנתים שיודעים לתכנת… כי בסופו של יום גם memcpy_s לא בטוחה לגמרי…

יום טוב.

5 Comments :, , , more...

השקת "סטיקר" של Binaryvision

by on אפר.22, 2009, under כללי

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

  • הסטיקר חייב להכיל את הכתובת של האתר (www.binaryvision.org.il)
  • הסטיקר יכול להכיל את הסלוגן "Because even wood can be hacked", או סלוגן אחר מגניב ולא חרוש…
  • הסטיקר חייב להיות יפה 🙂

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

זה פחות או יותר העניין, נשמח לראותכם משתתפים, ויותר נשמח אם תגישו עיצובים נורמליים.

יום טוב.

דוגמאות לסטיקרים שאני אהבתי: (הצורות, והלוגואים הצבעוניים והיפים)
http://www.realgeek.com/geek-laptop-stickers

דוגמא ל template שאני חושב שנכון, את הצורה אפשר לשנות, אני מדבר יותר על מיקומים של דברים וצורה כללית:
(לקח לי שתי שניות להכין, אני עסוק, אז הבחירת צבעים לא משהו וזו ממש לא גרסה סופית, והיא יחסית מכוערת, זה רק mock up, וגנבתי ממישהו את הגרזן, בטח יתבעו אותי על זכויות יוצרים):

Binaryvision logo mockup

Binaryvision logo mockup

אני חושב שאולי כל הסטיקר צריך להיות בצורה של גרזן\גזע עץ+גרזן ועליו יהיה כתוב כל הטקסט, מה אתם חושבים? נראה לי הרעיון האחרון שהצעתי מעולה! 🙂

בקיצור, הביעו את דעתכם.

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

25 Comments :, , , more...

האם goto באמת שטני?

by on ינו.26, 2009, under כללי

מבוא
יש כל מיני "שיטות" תכנות, יש אנשים שמתכנתים "פרוצדוראלי", יש אנשים שמתכנתים "מונחה עצמים", יש אנשים שמעדיפים אבסטרקטיות ויש אנשים שלא. קיימות הרבה טענות בעד ונגד כל אחת מהשיטות, ישנה הסכמה שחלק מהשיטות נכונות יותר למקרים מסויימים, וחלק לאחרים, בקיצור, כולם מסכימים שאין שיטה אחת מושלמת, או לחלופין, נוראית. אולם, ישנה הסכמה ש goto הוא שליחו של השטן לעולם התיכנות.

לאלה מכם שלא יודעים goto זו קפיצה בלתי מותנית ב c (ודומותיה).
דוגמא לשימוש בקוד:

  1. goto exit;
  2. printf("hello world!\n");
  3. exit: return 0;

בקוד הזה, שורה 2 לעולם לא תקרא, שורה 1 תגרום לקפיצה בלתי מותנית (קרי: תמיד) לשורה 3, אשר תסיים את הרצת הפונקציה.


אז למה בעצם goto נחשב רע?

לפי דעתי ישנן כמה סיבות אפשריות:
1. goto נחשב רע עקב רצון שאנשים ישתמשו באלטרנטיבות היותר "high level" שלו, כמו for, while, if, try, etc… ובשביל שאנשים באמת ישתמשו בהם, התחילו להעליל על goto.
הנה לדוגמא קוד ב goto (סטייל אסמבלי) וקוד עם for:

  for (i=0; i<5 ; i++)
  	print("hi");

לעומת הקוד עם goto:

  i=0;
  
  start:
  if (i<5) 
  
  print ("hi");
  
  i++;
  goto start;
  
  exit:

2. יותר מידי אנשים שהיו רגילים לאסמבלי ששם אפשר לעשות שטויות עם goto שאי אפשר לעשות ב c, לדוגמא "קפיצות רחוקות" החליטו שלא רוצים בלאגן בקוד שלהם, ולכן צריך גישה חדשה, נטולת goto.
הנה קוד שלא אפשרי ב c אבל זו הגישה המדוברת והממש מעצבנת לעקיבה:

  int
  foo (int i) 
  {
  	goto a;	
  	return 0;
  }
  
  int
  bar (int j)
  {
  a:	
  	print("hi");
  	return 0;
  }

3. בעצם אנשים עשו בלאגן עם goto בעבר הרחוק ואז אנשים התרגלו לראות goto ולברוח בזוועה.
לדוגמא:

  while (i<6) {
  start:
  	for (j=0; j<5; j++) {
  		if (j==3)
  			goto start;
  	}
  	i++;
  }

למה כן
כל אחת מהסיבות האלה הגיונית ואפשרית אבל האם הן מספקות? הרי אפשר להתעלל בעוד הרבה פקודות ב C אם לא עושים דברים נכון. זה כמו לאסור להשתמש במצביעים ל void הרי עם שימוש במצביעים כאלה אין type checking בחלק מהמקומות וזה יכול להוביל לשגיאות. זה פשוט לא הגיוני!

לעומת הטענות החלשות נגד goto יש טענות חזקות בעד:
1. אנשים משתמשים ב goto (בלי לדעת) כל הזמן.
2. goto עוזר מאוד בכל מיני מקרים.

סינטקס קיים שפועל כמו goto
1. break ו continue זה goto שעטפו אותו מעט! הרי בקלות אפשר לממש את שניהם בעזרת goto:

  while (true) {
  	if (i>1)
  		continue;
  	else
  		break;
  }

ועם goto:

  while (true) {
  next:
  	if (i>1)
  		goto next;
  	else
  		goto end;
  }
  end:

והמימוש עם לולאות for לא שונה בהרבה!

מה בנוגע ל try and catch? (כן, אני יודע שזה c++)

  try {
  	if (error) {
  		throw "error!";
  	}
  }
  catch (char * str) {
  	printf("%s\n", str);
  }

לעומת:

  if (error) {
  	str = "error!";
  }
  else {
  	goto cont;
  }
  
  /* catch */
  error:
  printf("%s\n", str);
  
  cont:

אז נכון, להשתמש בסינטקס הקיים יותר נקי, ובטח מרגיש יותר נכון, אבל הוא התחיל כ goto סתם עטפו אותו יפה, ואם הוא בעצם מתנהג כמו goto, אז הרי בטח יש לו את אותן תכונות שטניות…
הסינטקסטים שנתתי מגבילים את המתכנת לא לעשות שטויות מוגזמות, אבל ע"י כתיבה נכונה גם עם goto אפשר לעשות קוד נקי ומובן.

שימושים נכונים:

אני לדוגמא משתמש ב goto רק בשימוש אחד (אני חייב להודות שקיבלתי השראה מהקוד של הקרנל…) ניקוי פונקצייה אחרי שגיאה:
לדוגמא ללא שימוש ב goto:

  int
  foo (int i)
  {
  	char *a;
  	char *b;
  	char *c;
  
  	a = malloc(5);
  	if (a == null) 
  		return -1;
  
  	b = malloc(6);
  	if (b == null) {
  		free(a);
  		return -1;
  	}
  	
  	c = malloc(7);
  	if (c == null) {
  		free(a);
  		free(b);
  		return -1;
  	}
  	return 0;
  }

לעומת ניקיון בעזרת goto:

  int
  foo (int i)
  {
  	int ret=0;
  	char *a;
  	char *b;
  	char *c;
  
  	a = malloc(5);
  	if (a == null) 
  		goto errora;
  
  	b = malloc(6);
  	if (b == null) 
  		goto errorb;
  	
  	c = malloc(7);
  	if (c == null) 
  		goto errorc;
  
  exit:
  	return ret;
  
  
/* error handling section */  
  errorc:
  	free(b);
  errorb:
  	free(a);
  errora:
  	ret = -1;
  	goto exit;
  }

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

עוד דוגמא:

  int
  foo (int i)
  {
  	int stop = 0;
  	while (true) {
  		while (true) {
  			if (i==3) {
  				stop = 1;
  				break;
  			}
  		}
  		if (stop)
  			break;
  	}
  	/*more code */
  }
  

לעומת:

  int
  foo (int i)
  {
  	while (true) {
  		while (true) {
  			if (i==3) 
  				goto end;
  		}
  	}
  end:
  	/*more code */
  }

או עוד דוגמא:

  if (a) {
  	if (b) {
  		if (c) {
  			printf("a ");
  			printf("= b = c = true\n");
  		}
  	}
  	else {
  		if (d) {
  			printf("a = !b = d = true\n");
  		}
  	}
  }

לעומת:

  if (!a)
  	goto end;
  
  if (b) {
  	if (! c)
  		goto endc;
  
  	printf("a ");
  	printf("= b = c = true\n");
  	
  	endc:
  }
  else {
  	if (! d)
  		goto endd;
   	
  	printf("a = !b = d = true\n");
  	
  	endd:
  }
  
  end:

שהרבה יותר ברור (ויותר חשוב, מונע הזחות מיותרות!).

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

2. בלי קפיצות "אחורה".
לא מומלץ לכתוב קוד שבו הקפיצה תוביל "למעלה" במעלי הקוד, כלומר לקוד שהורץ לפני ה goto, לדוגמא, לא לעשות דבר כזה:

  start:
  /*code*/
  goto start;

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

4. יש לכם עוד רעיונות? תכתוב בתגובות…

אני מקווה שעד עכשיו אתם מסכימים של goto יש מקום בעולם ואסור לפסול אותו על הסף, כי גם לו יש שימוש מעניינים ונכונים.

18 Comments :, , more...

מחפש משהו?

תשתמש בטופס למטה כדי לחפש באתר: