I've wound up just putting the protocol state in a struct and making the "conforming" action to have that struct in the conforming object with a standardized field name. Then just use a macro to get the protocol pointer and pass it to the protocol's implementation functions.
But I really, really wish we could have a lightweight protocol/trait feature in C. It would remove a large source of unsafe code that has to cast back and forth between void *.