Skip to content

Commit f83fc50

Browse files
zh-jqcursoragent
andcommitted
Avoid zeroing I/O buffers that are filled before read
Allocate recv/copy buffers with uninitialized capacity instead of vec![0]/BytesMut::zeroed, since only the filled prefix is ever observed. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ea6595c commit f83fc50

18 files changed

Lines changed: 79 additions & 22 deletions

File tree

lib/vey-cert-agent/src/query.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ impl QueryRuntime {
3939
QueryRuntime {
4040
socket,
4141
query_handle,
42-
read_buffer: vec![0u8; 16384].into_boxed_slice(),
42+
// SAFETY: only `read_buffer[..len]` is parsed after poll_recv fills it.
43+
read_buffer: unsafe { Box::<[u8]>::new_uninit_slice(16384).assume_init() },
4344
write_queue: VecDeque::new(),
4445
protective_ttl: config.protective_cache_ttl,
4546
maximum_ttl: config.maximum_cache_ttl,

lib/vey-daemon/src/listen/udp/listen.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,11 @@ where
494494
ingress_net_filter: None,
495495
_alive_guard: None,
496496

497-
packet_buf: vec![0; self.packet_max_size as usize],
497+
// SAFETY: only `packet_buf[..nr]` is copied after recv fills it.
498+
packet_buf: {
499+
let n = self.packet_max_size as usize;
500+
Vec::from(unsafe { Box::<[u8]>::new_uninit_slice(n).assume_init() })
501+
},
498502
}
499503
}
500504

@@ -518,7 +522,11 @@ where
518522

519523
let mut packets_buf = Vec::with_capacity(self.conn_track.batch_recv_size());
520524
for _i in 0..self.conn_track.batch_recv_size() {
521-
packets_buf.push(vec![0; self.packet_max_size as usize]);
525+
// SAFETY: only `packets_buf[i][..nr]` is copied after recv fills it.
526+
let n = self.packet_max_size as usize;
527+
packets_buf.push(Vec::from(unsafe {
528+
Box::<[u8]>::new_uninit_slice(n).assume_init()
529+
}));
522530
}
523531

524532
ListenUdpRuntimeInstance {

lib/vey-h2/src/body/encoder.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ impl H2BodyEncodeTransferInternal {
107107
}
108108
}
109109
} else {
110-
let mut data = BytesMut::zeroed(self.buffer_size);
110+
let mut data = BytesMut::with_capacity(self.buffer_size);
111+
// SAFETY: only `filled()` / `split_to(nr)` is used after poll_read.
112+
unsafe { data.set_len(self.buffer_size) };
111113
let mut buf = ReadBuf::new(&mut data);
112114
ready!(reader.as_mut().poll_read(cx, &mut buf))
113115
.map_err(H2StreamBodyEncodeTransferError::ReadError)?;

lib/vey-hickory-client/src/io/quic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ async fn quic_recv(mut recv_stream: RecvStream) -> Result<DnsResponse, NetError>
144144
.map_err(|e| format!("quic read len error: {e}"))?;
145145
let message_len = u16::from_be_bytes(len_buf) as usize;
146146

147-
let mut buffer = vec![0u8; message_len];
147+
// SAFETY: `read_exact` fully initializes `buffer` before it is parsed.
148+
let mut buffer = Vec::from(unsafe {
149+
Box::<[u8]>::new_uninit_slice(message_len).assume_init()
150+
});
148151
recv_stream
149152
.read_exact(&mut buffer)
150153
.await

lib/vey-hickory-client/src/io/udp.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,13 @@ async fn udp_send_recv(
104104
}
105105

106106
loop {
107-
let mut recv_buf = vec![0; MAX_RECEIVE_BUFFER_SIZE];
107+
// SAFETY: only `[..nr]` is kept after recv fills it.
108+
let mut recv_buf = Vec::from(unsafe {
109+
Box::<[u8]>::new_uninit_slice(MAX_RECEIVE_BUFFER_SIZE).assume_init()
110+
});
108111

109112
let nr = socket.recv(&mut recv_buf).await?;
110-
recv_buf.resize(nr, 0);
113+
recv_buf.truncate(nr);
111114
let response = DnsResponse::from_buffer(recv_buf)?;
112115
if response.id() != id {
113116
continue;

lib/vey-io-ext/src/stream/copy.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ impl StreamCopyBuffer {
7676
fn new(config: &StreamCopyConfig) -> Self {
7777
StreamCopyBuffer {
7878
read_done: false,
79-
buf: vec![0; config.buffer_size].into_boxed_slice(),
79+
// SAFETY: only `buf[w_off..r_off]` is read after poll_read fills it.
80+
buf: unsafe { Box::<[u8]>::new_uninit_slice(config.buffer_size).assume_init() },
8081
yield_size: config.yield_size,
8182
r_off: 0,
8283
w_off: 0,
@@ -435,3 +436,23 @@ where
435436
.poll_copy(cx, Pin::new(&mut me.reader), Pin::new(&mut *me.writer))
436437
}
437438
}
439+
440+
#[cfg(test)]
441+
mod tests {
442+
use super::*;
443+
444+
#[test]
445+
fn with_data_handles_spare_capacity_below_buffer_size() {
446+
// len << capacity < buffer_size used to panic: reserve_exact is based on len.
447+
let mut data = Vec::with_capacity(64);
448+
data.extend_from_slice(b"hello");
449+
assert!(data.len() < data.capacity());
450+
assert!(data.capacity() < MINIMAL_COPY_BUFFER_SIZE);
451+
452+
let mut config = StreamCopyConfig::default();
453+
config.set_buffer_size(MINIMAL_COPY_BUFFER_SIZE);
454+
let buf = StreamCopyBuffer::with_data(&config, data);
455+
assert_eq!(&buf.buf[..buf.r_off], b"hello");
456+
assert_eq!(buf.buf.len(), MINIMAL_COPY_BUFFER_SIZE);
457+
}
458+
}

lib/vey-io-ext/src/stream/line_recv_vec.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ impl LineRecvVec {
2222
length: 0,
2323
line_start: 0,
2424
line_end: 0,
25-
buf: vec![0; cap].into_boxed_slice(),
25+
// SAFETY: only `buf[line_start..line_end]` / `[0..length]` are read after fills.
26+
buf: unsafe { Box::<[u8]>::new_uninit_slice(cap).assume_init() },
2627
}
2728
}
2829

lib/vey-io-ext/src/udp/relay/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ impl UdpRelayPacket {
3232
fn new(reserved_size: usize, packet_size: u16) -> Self {
3333
let buf_size = packet_size as usize + reserved_size;
3434
UdpRelayPacket {
35-
buf: vec![0; buf_size].into_boxed_slice(),
35+
// SAFETY: only `buf[off..end]` is read after recv fills that range.
36+
buf: unsafe { Box::<[u8]>::new_uninit_slice(buf_size).assume_init() },
3637
buf_data_off: 0,
3738
buf_data_end: 0,
3839
ups: UpstreamAddr::empty(),

lib/vey-io-ext/src/udp/stream_copy/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ pub struct UdpCopyPacket {
3737
impl UdpCopyPacket {
3838
pub(crate) fn new(reserved_size: usize, packet_size: u16) -> Self {
3939
let buf_size = packet_size as usize + reserved_size;
40+
let mut buf = BytesMut::with_capacity(buf_size);
41+
// SAFETY: only `buf[off..end]` is read after recv fills that range.
42+
unsafe { buf.set_len(buf_size) };
4043
UdpCopyPacket {
41-
buf: BytesMut::zeroed(buf_size),
44+
buf,
4245
buf_data_off: 0,
4346
buf_data_end: 0,
4447
}

lib/vey-ip-locate/src/query.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ impl IpLocationQueryRuntime {
3838
IpLocationQueryRuntime {
3939
socket,
4040
query_handle,
41-
read_buffer: vec![0u8; 16384].into_boxed_slice(),
41+
// SAFETY: only `read_buffer[..len]` is parsed after poll_recv fills it.
42+
read_buffer: unsafe { Box::<[u8]>::new_uninit_slice(16384).assume_init() },
4243
write_queue: VecDeque::new(),
4344
default_expire_ttl: config.default_expire_ttl,
4445
maximum_expire_ttl: config.maximum_expire_ttl,

0 commit comments

Comments
 (0)