Holy cow, I wrote a book!
There are some basic ground rules that apply to all system programming, so obvious that most documentation does not bother explaining them because these rules should have been internalized by practitioners of the art to the point where they need not be expressed. In the same way that when plotting driving directions you wouldn't even consider taking a shortcut through somebody's backyard or going the wrong way down a one-way street, and in the same way that an experienced chess player doesn't even consider illegal moves when deciding what to do next, an experienced programmer doesn't even consider violating the following basic rules without explicit permission in the documentation to the contrary:
NULL
CRITICAL_SECTION
this
IUnknown::AddRef
(Remember, every statement here is a basic ground rule, not an absolute inescapable fact. Assume every sentence here is prefaced with "In the absence of indications to the contrary". If the caller and callee have agreed on an exception to the rule, then that exception applies. For example, a pointer is prototyped as volatile is explicitly marked as "This value can change from another thread," so the rule against modifying function parameters does not apply to such a pointer.)
volatile
Coming up with this was hard, in the same way it's hard to come up with a list of illegal chess moves. The rules are so automatic that they aren't really rules so much as things that simply are and it would be crazy even to consider otherwise. As a result, I'm sure there are other "rules so obvious they need not be said" that are missing. (For example, "You cannot terminate a thread while it is inside somebody else's function.")
One handy rule of thumb for what you can do to a function call is to ask, "How would I like it if somebody did that to me?" (This is a special case of the "Imagine if this were possible" test.)