2014年3月25日火曜日

ClamAV のオンアクセス スキャン

ClamAV のオンアクセス スキャンを行おうとした場合、以前は ClamFS か Dazuko (Clamuko) を使用することになっていたが、最近のバージョンではカーネルの fanotify を使用した方法に変わっていた。これを (Gentoo で) 使ってみようとしたところ注意が必要な事項があったのでそれらについて記載する。
環境は以下のとおり。
  • linux-3.12.13 (amd64: これが重要!)
  • clamav-0.98.1 

    1. fanotify サポートの有効化

    1.1 カーネルオプション

    gentoo-sources ではデフォルト無効だった。
    File systems  --->
     [*] Filesystem wide access notification
    

    1.2 ClamAV

    Gentoo ebuild では 2014 年 3 月現在、clamav の fanotify サポートが無効化 (--disable-fanotify) されており、オンアクセス スキャンが使用できない。けれども、x86 / amd64 では fanotify サポートを無効化しなくとも動作させることができるので、当該オプションを削除して fanotify サポートを有効化させることが可能。

    2. サブディレクトリの検索

    オンアクセス スキャンの対象ディレクトリは clamd.conf の OnAccessIncludePath で指定することになるが、当該ディレクティブはサブディレクトリを対象としない。つまり、OnAccessIncludePath / と指定しても ~/eicar.com へのアクセスはスキャンされない。これは期待された動作らしい (!)。私が期待する動作は (サブディレクトリも含めて) ファイルシステム全体をスキャンすることだったため以下のようにコードを変更した。
    --- a/clamd/fan.c       2014-01-13 12:02:18.000000000 -0500
    +++ b/clamd/fan.c       2014-03-23 20:33:12.382967001 -0400
    @@ -134,7 +134,7 @@
     
         if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
            while(pt) {
    -           if(fanotify_mark(fan_fd, FAN_MARK_ADD, fan_mask, fan_fd, pt->strarg) != 0) {
    +           if(fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, fan_mask, fan_fd, pt->strarg) != 0) {
                    logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg);
                    return NULL;
                } else
    
    FAN_MARK_MOUNT はマウントポイント全体を対象にするオプションのためサブディレクトリの検索とは異なるが、まあファイルシステム全体を対象にしてスキャンする使い方には問題がない。

    3. ebuild とパッチ

    ClamAV か関連の上記 2 点に対応した ebuild とパッチ (x86 / amd64 用) を https://github.com/drmn/portage/tree/master/app-antivirus/clamav に置いた。

    参考

    履歴

    • 2014/03/26: カーネルオプションについて追記、FAN_MARK_MOUNT に関する説明の誤りを訂正 (K 氏ご指摘)

    2014年3月3日月曜日

    Portage の userpriv feature

    vmware-modules が以下のエラーで emerge できず、しばらく VMware Player を使えていなかった。
    # emerge -av1 vmware-modules
    
    These are the packages that would be merged, in order:
    
    Calculating dependencies... done!
    [ebuild   R   ~] app-emulation/vmware-modules-279.0  USE="-pax_kernel" 0 kB
    
    -- snip --
    
    >>> Compiling source in /var/tmp/portage/app-emulation/vmware-modules-279.0/work ...
     * Preparing vmblock module
    make -j -l 3 HOSTCC=x86_64-pc-linux-gnu-gcc CROSS_COMPILE=x86_64-pc-linux-gnu- 'LDFLAGS=-m elf_x86_64' auto-build KERNEL_DIR=/usr/src/linux KBUILD_OUTPUT=/lib/modules/3.10.25-gentoo/build 
    Using standalone build system.
    Makefile:167: Makefile.normal: No such file or directory
    make: *** No rule to make target `Makefile.normal'.  Stop.
     * ERROR: app-emulation/vmware-modules-279.0::gentoo failed (compile phase):
     *   emake failed
    -- snip --
    
    これはどうやらカーネルのソースツリーを参照しに行ったけれどもファイルが見つからなかったというものらしく、以下で無事 emerge できた。
    # FEATURES="-userpriv" emerge -av1 vmware-modules
    
    userpriv feature は emerge のプロセスをユーザ権限 (portage) で実行する機能で、portage ユーザに必要なパーミッションがない場合に上記のようなエラーで失敗するもよう。FEATURE="-userpriv" することにより emerge が root 権限で実行され、このパーミッション問題が解決した。
    検索しても直接関わる情報が見つからなかったのは、umask をデフォルトより厳しい値 (0077) に設定していたからと思われる。また、この他にもカーネルモジュールを emerge する場合には同様の問題が発生するだろう。

    ということで、今後の emerge で -userpriv されるように以下の設定を行った。
    # echo 'FEATURES="-userpriv"' >> /etc/portage/env/rootpriv.conf
    # ecoo 'app-emulation/vmware-modules rootpriv.conf' >> /etc/portage/package.env
    

    2013年9月28日土曜日

    モナドの実際の使い方

    私の感覚としては、モナドはかなりわかりづらい。モナドの定義とか計算法則はわりとわかる。以下サイトがおすすめ。

    Haskell の State モナド (1) - 状態を模倣する | すぐに忘れる脳みそのためのメモ:
    http://jutememo.blogspot.jp/2009/10/haskell-state-1.html

    しかし実際にプログラムを書いてみると、使い方がわからない。特に IO モナドがわからない。実際にプログラムを書いて困った点、それらをどのように解決しているか? ということをまとめる。
    今から振り返ると Real World Haskell の内容ちゃんと理解して読んでたら問題にすらならなかったんですけどね。

    1. 副作用の分離?

    モナドは副作用のある計算を分離するなど言われる。確かに多くのモナドでは純粋な関数からモナドを使用し、run することにより値を取り出すことができ、状態によって結果が変わるかもしれないコードを分離することができる。素晴しい。
    ただし、最もよく使われるであろう IO モナドは値を取り出してモナドから純粋な関数に戻ることができない (unsafePerformIO は考えないことにする)。つまり、「IO モナドの関数は IO モナドの関数からしか使用できない」。
    例えば以下のようなコード。
    -- Test1.hs
    import Data.Char
    
    main = do
        let s = func0
        func1 s
    
    func0 = "s"
    
    func1 s = func11 $ s ++ s
    func11 = func12 . map toUpper
    func12 = putStrLn
    
    func1 > func11 > func12 と続く連鎖のなかで、IO を使用している関数は func12 のみだが、その間で純粋な関数しか使用しない func1, func11 も連鎖して IO モナドの関数になってしまう。
    これは非常に扱いづらいように思える。どんなプログラムでも、関数の連鎖の一番先に IO 使用する場合、そこに至る全ての関数は IO モナドにしなくてはいけない。これは 「IO モナドが伝染する」 と言われたりするよう。
    上の例だと単純に書き方が下手なだけだけど、例えばネットワークプログラミングをするとしばしば利用するであろう inet_ntoa なんかは副作用なんてあるわけなさそうだが、C の関数を利用するからという理由で IO になっている。実際のプログラムを書くにあたってこの問題は避けて通れないと思う。

    2. グローバル変数?

    これも副作用を排すという Haskell の基本ルールだが、グローバル変数が使用できない。ただ、これも実際にプログラムを書くといくらでも使いたくなる。こういった性質の変数がないと、「状態」 を使用するプログラムが極端に書きづらくなる。
    以下、ユーザから入力を受け取り、その状態に基づき出力結果を変えるプログラムのサンプル。
    -- Test2.hs
    import Data.Char
    
    main = do
        s <- func0
        func1 s
        func2 s
    
    func0 = do
        putStrLn "Enter some string:"
        getLine
    
    func1 s = do
        putStrLn "Repeat plese:"
        s' <- getLine
        putStrLn $ if s == s' then "Correct"
                              else "Wrong"
    
    func2 s = do
        putStrLn "In upper cases please:"
        s' <- getLine
        putStrLn $ if (map toUpper s) == s' then "Correct"
                                            else "Wrong"
    
    当然だけれど最初に入力した文字列をその後の処理を行う関数に渡す必要がある。実際のプログラムではこの関数がさらに別の関数を呼び…と続き、引数の引き回し地獄となること必須。こんなんだったらグローバル変数使わせてくれ、と思ってしまう。

    3. モナドの実際の使い方

    では、これらの問題をどうやって解決するかということで、実際のプログラムを見てみる。私の Haskell プログラムのリファレンスはだいたい xmonad です。今回のコードは xmonad-0.11 より。
    -- Xmonad/Main.hsc
    xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
    xmonad initxmc = do
        -- snip --
        allocaXEvent $ \e ->
            runX cf st $ do
                -- snip --
    
                -- main loop, for all you HOF/recursion fans out there.
                forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
    
        -- snip --
    
    省略した部分は基本的にメインの処理に至るまでの前準備で、ポイントはメインのループ処理。ここで、ユーザからの入力やアプリケーションの状態変化を受け取り、その状態に応じて処理を行う。この関数は "X" モナドになっており、それが runX によって走らされている。X モナドの定義は以下。
    -- XMonad/Core.hs
    -- | The X monad, 'ReaderT' and 'StateT' transformers over 'IO'
    -- encapsulating the window manager configuration and state,
    -- respectively.
    --
    -- Dynamic components may be retrieved with 'get', static components
    -- with 'ask'. With newtype deriving we get readers and state monads
    -- instantiated on 'XConf' and 'XState' automatically.
    --
    newtype X a = X (ReaderT XConf (StateT XState IO) a)
        deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
    
    -- | Run the 'X' monad, given a chunk of 'X' monad code, and an initial state
    -- Return the result, and final state
    runX :: XConf -> XState -> X a -> IO (a, XState)
    runX c st (X a) = runStateT (runReaderT a c) st
    
    コメントのとおりだが、IO, State, Reader モナドをトランスフォーマーによって組み合わせたものが X モナドである。X モナドの関数の中で get / set (State) すれば XState を操作、ask (Reader) すれば XConf を参照できる。また、X モナドは MonadIO のインスタンスになっている。このことにより、X モナドの関数中では liftIO を使用することにより IO の関数を呼び出すことができ、見かけ上 IO モナドの伝染を防ぐことができる (なお、xmonad では io = liftIO としている)。

    上述の問題で悩んでいるとき、State モナドの存在は知っていたけど、メインの処理を全てモナドで包んでやるという発想が出てこなかった。これだと、グローバル変数使うのと何が違うのか? という疑問も出るが、少なくともその変数の所在が明らかになるという点でメリットはあるのではないかと思う。

    参考

    Real World Haskell Chapter 18. Monad transformers: http://book.realworldhaskell.org/read/monad-transformers.html
    本物のプログラマはHaskellを使う - 第29回 グローバル変数の代わりに使えるReaderモナドとWriterモナド:ITpro: http://itpro.nikkeibp.co.jp/article/COLUMN/20090303/325807/

    2013年4月24日水曜日

    Haskell に興味がある人向け xmonad 設定ガイド



    2016/03/12 追記: あらためて読み返してみると,Haskell に関連する記述はだいたい間違っていることがわかりました.xmonad の設定をするうえではあまり困らないことだとは思いますが,その旨ご了承ください.

    2013年4月14日日曜日

    Linux で USB DAC を使う

    いいヘッドフォンを買ったので、USB DAC にもチャレンジしてみた。
    購入前はどの DAC が Linux で使えるかよくわからなかったので、調べたことをメモメモ。
    環境は以下のとおり。
    • Linux 3.7.10-gentoo
    • ALSA
    • PulseAudio とか JACK は使ってない、よくわからない
    まずどの DAC が使用可能かは、Windows や Mac の OS 標準ドライバで動くと謳っているものは (だいたい?) Linux でも使用可能なよう。
    USB オーディオの接続方式として、大きく分けると以下の 3 つがある。
    • USB Audio Class 1
    • USB Audio Class 2
    • その他独自方式
    USB Audio Class 1, 2 が USB.org で策定されており Linux もこれらに対応しているため、普通に ALSA のドライバで使える。
    独自方式にものについては Windows や Mac にも OS 標準ドライバとして搭載できないため別途ドライバをインストールする必要があり、そうすると Linux 向けのドライバがあるものは少ないでしょう。

    カーネルコンフィグで有効にするものは以下。
    CONFIG_SND_USB_AUDIO=m
    
    これでデバイスを接続するとあっさりと認識された。
    $ dmesg
    --- snip ---
    [   53.016830] usb 3-3.7: New USB device found, idVendor=0909, idProduct=0015
    [   53.016833] usb 3-3.7: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [   53.016837] usb 3-3.7: Product: USB Headphone Amp.
    [   53.016840] usb 3-3.7: Manufacturer: audio-technica
    [   53.017051] usb 3-3.7: usb_probe_device
    [   53.017067] usb 3-3.7: configuration #1 chosen from 1 choice
    [   53.017213] usb 3-3.7: Successful Endpoint Configure command
    [   53.017402] usb 3-3.7: adding 3-3.7:1.0 (config #1, interface 0)
    [   53.017526] usbhid 3-3.7:1.0: usb_probe_interface
    [   53.017540] usbhid 3-3.7:1.0: usb_probe_interface - got id
    [   53.027020] input: audio-technica USB Headphone Amp. as /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.7/3-3.7:1.0/input/input11
    [   53.027346] hid-generic 0003:0909:0015.0005: input,hidraw4: USB HID v1.00 Device [audio-technica USB Headphone Amp.] on usb-0000:00:14.0-3.7/input0
    [   53.027421] usb 3-3.7: adding 3-3.7:1.1 (config #1, interface 1)
    [   53.030843] usb 3-3.7: adding 3-3.7:1.3 (config #1, interface 3)
    [   53.039448] snd-usb-audio 3-3.7:1.1: usb_probe_interface
    [   53.039463] snd-usb-audio 3-3.7:1.1: usb_probe_interface - got id
    [   53.049793] usb 3-3.7: Successful Endpoint Configure command
    [   53.050427] usb 3-3.7: Successful Endpoint Configure command
    [   53.050878] usb 3-3.7: Successful Endpoint Configure command
    [   53.051410] usb 3-3.7: Successful Endpoint Configure command
    [   53.052305] usbcore: registered new interface driver snd-usb-audio
    --- snip ---
    $ cat /proc/asound/cards 
     0 [PCH            ]: HDA-Intel - HDA Intel PCH
                          HDA Intel PCH at 0xa0610000 irq 47
     1 [Amp            ]: USB-Audio - USB Headphone Amp.
                          audio-technica USB Headphone Amp. at usb-0000:00:14.0-3.7, full speed
    $ aplay -L
    --- snip ---
    sysdefault:CARD=Amp
        USB Headphone Amp., USB Audio
        Default Audio Device
    front:CARD=Amp,DEV=0
        USB Headphone Amp., USB Audio
        Front speakers
    surround40:CARD=Amp,DEV=0
        USB Headphone Amp., USB Audio
        4.0 Surround output to Front and Rear speakers
    --- snip ---
    

    実際に音を出すには、ALSA のデフォルトの出力先か、アプリケーション単位で出力先デバイスを設定する必要がある。前者は ~/.asoundrc に設定すれば良い。
    # ~/.asoundrc
    pcm.!default sysdefault:Amp
    
    この設定はアプリケーション起動時のものが反映されるようなので、ノート PC を使用している時など接続変更が発生する時でも、接続後に設定、アプリーケション起動としてやれば良い。

    USB DAC をつないで音が良くなったか? というのは別問題。耳が追いついていない。

    参考

    第1回 USB-DACといっても必ずしも同じではない | Gaudio+PCオーディオfan: http://www.pc-audio-fan.com/special/usb-dac-research/20101115_1438/
    USB.org - Approved Device Class Document Download: http://www.usb.org/developers/devclass_docs
    What's Best Forum - USB audio: http://www.whatsbestforum.com/showthread.php?2932-USB-audio
    .asoundrc - ALSA wiki: http://alsa.opensrc.org/.asoundrc

    2013年4月12日金曜日

    QuickCheck を使う

    Real World Haskell の Chapter 11 に QuickCheck の使い方が載っていて、これは素晴らしいと思ったのだけれど、一部記載が古いバージョン (QuickCheck 1) に関するものであり現行バージョン (QuickCheck 2) でそのまま使えない箇所があったため、補完的な意味で書いてみる。
    動作環境は以下のとおり。

    • ghc-7.4.2
    • QuickCheck-2.4.2

    1. 概要

    QuickCheck はユニットテストを行うための仕組みで、テストにランダムな値を生成して与えることができる。具体的な値を決めてテストを実施するわけではないので、テスト通過可否はその性質によって判断する。
    以下はクイックソートの例。クイックソート本体 (qsort) の他に、qsort の動作を確認するための関数 prop_sorted を記述している。これは qsort 後のリストが昇順にソートされているかを確認する。
    -- file: QuickSort.hs
    import Data.List
    import Test.QuickCheck
    
    qsort :: Ord a => [a] -> [a]
    qsort []     = []
    qsort (x:xs) = qsort lhs ++ [x] ++ qsort rhs
        where lhs = filter  (< x) xs
              rhs = filter (>= x) xs
    
    prop_sorted :: Ord a => [a] -> Bool
    prop_sorted xs = sorted $ qsort xs
        where sorted (x:xs@(x':_)) = x <= x' && sorted xs
              sorted _             = True
    GHCi 上でテスト実施。prop_sorted に 100 通りの値を与えて qsort の動作を確認している。
    ghci> :l QuickSort.hs
    [1 of 1] Compiling Main             ( qsort.hs, interpreted )
    Ok, modules loaded: Main.
    ghci> quickCheck (prop_sorted :: [Integer] -> Bool)
    +++ OK, passed 100 tests.
    具体的には、こんな値を与えている (verboseCheck で確認可能)。
    ghci> verboseCheck (prop_sorted :: [Integer] -> Bool)
    Passed:  
    []
    Passed: 
    []
    Passed:  
    [0,-2]
    Passed:  
    [0,3,0]
    Passed:  
    [-3,-2,-3,4]
    --- snip ---
    Passed:   
    [-29,68,-24,-85,-7,-76,27,34,65,-31,-62,-27,65,-53,76,-82,24,97,-43,-70,-67,58,
    56,-95,-48,18,26,-91,97,-56,33,10,-57,29,-61,-60,-23,-87,77,-9,-30,21,-89,-32,
    -42,-73,21,60,65,-9,-76]
    Passed:   
    [97,-9,-27,42,-30,49,-97,46,-48,11,-87,-47,-36,45,-55,-25,2,-58,-43,82,3,81,97,
    -28,-84,75,-13,42,-50,0,7,54,46,-13,4,73,-35,-32,-77,-37,-42,81,-3]
    Passed:   
    [-78,85,3,18,-67,94,99,-17,-66,94,-1,-83,-96,-68,-31,72,-79,81,47,24,-78,20,-87,
    -56,38,65,-69,40,-54,-53,42,96,15,62,55,-10,-89,-83,67,36,-85,-97,71,-28,-3,77,
    84,-7,57,-55,11,84,-85,-89]
    +++ OK, passed 100 tests.
    
    ランダムテストの良いところは、人間が思いもよらない値をテストに与えることができること、具体的な値を定義する手間が省けること。そもそもコードとテストを書く人間が同じ場合、テストパターンとして思い浮かぶ時点で、コードにもその考慮がされていることが多いんじゃないでしょうか。

    2. 値の生成

    値の生成は System.Random で乱数を発生させて、それに基づき Arbitrary クラスで定義された arbitrary 関数で当該型の値を生成するという流れになる。なお、Gen モジュールでランダムな値を生成するための便利な関数が用意されているため、arbitrary 関数で直接 System.Random の乱数を扱う必要は多分ほとんどない。以下がキーとなる (と RWH に書かれていた) 関数。
    -- module: Test.QuickCheck.Gen
    elements :: [a] -> Gen a
    choose :: Random a => (a, a) -> Gen a
    oneof :: [Gen a] -> Gen a
    
    なお、Gen は state-passing monad ということだがモナドはまだよく理解できていない。とにかく、実際に GHCi 上で値を取り出して挙動を確認するためには sample 関数を使用する。
    -- module: Test.QuickCheck.Gen
    sample :: Show a => Gen a -> IO ()
    
    GHCi 上でテスト。
    ghci> :m +Test.QuickCheck
    ghci> sample $ elements [0,1,2,3,5,8]
    8
    3
    1
    8
    0
    5
    5
    2
    3
    2
    8
    ghci> sample $ choose ('a','z')
    'h'
    'f'
    'g'
    'q'
    'c'
    'i'
    'a'
    'a'
    'a'
    'r'
    'g'
    ghci> sample $ oneof [choose (0,10), choose (100,110), choose (200,210)]
    207
    0
    9
    207
    105
    8
    106
    202
    8
    202
    110
    
    これらを使用して、必要な型の乱数値を生成する。例えば、あらかじめ実装されている Char は以下のようになっている。
    -- module: Test.QuickCheck.Arbitrary
    instance Arbitrary Char where
      arbitrary = chr `fmap` oneof [choose (0,127), choose (0,255)]
    
    これを使用して独自の型の乱数値を生成する例。良い例が思い浮かばなかったので Real World Haskell の例そのまま。
    -- Doc.hs
    import Test.QuickCheck  
    import Control.Monad (liftM, liftM2)  
      
    data Doc = Empty  
             | Char Char  
             | Text String  
             | Line  
             | Concat Doc Doc  
             | Union Doc Doc  
             deriving (Show)  
      
    instance Arbitrary Doc where  
        arbitrary =  
            oneof [ return Empty  
                  , liftM Char arbitrary  
                  , liftM Text arbitrary  
                  , return Line  
                  , liftM2 Concat arbitrary arbitrary  
                  , liftM2 Union arbitrary arbitrary ]  
    
    arbitrary は型推論され、関数に必要な型の乱数値を与えている。Text には String を、Concat には再帰して Doc の値を与える。
    ghci> :t arbitrary
    arbitrary :: (Arbitrary a) => Gen a
    ghci> :m +Control.Monad
    ghci> :t liftM Text
    liftM Text :: (Monad m) => m String -> m Doc
    ghci> :t liftM2 Concat
    liftM2 Concat :: (Monad m) => m Doc -> m Doc -> m Doc
    
    また、arbitrary 関数中の oneof .. の部分は以下のように書くこともできるが、oneof ... のほうがすっきりしてわかりやすい。
    instance Arbitrary Doc where
        arbitrary = do
            n <- choose (1, 6) :: Gen Int
            case n of
                 1 -> return Empty
    
                 2 -> do x <- arbitrary
                         return (Char x)
    --- snip ---
    
    sample 関数で生成を確認する。
    ghci> :l Doc.hs
    [1 of 1] Compiling Main             ( Doc.hs, interpreted )
    Ok, modules loaded: Main.
    ghci> sample (arbitrary :: Gen Doc)  
    Char 'r'  
    Line  
    Union (Text "Ce") (Char '\194')  
    Text "\179lj(\169K"  
    Text "\233y\189d"  
    Union (Union (Char 'K') (Concat (Concat Empty (Char 'c')) Empty)) (Char 'A')  
    Text "\213V\236s\ESC\167\NUL\224\ENQ4u"  
    Union (Union Empty (Char '\219')) (Char '\138')  
    Line  
    Union Line (Concat Empty (Concat (Char 'w') Line))  
    Char '}'  
    

    3. テストをまとめて実行する

    テストの作り方は上に示したが、テストを複数作ってそれをひとつずつ GHCi で実行していくわけにはもちろんいかないので、まとめる。関数の中にひとつずつ quickCheck prop_hoge と記述していっても良いが、全部のテストをまとめて行うための仕組みも用意されている。{-# LANGUAGE TemplateHaskell #-} したうえで $quickCheckAll を実行するだけ。
    -- TestSuite.hs
    {-# LANGUAGE TemplateHaskell #-}
    
    import Test.QuickCheck
    import Test.QuickCheck.All
    
    prop_plus1 :: Integer -> Bool
    prop_plus1 x = x + 1 > x
    
    prop_abs :: Integer -> Bool
    prop_abs x = abs x >= 0
    
    prop_square :: Integer -> Bool
    prop_square x = x ^ 2 > x
    
    main = $quickCheckAll
    
    これで全てのテストが実行される。prop_square は失敗するテストの例。
    $ runghc TestSuite.hs 
    === prop_plus1 on TestSuite.hs:6 ===
    +++ OK, passed 100 tests.
    === prop_abs on TestSuite.hs:9 ===
    +++ OK, passed 100 tests.
    === prop_square on TestSuite.hs:12 ===
    *** Failed! Falsifiable (after 1 test):  
    0
    False
    
    一番大事なことは、テストを書くことをめんどくさがらないことですかね。

    参考

    Real World Haskell Chapter 11. Testing and quality assurance: http://book.realworldhaskell.org/read/testing-and-quality-assurance.html
    Introduction to QuickCheck2: http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck2