Answering my own question. I found the answers on the libuv mailing list here: https://groups.google.com/forum/#!topic/libuv/fRNQV_QGgaA
Copying the details here if the link becomes unavailable:
Attaching your own data structure to a handle:
The handle has a void* data
field that is yours to use. You can
make it point it to an auxiliary structure where you store the length
and the buffer.
Alternatively, you can embed the uv_tcp_t in another structure, then look up the embedding structure with container_of. It's not a standard C macro but you can find its definition and usage examples in the libuv/ source tree. Its benefit is that it just does some pointer arithmetic, it saves you from another level of pointer indirection.
Stack allocation for the receiving buffer:
No, that's not possible. The proper way of thinking about it is that your alloc_cb returns a buffer that libuv will fill with data sometime in the future. The stress is on "sometime" because there are no guarantees when that will happen; it may be immediate, it may be seconds (or minutes) away.