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

ここで再コンパイルされるのは私のコードだけなので、拡張機能 (CPP の些細な使用を除く) または低レベルまたは安全でない呼び出しを使用していません。

4

2 に答える 2

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` 00 になります。

ただし、 であり、logBase 2 0有限値 (対象の型の範囲内) に対してのみ明確に定義されています。-Infinityfloor

最適化なしでコンパイルする場合floorは、 を介してビットパターンを使用しますdecodeFloat。次にfloor (logBase 2 0)、すべての標準固定幅整数型に対して 0 を生成します。

最適化により、rewrite-rule がアクティブになり、x86 resp でハードウェアが行うことを返すfloorprimop が使用されます。double2Int#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

もちろん、それは間違った結果を引き起こします。これは、 の「長さ」が 0putMPI 0なり、「長さ」フィールドの後に置かれた 0 バイトが次の「長さ」の一部として解釈されるためMPIです。

于 2012-09-11T13:24:07.590 に答える
5

この問題は、 の BINARY_CLASS インスタンスに関連していますMPI。私が変われば

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)})

MPI インスタンスをこのより単純な実装に変更します。

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)

問題を修正します。

問題の原因となる可能性のあるものがいくつかあります。あなたのコードが正しい可能性があります (私はこれを何らかの方法でチェックしていません)。その場合、GHC は何らかの無効な変換を実行しており、オーバーフロー/アンダーフローがどこかで発生しています。また、コードが、特定の最適化によってのみ明らかになる誤った処理を行っている可能性もあります。

于 2012-09-11T05:42:28.957 に答える