2018-2019 में सम्मेलनों में पीवीएस-स्टूडियो टीम द्वारा प्रस्तुत बग-खोज चुनौतियों का समाधान

चित्र 2


हाय! हालांकि 2019 सम्मेलन का मौसम अभी खत्म नहीं हुआ है, हम पिछले सम्मेलनों के दौरान अपने बूथ पर आगंतुकों को दी जाने वाली बग-खोज चुनौतियों के बारे में बात करना चाहते हैं। 2019 के पतन के साथ शुरू, हम चुनौतियों का एक नया सेट ला रहे हैं, इसलिए हम अब 2018 के पिछले कार्यों के समाधान और 2019 की पहली छमाही का खुलासा कर सकते हैं - आखिरकार, उनमें से कई पहले से पोस्ट किए गए लेखों से आए थे। और हमारे पास हमारे चुनौती पत्र पर छपे संबंधित लेखों की जानकारी के साथ एक लिंक या QR कोड था।

यदि आपने उन घटनाओं में भाग लिया जहां हमने एक बूथ के साथ भाग लिया, तो आपने शायद हमारी कुछ चुनौतियों को हल करने का प्रयास किया या देखा। ये C, C ++, C #, या Java में लिखे गए वास्तविक ओपन-सोर्स प्रोजेक्ट्स से कोड के स्निपेट हैं। प्रत्येक स्निपेट में एक बग होता है, और मेहमानों को इसे खोजने की कोशिश करने के लिए चुनौती दी जाती है। एक सफल समाधान (या बस बग की चर्चा में भागीदारी) एक पुरस्कार के साथ पुरस्कृत किया जाता है: एक सर्पिल-बाध्य डेस्कटॉप स्थिति, एक चाबी का गुच्छा, और जैसे:

चित्र 4

कुछ भी चाहिए? फिर आने वाले कार्यक्रमों में हमारे बूथ द्वारा छोड़ने का स्वागत करते हैं।

वैसे, " कांफ्रेंस टाइम! 2018 की समाप्ति! " और " सम्मेलन। 2019 की पहली छमाही के लिए उप-योग " के लेखों में, हम इस साल की शुरुआत में और 2018 में आयोजित होने वाले कार्यक्रमों में भाग लेने के अपने अनुभव को साझा करते हैं।

ठीक है, चलो हमारे "बग खोजें" खेल खेलते हैं। पहले हम भाषा द्वारा समूहीकृत 2018 की पूर्व की चुनौतियों पर एक नज़र डालेंगे।

2018


सी ++


क्रोमियम बग

static const int kDaysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; bool ValidateDateTime(const DateTime& time) { if (time.year < 1 || time.year > 9999 || time.month < 1 || time.month > 12 || time.day < 1 || time.day > 31 || time.hour < 0 || time.hour > 23 || time.minute < 0 || time.minute > 59 || time.second < 0 || time.second > 59) { return false; } if (time.month == 2 && IsLeapYear(time.year)) { return time.month <= kDaysInMonth[time.month] + 1; } else { return time.month <= kDaysInMonth[time.month]; } } 

समाधान
क्रोमियम में पाया गया यह बग संभवतः सबसे "लंबी चलने वाली" चुनौती थी; हम इसे 2018 के माध्यम से सभी तरह से पेश कर रहे थे और इसे कई प्रस्तुतियों में भी शामिल किया था।

 if (time.month == 2 && IsLeapYear(time.year)) { return time.month <= kDaysInMonth[time.month] + 1; // <= day } else { return time.month <= kDaysInMonth[time.month]; // <= day } 

अंतिम इफ- ब्लॉक के बॉडी में रिटर्न स्टेटमेंट में टाइपोस होता है : समय। महीने गलती से समय के बजाय दूसरी बार लिखा जाता था। यह गलती हर समय फ़ंक्शन को सही बनाती है । बग को " 31 फरवरी " लेख में विस्तार से चर्चा की गई है और यह एक बग का एक अच्छा उदाहरण है जो आसानी से कोड समीक्षा द्वारा देखा नहीं जाता है। यह मामला भी एक अच्छा प्रदर्शन है कि हम डेटाफ्लो विश्लेषण का उपयोग कैसे करते हैं।

अवास्तविक इंजन बग

 bool VertInfluencedByActiveBone( FParticleEmitterInstance* Owner, USkeletalMeshComponent* InSkelMeshComponent, int32 InVertexIndex, int32* OutBoneIndex = NULL); void UParticleModuleLocationSkelVertSurface::Spawn(....) { .... int32 BoneIndex1, BoneIndex2, BoneIndex3; BoneIndex1 = BoneIndex2 = BoneIndex3 = INDEX_NONE; if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2]) &BoneIndex3) { .... } 

समाधान
यहां ध्यान देने वाली पहली बात यह है कि वर्टफ्लुइंडबाइअक्टिवबोन () फ़ंक्शन के अंतिम तर्क में एक डिफ़ॉल्ट मान है और निर्दिष्ट करने की आवश्यकता नहीं है। अब देखिए कि क्या ब्लॉक सरल रूप में है:

 if (!foo(....) && !foo(....) && !foo(....) & arg) 

बग अब स्पष्ट रूप से दिखाई दे रहा है। टाइपो के कारण, वर्टीनएफ़्लुएंटबाइएक्टिवबोन () फ़ंक्शन की तीसरी कॉल चार के बजाय तीन तर्कों के साथ की जाती है, रिटर्न वैल्यू के साथ फिर एक & ऑपरेशन (बिटवाइंड एंड: लेफ्ट ऑपरेंड) में भाग लिया जाता है। ) , और सही ऑपरेंड पूर्णांक चर BoneIndex3 है )। कोड अभी भी संकलित है। यह निश्चित संस्करण है (एक अल्पविराम जोड़ा गया है, समापन कोष्ठक अभिव्यक्ति के अंत में चला गया है):

 if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2], &BoneIndex3)) 

इस त्रुटि को मूल रूप से " अ लॉन्ग-अवेटेड चेक ऑफ़ अनारियल इंजन 4 " लेख में उल्लेख किया गया था, जहां इसे "सबसे अच्छी त्रुटि" शीर्षक दिया गया था, जिसे मैं पूरी तरह से सहमत हूं।

Android बग्स

 void TagMonitor::parseTagsToMonitor(String8 tagNames) { std::lock_guard<std::mutex> lock(mMonitorMutex); // Expand shorthands if (ssize_t idx = tagNames.find("3a") != -1) { ssize_t end = tagNames.find(",", idx); char* start = tagNames.lockBuffer(tagNames.size()); start[idx] = '\0'; .... } .... } 

समाधान
प्रोग्रामर की आई ब्लॉक की स्थिति में संचालन की पूर्वता के बारे में गलत धारणाएं थीं। यह कोड अपेक्षा के अनुरूप काम नहीं करता है:

 if (ssize_t idx = (tagNames.find("3a") != -1)) 

Idx वैरिएबल को 0 या 1 मान दिया जाएगा, और क्या यह शर्त सही है या गलत इस मूल्य पर निर्भर करेगा, जो कि एक गलती है। यह निश्चित संस्करण है:

 ssize_t idx = tagNames.find("3a"); if (idx != -1) 

इस बग का उल्लेख " हमने पीवीएस-स्टूडियो द्वारा एंड्रॉइड सोर्स कोड की जांच की, या कुछ भी सही नहीं है ।"

यहां Android बग के साथ एक और गैर-तुच्छ चुनौती है:

 typedef int32_t GGLfixed; GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) { if ((d>>24) && ((d>>24)+1)) { n >>= 8; d >>= 8; } return gglMulx(n, gglRecip(d)); } 

समाधान
समस्या (d >> 24) + 1 अभिव्यक्ति में है।

प्रोग्रामर यह जांचना चाहता था कि डी वेरिएबल के 8 सबसे महत्वपूर्ण बिट्स एक बार में 1 पर सेट नहीं हैं। दूसरे शब्दों में, वे यह जांचना चाहते थे कि सबसे महत्वपूर्ण बाइट 0x00 और 0xFF को छोड़कर किसी भी मूल्य को संग्रहीत करता है। पहले प्रोग्रामर (d >> 24) अभिव्यक्ति का उपयोग करते हुए अशक्त के लिए सबसे महत्वपूर्ण बिट्स की जांच करता है। फिर वे आठ सबसे महत्वपूर्ण बिट्स को कम से कम महत्वपूर्ण बाइट में स्थानांतरित करते हैं, अन्य सभी बिट्स में नकल करने के लिए सबसे महत्वपूर्ण साइन बिट की अपेक्षा करते हैं। यही है, यदि d चर का मान 0b11111111'00000000'00000000'00000000 है, तो यह पारी के बाद 0b1111111111'11111111'11111111'11111111 में बदल जाएगा। 1 को int मान 0xFFFFFFFF में जोड़कर, प्रोग्रामर 0 (-1 + 1 = 0) प्राप्त करने की उम्मीद कर रहा है। इस प्रकार, ((d >> 24) +1) अभिव्यक्ति का उपयोग यह जांचने के लिए किया जाता है कि सभी आठ सबसे महत्वपूर्ण बिट्स 1 पर सेट नहीं हैं।

हालांकि, सबसे महत्वपूर्ण साइन बिट आवश्यक रूप से स्थानांतरित होने पर "फैल" नहीं होता है। यह वही है जो मानक कहता है: “ई 1 >> ई 2 का मूल्य ई 1 सही-स्थानांतरित शिफ्ट ई 2 बिट स्थिति है। यदि E1 में एक अहस्ताक्षरित प्रकार है या यदि E1 में एक हस्ताक्षरित प्रकार और एक गैर-नकारात्मक मूल्य है, तो परिणाम का मान E1 / 2 ^ E2 के भागफल का अभिन्न अंग है। यदि E1 में एक हस्ताक्षरित प्रकार और एक नकारात्मक मूल्य है, तो परिणामी मूल्य कार्यान्वयन-परिभाषित है । "

तो, यह कार्यान्वयन-परिभाषित व्यवहार का एक उदाहरण है। यह कोड कैसे काम करेगा यह सीपीयू वास्तुकला और संकलक कार्यान्वयन पर निर्भर करता है। सबसे महत्वपूर्ण बिट्स शिफ्ट के बाद जीरो के रूप में अच्छी तरह से समाप्त हो सकती हैं, और ((डी >> 24) +1) अभिव्यक्ति तब हमेशा 0 के अलावा एक मान लौटाएगी, यानी हमेशा एक सच्चे मूल्य।

वास्तव में, यह एक गैर-तुच्छ चुनौती है। पिछले बग की तरह, यह मूल रूप से " वीएसएस-स्टूडियो द्वारा एंड्रॉइड सोर्स कोड, या नथिंग परफेक्ट है " लेख में चर्चा की गई थी।

2019


सी ++


"यह सभी जीसीसी की गलती है"

 int foo(const unsigned char *s) { int r = 0; while(*s) { r += ((r * 20891 + *s *200) | *s ^ 4 | *s ^ 3) ^ (r >> 1); s++; } return r & 0x7fffffff; } 

प्रोग्रामर बग के लिए जीसीसी 8 कंपाइलर को दोषी मानता है। क्या यह वास्तव में जीसीसी की गलती है?

समाधान
यह फ़ंक्शन नकारात्मक मान लौटाता है क्योंकि कंपाइलर बिटवाइज़ और (&) के लिए कोड उत्पन्न नहीं करता है। बग का अपरिभाषित व्यवहार करना है। संकलक ने नोटिस किया कि r चर का उपयोग किसी राशि की गणना और भंडारण के लिए किया जाता है, जिसमें केवल सकारात्मक मान शामिल होते हैं। R वैरिएबल ओवरफ्लो नहीं होना चाहिए क्योंकि यह अपरिभाषित व्यवहार होगा, जो कंपाइलर के साथ पूरी तरह से रेकन करने के लिए बाध्य नहीं है। इसलिए यह निष्कर्ष निकालता है कि चूंकि r लूप के अंत में एक नकारात्मक मान नहीं हो सकता है, ऑपरेशन r & 0x7fffffff , जो साइन बिट को साफ़ करता है, अनावश्यक है, इसलिए यह फ़ंक्शन को r के मान को वापस करने के लिए कहता है।

यह त्रुटि " पीवीएस-स्टूडियो 6.26 जारी " लेख में वर्णित की गई थी।

क्यूटी बग

 static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } bool QMetaEnum::isFlag() const { const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1; return mobj && mobj->d.data[handle + offset] & EnumIsFlag; } 

समाधान
Mobj पॉइंटर को असुरक्षित तरीके से हैंडल किया जाता है: पहले डिरेल किया गया, फिर चेक किया गया। एक क्लासिक।

बग का उल्लेख " पीवीएस-स्टूडियो के साथ क्यूटी 5 की एक तीसरी जांच " लेख में किया गया था।

सी #


Infer.NET बग

 public static void WriteAttribute(TextWriter writer, string name, object defaultValue, object value, Func<object, string> converter = null) { if ( defaultValue == null && value == null || value.Equals(defaultValue)) { return; } string stringValue = converter == null ? value.ToString() : converter(value); writer.Write($"{name}=\"{stringValue}\" "); } 

समाधान
मान का मूल्यांकन करते समय अशक्तता घटित हो सकती है। मूल्यांकन (defaultValue) अभिव्यक्ति। यह तब होगा जब चर का मान इस तरह का हो कि defaultValue! = Null और value == null हो

यह बग " इनफ़रनेट कोड में क्या त्रुटियां लर्क " लेख से है ?

FastReport बग

 public class FastString { private const int initCapacity = 32; private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity); .... } public FastString() { Init(initCapacity); } public FastString(int iniCapacity) { Init(initCapacity); } public StringBuilder StringBuilder => sb; } .... Console.WriteLine(new FastString(256).StringBuilder.Capacity); 

कंसोल में प्रोग्राम आउटपुट क्या होगा? FastString वर्ग के साथ गलत क्या है?

समाधान
कार्यक्रम मूल्य 32 को आउटपुट करेगा। इसका कारण कंस्ट्रक्टर में इनिट विधि के लिए पारित चर का गलत नाम है:

 public FastString(int iniCapacity){ Init(initCapacity); } 

कंस्ट्रक्टर पैरामीटर iniCapacity का उपयोग नहीं किया जाएगा; इसके बजाय जो बीत जाता है वह है निरंतर असमानता

बग की चर्चा " द फास्टेस्ट रिपोर्ट्स इन द वाइल्ड वेस्ट - एंड अ हैंडफुल बग्स ... " में की गई थी।

रोजलिन बग

 private SyntaxNode GetNode(SyntaxNode root) { var current = root; .... while (current.FullSpan.Contains(....)) { .... var nodeOrToken = current.ChildThatContainsPosition(....); .... current = nodeOrToken.AsNode(); } .... } public SyntaxNode AsNode() { if (_token != null) { return null; } return _nodeOrParent; } 

समाधान
वर्तमान में वर्तमान में संभावित अशक्तता का प्रभाव। FullSpan.Contains (....) अभिव्यक्ति। वर्तमान चर को नोडऑट्रोटेन.एन्सनोड () विधि के परिणामस्वरूप निल मान दिया जा सकता है।

यह बग " रोसलिन सोर्स कोड की जाँच " लेख से है।

एकता बग

 .... staticFields = packedSnapshot.typeDescriptions .Where(t => t.staticFieldBytes != null & t.staticFieldBytes.Length > 0) .Select(t => UnpackStaticFields(t)) .ToArray() .... 

समाधान
एक टाइपो: और ऑपरेटर का उपयोग && के बजाय किया जाता है। यह t.staticFieldBytes.Length> 0 को क्रियान्वित करने का परिणाम है, भले ही t.staticFieldBytes वेरिएबल अशक्त हो , जो अपनी बारी में, एक अशांति की ओर जाता है।

यह बग मूल रूप से " यूनिटी 3 डी के ओपन-सोर्स कंपोनेंट्स में त्रुटियों पर चर्चा " लेख में दिखाया गया था।

जावा


इंटेलीज आईडिया बग

 private static boolean checkSentenceCapitalization(@NotNull String value) { List<String> words = StringUtil.split(value, " "); .... int capitalized = 1; .... return capitalized / words.size() < 0.2; // allow reasonable amount of // capitalized words } 

कार्यक्रम गलत तरीके से पूंजीकृत शब्दों की संख्या की गणना क्यों करता है?

समाधान
यदि पूंजीकृत शब्दों की संख्या 20% से कम है, तो फ़ंक्शन सही लौटने की उम्मीद है। लेकिन पूर्णांक विभाजन के कारण चेक काम नहीं करता है, जो केवल 0 या 1 का मूल्यांकन करता है। फ़ंक्शन केवल तभी गलत होगा, जब सभी शब्द बड़े हो जाएंगे। अन्यथा, विभाजन का परिणाम 0 होगा और फ़ंक्शन सही लौटेगा।

यह बग " पीवीएस-स्टूडियो फॉर जावा " लेख से है।

स्पॉटबग्स बग

 public static String getXMLType(@WillNotClose InputStream in) throws IOException { .... String s; int count = 0; while (count < 4) { s = r.readLine(); if (s == null) { break; } Matcher m = tag.matcher(s); if (m.find()) { return m.group(1); } } throw new IOException("Didn't find xml tag"); .... } 

Xml टैग की खोज में क्या गलत है?

समाधान
गिनती <4 स्थिति हमेशा सत्य होगी क्योंकि चर गणना लूप के अंदर नहीं बढ़ाई जाती है। Xml टैग को फ़ाइल की पहली चार पंक्तियों में खोजा जाना था, लेकिन कमी के कारण प्रोग्राम पूरी फ़ाइल को पढ़ रहा होगा।

पिछले बग की तरह, यह " जावा के लिए पीवीएस-स्टूडियो " लेख में वर्णित किया गया था।

आज के लिए बस इतना ही। आने वाली घटनाओं पर हमें देखें - गेंडा की तलाश करें। हम नई दिलचस्प चुनौतियों की पेशकश करेंगे और निश्चित रूप से, पुरस्कार दे रहे हैं। मिलते हैं!

Source: https://habr.com/ru/post/hi476268/


All Articles