How to avoid buffer overflow on asynchronous non-blocking WSASend calls
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
With asynchronous WSASend, the biggest risk is usually not a classic stack smash. It is sending from memory that gets reused, freed, or overwritten before the overlapped send actually completes. In other words, the safe design is about buffer ownership, queue limits, and completion handling.
What WSASend Guarantees and What It Does Not
When you call WSASend with overlapped I/O, the function may return before the data is physically transmitted. That means the memory referenced by your WSABUF must remain valid until the send completes.
This is the core rule:
- do not modify the send buffer until completion
- do not free the send buffer until completion
- do not reuse the
OVERLAPPEDstructure until completion
If you violate that rule, the socket layer may read corrupted or unrelated bytes later.
The Real Fix: Own the Buffer Per Pending Send
A common safe pattern is to allocate one send object per pending operation.
Here the std::vector<char> owns the bytes. As long as the PendingSend object stays alive, the buffer stays valid.
Queue Outbound Data Instead of Sending From Temporary Memory
In non-blocking code, it is tempting to do this:
That is unsafe if tmp goes out of scope or gets reused before completion.
A better approach is a per-connection send queue.
Push each outbound message into the queue and pop it only after completion is reported.
Handle Partial Sends and Completion Correctly
Even asynchronous APIs can complete with fewer bytes than expected in some designs. Your completion path must inspect the byte count and decide whether another send is needed for the remaining bytes.
That means your send object should track progress, not just raw storage.
Limit Queue Growth
Avoiding corruption is only half the story. If the peer is slow and you keep enqueuing data forever, memory usage can explode.
Use backpressure:
- cap the number of pending bytes per connection
- stop reading or producing data when the queue is too large
- disconnect misbehaving peers if necessary
That is how you avoid a memory exhaustion bug that feels like a buffer problem at runtime.
Example Send Submission
The important part is that ps must stay alive after this call if completion is pending.
Completion Ports Make Ownership Easier
If you use I/O completion ports, keep a pointer to the send context and release it only after the completion callback or worker thread processes the finished send.
That gives you a clean ownership lifecycle:
- allocate send context
- submit
WSASend - wait for completion
- release send context
Common Pitfalls
A common mistake is passing a pointer to stack memory or to a mutable shared buffer and then reusing it immediately after calling WSASend.
Another mistake is reusing the same OVERLAPPED structure for multiple in-flight sends on the same socket.
Developers also often forget backpressure. Even if every buffer is technically safe, an unbounded queue can still exhaust memory under load.
Summary
- The send buffer for overlapped
WSASendmust remain valid until completion. - Give each pending send its own owned buffer and
OVERLAPPEDstate. - Use a per-connection send queue instead of temporary memory.
- Apply backpressure so slow peers cannot grow memory without limit.
- Think in terms of buffer ownership and completion lifecycle, not just API syntax.
Related reading
- How to avoid installing Unlimited Strength JCE policy files when deploying an application?
- How to avoid reverse engineering of an APK file
- How to avoid reverse engineering of an APK file
- How to change password of AWS Cognito User?
- How to avoid long nesting of asynchronous functions in Node.js
- How to avoid MySQL 'Deadlock found when trying to get lock; try restarting transaction
- How to change User Status FORCE_CHANGE_PASSWORD?
- How to check certificate name and alias in keystore files?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.