2012-09-11 23 views
9

Tôi có một người chạy thử nghiệm đơn giản cho các lỗi mà là ở mô-đun OpenPGP tôi https://github.com/singpolyma/OpenPGP-Haskell/blob/master/Data/OpenPGP.hs:Tại sao mã này hoạt động khác với các tùy chọn bật hoặc tắt?

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 

Nếu bạn biên dịch này (trên GHC 7.4.1) với:

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

Nó hoạt động như mong đợi (có nghĩa là, nó in True), nhưng nếu bạn biên dịch như thế này:

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

hay này:

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

Nó sẽ in False.

Tôi không sử dụng bất kỳ tiện ích mở rộng nào (ngoại trừ việc sử dụng CPP nhỏ) hoặc cuộc gọi cấp thấp hoặc không an toàn và hành vi phải từ thư viện của tôi chứ không phải phụ thuộc, vì chỉ có mã của tôi được biên dịch lại ở đây .

+5

Tôi có thể tạo lại lỗi này trong GHC 7.4.2 –

+1

Bạn đang sử dụng nhị phân hoặc ngũ cốc khi bạn quan sát lỗi này? –

Trả lời

5

Đó là lỗi trong mã của bạn. Cân nhắc

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) 

Bây giờ, nếu chúng ta mã hóa MPI 0, bytes' là trống rỗng, do đó bytes = B.singleton 0 và do đó bytes `B.index` 0 là 0.

Nhưng logBase 2 0-Infinity, và floor chỉ nổi được định nghĩa cho các giá trị hữu hạn (trong phạm vi của kiểu đích).

Khi biên dịch mà không tối ưu hóa, floor sử dụng mẫu bit qua decodeFloat. Sau đó, floor (logBase 2 0) sản lượng 0 cho tất cả các loại số nguyên cố định chiều rộng chuẩn.

Với tối ưu hóa, quy tắc ghi lại đang hoạt động và floor sử dụng primop double2Int#, trả về bất kỳ phần cứng nào, trên x86 resp. x86-64, đó là minBound :: Int, theo như tôi biết, bất kể mẫu bit.Các mã có liên quan là

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

và dĩ nhiên, -Infinity < int2Double minBound, vì vậy giá trị trở nên minBound - 1, mà thường là maxBound.

Tất nhiên gây ra kết quả sai, vì bây giờ "độ dài" là put cho MPI 0 trở thành 0 và 0 byte đặt sau trường "độ dài" được hiểu là một phần của "độ dài" của MPI tiếp theo .

+0

Cảm ơn! Tôi sẽ không mong đợi hành vi của 'sàn 'để thay đổi với' -O', nhưng bạn nói đúng là tôi đã có một lỗi trong các giả định của tôi. – singpolyma

+1

Có một vài nơi mà quy tắc viết lại thay đổi hành vi. Chủ yếu là khi không có kết quả chính xác anyway, giống như với các giá trị ngoài phạm vi cho 'floor' et al. Nhưng đôi khi ngay cả ở những nơi có kết quả có ý nghĩa, ví dụ: '(realToFrac :: Float -> Double) (0/0)' tạo '-5.104235503814077e38' mà không tối ưu hóa,' NaN' với các tối ưu hóa. Báo cáo ngôn ngữ nói 'realToFrac = fromRational. toRational', tạo ra giá trị đầu tiên. Vì 'Rational' không thể thực sự xử lý' NaN 'và infinities, không có cách nào tốt để đối xử với chúng trong chuyển đổi đó và chúng bị ghi đè. Các primop bảo tồn chúng. –

+0

Mọi thứ luôn thú vị hơn nhiều khi NaN tham gia ... –

5

Sự cố liên quan đến phiên bản BINARY_CLASS của bạn cho MPI. Nếu tôi thay đổi

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

tôi nhìn thấy đầu ra (biên soạn với -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)}) 

Thay đổi Ví dụ Bộ KH & ĐT của bạn để thực hiện đơn giản hơn này:

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) 

sửa chữa vấn đề.

Có một vài điều có thể là nguồn sự cố. Có thể mã của bạn là chính xác (tôi chưa kiểm tra theo cách này hay cách khác), trong trường hợp đó GHC đang thực hiện một số phép chuyển đổi không hợp lệ dẫn đến tràn/tràn ở đâu đó. Cũng có thể mã của bạn đang làm điều gì đó không chính xác mà chỉ bị phơi bày bởi một số tối ưu hóa nhất định.

Các vấn đề liên quan