belay icon indicating copy to clipboard operation
belay copied to clipboard

Guard against infinite expectation loops, especially in global handlers

Open ngsilverman opened this issue 5 years ago • 4 comments

Using expectations instead of handlers, especially the global one, could cause infinite loops when they fail.

expect.onGlobalFail = object : GlobalExpectationHandler() {
    override fun handleFail(exception: ExpectationException) {
        expect.fail("this will trigger an infinite loop as soon as an expectation fails")
    }
}

This is easy to avoid when the global handler logic is simple, which I expect to be the majority of cases, but it could be done inadvertently for more complex handlers who make various function calls, for example to disable parts of the application.

Since the purpose of the library is in part to be able to recover critical failures, it would be unfortunate to cause one. Ideally we would detect loops that are likely infinite and end them before a stack overflow. The approach could perhaps consist of detecting if multiple calls to handleFail are occurring without any of the calls returning. By inspecting the ExpectationException one could make an educated guess as to whether the same expectation is failing in a loop.

ngsilverman avatar Nov 20 '20 16:11 ngsilverman

Detecting the loop based on the stack trace sounds like a good idea 👍 It seems like you could decompose the stack trace into its individual frames, then do a simple check for cycles of frames that include the global expectation handler frame.

indragiek avatar Nov 20 '20 19:11 indragiek

Yeah, checking the stack trace itself is a good idea but only works if the calls are made from the same thread. Granted, I think that's pretty likely, but it's not a perfect solution. 😓

ngsilverman avatar Nov 23 '20 14:11 ngsilverman

Howdy my good ole folks @ngsilverman @indragiek! Just chiming in, with a question and perhaps a remedy. Can we perhaps check for program counters and/or function pointers to detect loops? If Kotlin doesn’t provide these, then no biggie, please discard, but if it does, usually the runtime arranges for them to be unique uintptr/uint64 values that could be checked for, (obviously after unfurling inclined calls)

odeke-em avatar Nov 23 '20 15:11 odeke-em

Hey @odeke-em! Thanks for chiming in. The JVM Tool Interface does appear to provide access to executable locations of methods, but that's not an API meant to be used by a library like this one. For practical purposes this doesn't seem possible for Kotlin (or Java). Function pointers are not available at all as far as I can tell. 🤷‍♂️

ngsilverman avatar Nov 23 '20 16:11 ngsilverman