The suggestion to avoid weak references does not solve the race issue.
T1 operator new, create object, references: 1
T1 passes interface object reference to T2, thinking it can "share" ownership
T1 suspends
T2 resumes
T2 QueryInterface
T2 suspends before InterlockedIncrement, references: 1
T1 resumes
T1 Calls Release
T1 suspends between InterlockedDecrement and operator delete, references: 0
T2 resumes, InterlockedIncrement occurs, references 1
T2 suspends
T1 resumes, operator delete executes, references 1 !!!
T1 suspends
T2 resumes
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1.
This is solvable in the COM server. The COM client, however, should not depend upon the server preventing this race condition. Therefore, COM clients MUST NOT share interface objects between threads. The only thread which should be allowed to access the interface object, is the ONE thread which currently "owns" the interface object.
T1 should NOT have called Release. Yes, it could have called AddRef prior to passing the interface object to T2. But that may not solve the race, only move it someplace else. The best practice is to always maintain the concept of one-interface-object, one-owner.
If the COM server wishes to support the concept that two (or more) interfaces can reference some shared server-internal state, the COM server should advertise the contract by supplying a CreateCopyOfInstance method and manage contention, internally. There are, of course, examples of servers which handle this sort of "fan-out". Take a look at the persistent storage interfaces from Microsoft. There, the interfaces do NOT "fan-out" .. each interface should be owned by a single user (thread/process/whatever) and the "fan-out" is managed, internally, by the server, with methods provided to the COM clients to control some facets of the contention issues. Microsoft's COM servers must, therefore, address the race conditions as part of their contracts to their clients.