The runtime knows exactly where GC pointers are so I would assume that is what it does. It even knows precisely when locals are no longer needed so it can stop treating objects they refer to as reachable. It's instruction level, not based on scopes, so an object can be freed while it is still in scope if the code doesn't access it!
> How does C# guard against someone doing something silly like turning a pointer into a long and then back into a pointer again later?
I don't think it does. You can't do most of these things without using unsafe code, which needs a compiler flag enabled and code regions marked as `unsafe`.