Software Contracts, Part 9: Annotations Outside the Compiler - More Runtime Enforced Annotations
Ok, if it's not become crystal clear that I'm writing this on-the-cuff, this post will finally put a nail in the coffin.
My last post discussed runtime enforced annotations and I used the RPC runtime library as an example of a runtime which enforces contractual annotations.
Of course I totally ignored the single most common example of a runtime enforced contractual annotation in favor of a relatively obscure (but near to my heart) example.
Of course, the most common example of a runtime enforced annotation is our old friend ASSERT (and it's brethren). Assertions are typically used to ensure that invariants are in fact maintained, and a function's contract is always invariant. Thus assertions can (and IMHO should) be used to enforce contracts.
Remember my "what's wrong with this code" from yesterday?
:HRESULT DoSomething(handle_t BindingHandle, const wchar_t *StringParam, int *ReturnedValue){ if (StringParam == NULL || ReturnedValue == NULL) { return E_POINTER; } <DoSomething>}:
I asserted that the error check was incorrect because the RPC contract ensured that the StringParam and ReturnedValue parameters wouldn't be null. Norman Diamond called me on it because it was possible that the function would be called incorrectly from inside the module. Ultimately, he's right, there SHOULD be a check. But it doesn't deserve to be a full-on check which returns an error, so the check ends up being just dead code.
Dead code is bad for two reasons:
First off, dead code can conceivably be used as an attack vehicle - because it's never been executed, it's never been tested, and a attacker might be able to find a way to use that dead code to exploit some vulnerability.
But more importantly, dead code increases the number of code paths through your system. One way of measuring the effectiveness of your test strategy is to measure the number of code paths that are executed by your test suites - in theory, the more code paths executed by the tests, the higher the quality of the tests. But if you have dead code (which thus cannot be executed by the tests), the dead code reduces the code coverage numbers for your tests, leading you to believe that (on this metric) your tests are worse than they actually are.
It's far better to enforce the contract for a situation like this with an assertion.
:
HRESULT DoSomething(handle_t BindingHandle, const wchar_t *StringParam, int *ReturnedValue)
{
_ASSERT(StringParam != NULL && ReturnedValue != NULL);
<DoSomething>
}
:
This is a far better implementation - it uses a runtime assertion to enforce the contract, but it doesn't increase the number of code paths.
One of the interesting aspects of assertions as a mechanism for runtime contract enforcement is that they normally aren't checked in all cases. Instead the assertion is usually present only on debug builds. Thus assertions don't affect the performance of (and number of code branches in) retail builds.
Next: Other kinds of annotations used to allow for contract enforcement.
Comments
Anonymous
January 26, 2007
Assert that disappears in non-debug build is one of those idiotic ideas that seem nice at the first glance. What is the point of removing the check from the actual software your customers are going to use?? Do you have a miraculous way of testing every possibility in debug build? If you don't, use an assertion facility that is always there. Now I am sure somebody will mention "performance". Which of course doesn't matter in 99.9% of cases where you need to assert.Anonymous
January 27, 2007
The comment has been removedAnonymous
January 27, 2007
If you're worried about dead code being used as an attack vector, wouldn't code with no sanity checks be even worse?Anonymous
January 27, 2007
The comment has been removedAnonymous
January 28, 2007
The comment has been removedAnonymous
January 28, 2007
josh, Not really - code with no sanity checks where every code path has been executed is likely to be more correct than code with lots of checks that has never been executed.Anonymous
January 28, 2007
The comment has been removedAnonymous
January 29, 2007
The comment has been removedAnonymous
February 04, 2007
The comment has been removed