SAL annotations are pretty great; just a couple weeks ago they found a codepath where I’d forgotten to acquire a spinlock. There aren’t many hard rules for how to use them; you’re naturally allowed to adapt any guidelines to fit your needs. E.g., a smaller project might not have much locking, and so might not need to trot out all the fancy lock annotations.
In general (and in particular for NDIS filter drivers – my own area), when there’s a standard callback that you have to implement, the OS header should have the IRQL annotation on the function prototype that’s in the WDK header file. For example, FILTER_ATTACH is a WDK-provided typedef with an accompanying annotation:
typedef
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(FILTER_ATTACH)
NDIS_STATUS
(FILTER_ATTACH)(
_In_ NDIS_HANDLE NdisFilterHandle,
_In_ NDIS_HANDLE FilterDriverContext,
_In_ PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters
);
When you implement this, I suggest (guideline!) that you use only _Use_decl_annotation_
on the implementation, so that you don’t have to update your code when you update the WDK. Example:
FILTER_ATTACH MyFilterAttach;
_Use_decl_annotations_
NDIS_STATUS
MyFilterAttach(
NDIS_HANDLE NdisFilterHandle,
NDIS_HANDLE FilterDriverContext,
PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters)
{ . . . }
While it’s totally legal to copy all the annotations from the header, you’re signing yourself up for keeping those up-to-date if the header ever changes. Note that some older WDK function prototypes don’t have SAL annotations. In that case, you do gotta write them yourself.
In contrast, when you control both the header and the source file (this likely will be most of the functions in your driver), I tend to choose to avoid _Use_decl_annotations_
and instead duplicate the annotations on both the .H and .C files. Duplication is bad (DRY etc), but I don’t mind this one since it serves as good documentation.
A common error: if you peek at the definition of PAGED_CODE()
, you’ll see that it’s a macro that expands out to an assert on the IRQL. However, it is not the same thing as NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL)
. The difference is that the PAGED_CODE()
macro asserts that this code can be paged out, while the latter only asserts something about the IRQL. To see an example where this makes a difference, consider this function:
_IRQL_requires_(PASSIVE_LEVEL)
void F()
{
PAGED_CODE(); // wrong!
if (RtlEqualUnicodeString(. . .)) { // requires passive level
KIRQL oldIrql;
KeAcquireSpinLock(&my_lock, &oldIrql); // raises to dispatch level
. . .
}
You must enter the function at PASSIVE_LEVEL, so you can access the string comparison routine. But the whole function cannot be pageable, because it does sometimes raise the
IRQL when grabbing the spinlock. So it’s wrong to mark the function as PAGED_CODE()
, because this code cannot be paged. The developer really meant to say NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL)
instead.
Another common error: annotations are about contracts, not implementations. Just because a function currently could run at DIRQL doesn’t mean that you should annotate it as such. Eventually you’ll develop a “feel” for what sort of code goes into which IRQL. E.g., for NDIS, the datapath runs <=DISPATCH_LEVEL, but controlpath mostly runs at PASSIVE_LEVEL. So when you go to annotate your own functions, think about which IRQLs this code ever deserves to run at, not what its current implementation could do. If you are conservative in what you annotate for, that gives you more latitude to later change the implementation without breaking the contract.
Microsoft does not have any rules for driver code style. Use whatever style makes you most productive and your colleagues most happy. There’s no single style used uniformly by all 60 hojillion lines of code in Windows, so you can’t say “I just want to use Windows style”. You can use CNF if you like, although CNF tends to fall apart with C++ code, and it has a few quirks that are typically considered antipatterns for modern code (e.g., declaring all variables at the top of the functions). But really, there’s enough people clamoring to tell you how to style your code that you don’t need Microsoft to join into the fray too.