haskell/security-advisories

draft: discover and identify a vulnerabilty

aleeusgr opened this issue · 4 comments

Hello!
I am solving Advent of Code and I came across an effect I never encountered before in Haskell: my code runs, but it makes the computer hang. It's a bit hard to reproduce, since you need to get personalized inputs file from AoC, but I have one I can share.

  • provide list of inputs
  • document the program
  • ID the package and the version I am using.
  • ID cvss

The changes that I made debugging the function at the line 42, on my list of inputs it reports an error that I am trying to call head on an empty list (or a singleton list?)

Code example is in the branch but again I just realized it won't run without the inputs.
https://github.com/aleeusgr/aoc23/tree/haskell-avisory-report

Mandatory information:

  • Package : ?
  • cvss: ?
  • affected versions: ?

Optional:

  • cve: ?
  • keywords: ?
  • aliases: ?
  • related: ?
  • affected OSes: ?
  • affected architecture: ?
  • declarations: ?
  • Long description: ?

edit realised I was building the wrong branch ^_^

Could you attach an input that triggers the condition.

I wonder if it would be a space leak that eats all your system memory and causes issues? Anyway, I would be happy to help identify the issue.

I tried it on my own input and found a problematic input that hangs the program:

85dntjeightwom

I can see the bug. I think you will be able to find it too, if you examine the input above. Let me know if you get stuck.

Thanks!
It will take me some time, it's the first opportunity I see to use property testing to find the bug. I want to learn QuickCheck and I want to find the bug probably the same way that you did. Thanks for the inspiration 🙏

This issue traces back to ghc and seems to be not a security issue.

Details

I used ghci debugger to realize that one of the lower level functions breaks on empty string.
Which pointed me to the buggy implementation:

wordContainsDigits word = or $ map (`isSubsequenceOf` word) ["one", ...]

So I used QuickCheck to find small inputs that break the high level function:

QC.testProperty "correctCalibrationValues returns ..." $
\x -> length (MyLib.correctCalibrationValues x :: [Char] )  <= length x

Turns out:

ghci> "two" `isSubsequenceOf` "twao"
True

This allowed incorrect inputs to be processed by the function, which ran out of items to check and was looping infinitely by adding an empty string to the incorrect input, which passed the check and higher level and got passed down again to the parser.