سؤال

النظر في البرنامج أدناه

    char str[5];
    strcpy(str,"Hello12345678");
    printf("%s",str);

عند تشغيل هذا البرنامج يعطي خطأ تجزئة.

ولكن عند استبدال STRCPY بالمتابعة، يعمل البرنامج بشكل جيد.

strcpy(str,"Hello1234567");

حتى تستفسح على أنه يجب أن تعطل عند محاولة النسخ إلى STR أي سلسلة أخرى لأكثر من 5 حرف.

فلماذا لا يتعطل من أجل "Hello1234567" ويعطل فقط "Hello12345678" IE من السلسلة مع طول 13 أو أكثر من 13.

تم تشغيل هذا البرنامج على آلة 32 بت.

هل كانت مفيدة؟

المحلول

هناك ثلاثة أنواع من سلوك المعايير التي يجب أن تكون مهتما بها.

1/ السلوك المحدد. وبعد هذا سيعمل على جميع التطبيقات الامتثال. استخدم هذا بحرية.

2/ السلوك المحدد بالتنفيذ. وبعد كما هو مذكور، فإنه يعتمد على التنفيذ ولكن على الأقل لا يزال محددا. يجب أن تؤدي التطبيقات إلى توثيق ما يفعلونه في هذه الحالات. استخدم هذا إذا كنت لا تهتم بالحمل.

3/ السلوك غير محدد. وبعد أي شيء يمكن أن يحدث. ونحن نعني اي شى, ، ما يصل إلى وتشمل الكمبيوتر بالكامل ينهار في التفرد المجردة وابتلاع نفسه، أنت ونسبة كبيرة من زملائكم. لا تستخدم هذا أبدا. أي وقت مضى! عنجد! لا تجعلني جاء هناك.

نسخ أكثر من 4 أحرف و صفرية بايت إلى char[5] هو السلوك غير محدد.

على محمل الجد، لا يهم السبب في تعطل برنامجك مع 14 حرفا ولكن ليس 13 عاما تقريبا، فأنت بالتأكيد الكتابة فوق بعض المعلومات غير التي تحطمها على المكدس، وسوف ينتج البرنامج على الأرجح نتائج غير صحيحة على أي حال. في الواقع، يكون الحادث أفضل منذ أن توقفك على الأقل عن الاعتماد على الآثار السيئة المحتوية.

زيادة حجم الصفيف إلى شيء أكثر ملاءمة (char[14] في هذه الحالة مع المعلومات المتاحة) أو استخدم بعض بنية البيانات الأخرى التي يمكن أن تعامل.


تحديث:

نظرا لأنك يبدو بالقلق للغاية مع معرفة سبب عدم تسبب أحرف إضافية إضافية على مشاكل ولكن 8 أحرف، فلنصنع تخطيط المكدس المحتمل عند الدخول main(). وبعد أقول "ممكن" منذ أن يعتمد التصميم الفعلي على اتفاقية الاتصال التي يستخدمها برنامج التحويل البرمجي الخاص بك. منذ مكالمات رمز بدء التشغيل C main() مع argc و argv, ، المكدس في بداية main(), ، بعد تخصيص مساحة ل char[5], ، يمكن أن تبدو مثل هذا:

+------------------------------------+
| C start-up code return address (4) |
| argc (4)                           |
| argv (4)                           |
| x = char[5] (5)                    |
+------------------------------------+

عندما تكتب البايتات Hello1234567\0 مع:

strcpy (x, "Hello1234567");

ل x, ، إنه يبث argc و argv ولكن، عند العودة من main(), ، حسنا. خاصة Hello populates. x, 1234 populates. argv و 567\0 populates. argc. وبعد شريطة أن لا تحاول في الواقع استعمال argc و / أو argv بعد ذلك، ستكون بخير:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4)                           |   '567<NUL>'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

ومع ذلك، إذا كتبت Hello12345678\0 (لاحظ إضافي "8") x, ، إنه يبث argc و argv و أيضا بايت واحد من عنوان المرسل بحيث عندما main() محاولات العودة إلى رمز بدء التشغيل C، وتخرج إلى الأرض الخيالية بدلا من ذلك:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |   '<NUL>'
| argc (4)                           |   '5678'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

مرة أخرى، هذا يعتمد بالكامل على اتفاقية الاتصال المترجم الخاص بك. من الممكن وجود مترجم مختلف ستقوم دائما بتصفيد الصفائف إلى مضاعفات 4 بايت ولن يفشل الكود هناك حتى كتبت ثلاثة أحرف أخرى. حتى نفس التحويل البرمجي قد يخصص المتغيرات على إطار المكدس بشكل مختلف لضمان محاذاة راضية.

هذا ما يعنيه undefined: أنت لا تفعل ذلك أعرف ماذا سيحدث.

نصائح أخرى

أنت نسخ إلى المكدس، لذلك يعتمد على ما وضعه المحول البرمجي على المكدس، لكي تطلب من البيانات الإضافية تحطم البرنامج الخاص بك.

قد ينتج بعض المترجمين رمز من شأنه أن يتعطل مع بايت واحد فقط عبر الحجم المخزن المؤقت - إنه غير محدد ما هو السلوك.

أعتقد أن حجم 13 يكفي للكتابة فوق عنوان المرسل، أو شيء مماثل، والذي يعطل عند إرجاع وظيفتك. لكن مترجم آخر أو منصة أخرى يمكن أن يتعطل مع طول مختلف.

كما قد يتعطل برنامجك بطول مختلف إذا كان يركض لفترة أطول، إذا كان هناك شيء أقل أهمية تم الكتابة فوقه.

لمنصة إنتل 32 بت، فإن Explanation هو ما يلي. عندما تعلن سحر [5] على كومة المترجم يخصص حقا 8 بايت بسبب المحاذاة. ثم من النموذجي للوظائف أن يكون لديك مقدمة التالية:

push ebp
mov ebp, esp

هذا يحفظ قيمة التسجيل EBP على المكدس، ثم يتحرك قيمة تسجيل ESP في EBP لاستخدام قيمة ESP للوصول إلى المعلمات. هذا يؤدي إلى 4 بايت أكثر على المكدس المراد احتلال قيمة EBP.

يتم استعادة Epilogue EBP، ولكن عادة ما يتم استخدام قيمتها فقط للوصول إلى معلمات الوظائف المخصصة للمكدس، لذلك قد لا تؤذي الكتابة فوقه في معظم الحالات.

لذلك لديك التصميم التالي (تكدس ينمو إلى أسفل على Intel): 8 بايت من صفيفك، ثم 4 بايت ل EBP، ثم عادة عنوان المرسل.

هذا هو السبب في أنك تحتاج إلى الكتابة فوق 13 بايت على الأقل لتعطل برنامجك.

لإضافة إلى الإجابات أعلاه: يمكنك اختبار الأخطاء مثل هذه مع أداة مثل valgrind.. وبعد إذا كنت على ويندوز، إلقاء نظرة على هذا موضوع جدا..

ذلك يعتمد على ما هو على المكدس بعد صفيف "Str". لا تحدث فقط عدم الاستدلال على أي شيء حرج حتى تقوم بنسخ العديد من الشخصيات.

لذلك ستعتمد على ما هو في الوظيفة، فإن التحويل البرمجي الذي تستخدمه وربما خيارات المحول البرمجي أيضا.

13 هو 5 + 8، مما يشير إلى وجود كلمتين غير مهمين بعد صفيف شارع، ثم شيء حرج (ربما عنوان المرسل)

هذا هو الجمال النقي لسلوك غير محدد (UB): إنه غير محدد.

الرمز الخاص بك:

char str[5];
strcpy(str,"Hello12345678");

يكتب 14 بايت / خرط str والتي يمكن أن تعقد فقط 5 بايت / سحر. هذا يستدعي UB.

س: فلماذا لا يتعطل من أجل "Hello1234567" ويعطل فقط "hello12345678" أي من السلسلة مع طول 13 أو أكثر من 13.

لأن السلوك غير محدد. استخدام strncpy. انظر هذه الصفحة http://en.wikipedia.org/wiki/strcpy.للمزيد من المعلومات.

STRNCPY غير آمن لأنه لا يضيف إنهاء فارغ إذا كانت السلسلة المصدر لها طول> = n حيث n هو حجم السلسلة الوجهة.

char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash

نحن نستخدم دائما strlcpy لتخفيف هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top