2012-09-11 9 views
9

मैं बग जो मेरे OpenPGP मॉड्यूल https://github.com/singpolyma/OpenPGP-Haskell/blob/master/Data/OpenPGP.hs में है के लिए एक साधारण परीक्षण धावक है:यह कोड ऑप्टोमाइज़ेशन के साथ अलग-अलग व्यवहार क्यों करता है?

module Main where 

import Data.OpenPGP 
import Data.Binary (encode, decode) 

packet = EmbeddedSignaturePacket (signaturePacket 2 168 ECDSA SHA256 [] [SignatureCreationTimePacket 1013401916,IssuerPacket "36FE856F4219F1C7"] 48065 [MPI 4,MPI 11,MPI 60,MPI 69,MPI 37,MPI 33,MPI 18,MPI 72,MPI 41,MPI 36,MPI 43,MPI 41,MPI 53,MPI 9,MPI 53,MPI 35,MPI 3,MPI 40,MPI 14,MPI 79,MPI 1,MPI 4,MPI 51,MPI 23,MPI 62,MPI 62,MPI 62,MPI 7,MPI 68,MPI 51,MPI 13,MPI 49,MPI 8,MPI 64,MPI 32,MPI 50,MPI 59,MPI 17,MPI 43,MPI 12,MPI 67,MPI 5,MPI 67,MPI 5,MPI 25,MPI 63,MPI 0,MPI 53,MPI 2,MPI 36,MPI 83,MPI 39,MPI 54,MPI 65,MPI 54,MPI 35,MPI 62,MPI 63,MPI 26,MPI 4,MPI 82,MPI 57,MPI 85,MPI 71,MPI 43,MPI 77]) 

main = print $ decode (encode packet) == packet 

आप संकलन तो इस के साथ (GHC 7.4.1 पर):

ghc -O0 -fforce-recomp --make t.hs 

यह उम्मीद के रूप में काम करता है (जो है, यह True प्रिंट), लेकिन आप इस तरह से संकलन करता है, तो:

ghc -O1 -fforce-recomp --make t.hs 

या इस:

ghc -O2 -fforce-recomp --make t.hs 

यह False प्रिंट करेगा।

मैं किसी भी एक्सटेंशन (सीपीपी के मामूली उपयोग को छोड़कर) या निम्न स्तर या असुरक्षित कॉल का उपयोग नहीं कर रहा हूं, और व्यवहार मेरी लाइब्रेरी से होना चाहिए और निर्भरता नहीं, क्योंकि यह केवल मेरा कोड है जो यहां पुनः संकलित हो रहा है ।

+5

मैं इस बग को जीएचसी 7.4.2 –

+1

में पुन: पेश कर सकता हूं क्या आप इस बग को देखते समय बाइनरी या अनाज का उपयोग कर रहे हैं? –

उत्तर

5

यह आपके कोड में एक त्रुटि है।

MPI 63,MPI 0,MPI 53 
     ^^^^^ 

और

instance BINARY_CLASS MPI where 
    put (MPI i) = do 
     put (((fromIntegral . B.length $ bytes) - 1) * 8 
       + floor (logBase (2::Double) $ fromIntegral (bytes `B.index` 0)) 
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
       + 1 :: Word16) 
    putSomeByteString bytes 
    where 
    bytes = if B.null bytes' then B.singleton 0 else bytes' 
    bytes' = B.reverse $ B.unfoldr (\x -> 
        if x == 0 then Nothing else 
          Just (fromIntegral x, x `shiftR` 8) 
      ) (assertProp (>=0) i) 

पर विचार करें अब, अगर हम MPI 0 सांकेतिक शब्दों में बदलना, bytes', रिक्त है इस प्रकार bytes = B.singleton 0 और इसलिए bytes `B.index` 0 0.

लेकिन logBase 2 0-Infinity है, और floor केवल अच्छी तरह से है सीमित मूल्यों के लिए परिभाषित (लक्ष्य प्रकार की सीमा के भीतर)।

अनुकूलन के बिना संकलन करते समय, floordecodeFloat के माध्यम से बिट-पैटर्न का उपयोग करता है। फिर floor (logBase 2 0) सभी मानक निश्चित-चौड़ाई पूर्णांक प्रकारों के लिए 0 उत्पन्न करता है।

अनुकूलन के साथ, एक पुनर्लेखन नियम सक्रिय है और floor प्राइमॉप double2Int# का उपयोग करता है, जो हार्डवेयर को जो भी करता है, x86 resps पर देता है। x86-64, यह minBound :: Int है, जहां तक ​​मुझे पता है, बिट-पैटर्न के बावजूद।प्रासंगिक कोड है

floorDoubleInt :: Double -> Int 
floorDoubleInt (D# x) = 
    case double2Int# x of 
     n | x <## int2Double# n -> I# (n -# 1#) 
     | otherwise    -> I# n 

और बेशक, -Infinity < int2Double minBound की है, तो मूल्य minBound - 1, जो आमतौर पर maxBound है हो जाता है।

पाठ्यक्रम है कि एक गलत परिणाम कारणों में से

, अब के बाद से "लंबाई" वह यह है कि MPI 0 के लिए put 0 हो जाता है, और 0 बाइट "लंबाई" क्षेत्र के बाद डाल अगले MPI की "लंबाई" के भाग के रूप में स्वीकार किया गया ।

+0

धन्यवाद! मैं 'फ्लोर' के व्यवहार को 'ओओ' के साथ बदलने की उम्मीद नहीं करता था, लेकिन आप सही हैं कि मेरी धारणाओं में मुझे एक बग था। – singpolyma

+1

कुछ ऐसे स्थान हैं जहां नियमों को फिर से लिखना व्यवहार को बदलता है। अधिकांशतः जब कोई सही परिणाम नहीं होता है, वैसे भी 'फर्श' एट अल के लिए ऑफ-ऑफ-रेंज मानों की तरह। लेकिन कभी-कभी अर्थपूर्ण परिणामों वाले स्थानों में भी, उदा। '(realToFrac :: Float -> Double) (0/0)' ऑप्टिमाइज़ेशन के साथ 'NaN' अनुकूलन के बिना' -5.104235503814077e38' का उत्पादन करता है। भाषा रिपोर्ट में कहा गया है कि 'realToFrac = fromRational। toRational', जो पहले पैदा करता है। चूंकि 'तर्कसंगत' वास्तव में 'NaN और infinities को संभाल नहीं सकता है, इसलिए उस रूपांतरण में उनका इलाज करने का कोई अच्छा तरीका नहीं है और वे गिर गए हैं। Primop उन्हें संरक्षित करता है। –

+0

नाइन शामिल होने पर चीजें हमेशा अधिक मजेदार होती हैं ... –

5

समस्या MPI के लिए आपके BINARY_CLASS उदाहरण से संबंधित है।

newtype MPI = MPI Integer deriving (Show, Read, Eq, Ord) 
instance BINARY_CLASS MPI where 
    put (MPI i) = do 
    put (fromIntegral $ B.length bytes :: Word16) 
    putSomeByteString bytes 
    where 
    bytes = if B.null bytes' then B.singleton 0 else bytes' 
    bytes' = B.pack . map (read . (:[])) $ show i 
    get = do 
    length <- fmap fromIntegral (get :: Get Word16) 
    bytes <- getSomeByteString length 
    return (MPI $ read $ concatMap show $ B.unpack bytes) 

समस्या ठीक करता है: यदि मैं

main = do 
    print packet 
    print (decode (encode packet) :: SignatureSubpacket) 
    print $ decode (encode packet) == packet 

बदल मैं निर्गम (-O2 साथ संकलित)

EmbeddedSignaturePacket (SignaturePacket {version = 2, signature_type = 168, key_algorithm = ECDSA, hash_algorithm = SHA256, hashed_subpackets = [], unhashed_subpackets = [SignatureCreationTimePacket 1013401916,IssuerPacket "36FE856F4219F1C7"], hash_head = 48065, signature = [MPI 4,MPI 11,MPI 60,MPI 69,MPI 37,MPI 33,MPI 18,MPI 72,MPI 41,MPI 36,MPI 43,MPI 41,MPI 53,MPI 9,MPI 53,MPI 35,MPI 3,MPI 40,MPI 14,MPI 79,MPI 1,MPI 4,MPI 51,MPI 23,MPI 62,MPI 62,MPI 62,MPI 7,MPI 68,MPI 51,MPI 13,MPI 49,MPI 8,MPI 64,MPI 32,MPI 50,MPI 59,MPI 17,MPI 43,MPI 12,MPI 67,MPI 5,MPI 67,MPI 5,MPI 25,MPI 63,MPI 0,MPI 53,MPI 2,MPI 36,MPI 83,MPI 39,MPI 54,MPI 65,MPI 54,MPI 35,MPI 62,MPI 63,MPI 26,MPI 4,MPI 82,MPI 57,MPI 85,MPI 71,MPI 43,MPI 77], trailer = Chunk "\168" (Chunk "<gI<" Empty)}) 
EmbeddedSignaturePacket (SignaturePacket {version = 2, signature_type = 168, key_algorithm = ECDSA, hash_algorithm = SHA256, hashed_subpackets = [], unhashed_subpackets = [SignatureCreationTimePacket 1013401916,IssuerPacket "36FE856F4219F1C7"], hash_head = 48065, signature = [MPI 4,MPI 11,MPI 60,MPI 69,MPI 37,MPI 33,MPI 18,MPI 72,MPI 41,MPI 36,MPI 43,MPI 41,MPI 53,MPI 9,MPI 53,MPI 35,MPI 3,MPI 40,MPI 14,MPI 79,MPI 1,MPI 4,MPI 51,MPI 23,MPI 62,MPI 62,MPI 62,MPI 7,MPI 68,MPI 51,MPI 13,MPI 49,MPI 8,MPI 64,MPI 32,MPI 50,MPI 59,MPI 17,MPI 43,MPI 12,MPI 67,MPI 5,MPI 67,MPI 5,MPI 25,MPI 63,MPI 0,MPI 0,MPI 339782829898145924110968965855122255180100961470274369007196973863828909184332476115285611703086303618816635857833592912611149], trailer = Chunk "\168" (Chunk "<gI<" Empty)}) 

इस और अधिक सरल कार्यान्वयन करने के लिए अपने एमपीआई उदाहरण बदलना देखें।

ऐसी कुछ चीजें हैं जो समस्या स्रोत हो सकती हैं। यह संभव है कि आपका कोड सही है (मैंने इस तरह से एक या दूसरे की जांच नहीं की है), इस मामले में जीएचसी कुछ अमान्य परिवर्तन कर रहा है जिससे कहीं अधिक ओवरफ्लो/अंडरफ्लो होता है। यह भी संभव है कि आपका कोड कुछ गलत कर रहा है जो केवल कुछ अनुकूलन द्वारा उजागर किया गया है।

संबंधित मुद्दे