You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When creating a new, empty DemiBuffer via DemiBuffer::new, the constructor initializes data_len and pkt_len to buf_len (all fields of MetaData). This area has a few related issues:
The Deref implementation creates a slice containing all DemiBuffer bytes in the range [0, data_len). Dereferencing a new DemiBuffer is undefined behavior, as it violates the safety requirements of slice::from_raw_parts, namely (emphasis added):
data must point to len consecutive properly initialized values of type T.
and from the GlobalAlloc::alloc documentation:
The allocated block of memory may or may not be initialized.
This behavior is inconsistent with DPDK. The closest equivalent function is probably rte_pktmbuf_alloc, which initializes buf_len depending on the memory pool but data_len and pkt_len are initially zero. The expected workflow here is to allocate an mbuf from the pool, then use method such as rte_pktmbuf_append as bytes in the packet are initialized.
Network transports used by LibOSes such as Catnap accept a DemiBuffer in their pop calls (e.g., in src\rust\catnap\win\transport.rs, SharedCatnapTransport::pop). These methods depend on DemiBuffer::len() method to determine how much data they may fill, however, as noted above, this relies on data_len, which should indicate the number of initialized/used bytes. Currently, these LibOSes rely on the UB described above.
Proposed Solution
Make the following changes:
Initialize the data_len and pkt_len fields of MetaData to zero for new (i.e., not cloned/copied) DemiBuffers.
Add a buffer_len() or equivalent method to DemiBuffer, exposing the buf_len MetaData field.
Add append and prepend methods for manipulating the DemiBuffer length and content. There are many considerations here:
Ideally, these would be safe methods. The naive way to accomplish this would use copies (e.g., append(content: &[u8]) -> copy content into the buffer); however, this is not a great solution for performance-sensitive applications.
The unstable extension core_io_borrowed_buf offers an interesting idiom here. Borrowing a slice of [MaybeUninit] as a BorrowedBuf could offer a safe idiom as follows (not taking into account chaining, lifetimes elided):
Expose a series of unsafe functions which allow direct manipulation of the buffer and/or length. Require the user to initialize or indicate initialization correctly. This is likely the most performant/flexible approach.
Combination of the above.
Alternative Solutions
Alternatively, DemiBuffer might just initialize all bytes upfront. This still breaks consistency with DPDK, but provides a very simple solution which doesn't break anything else. Conceptually, DemiBuffer still lacks counterpart methods for trim and adjust, so this approach likely just postpones a real solution here.
The text was updated successfully, but these errors were encountered:
Context
When creating a new, empty
DemiBuffer
viaDemiBuffer::new
, the constructor initializesdata_len
andpkt_len
tobuf_len
(all fields ofMetaData
). This area has a few related issues:The
Deref
implementation creates a slice containing all DemiBuffer bytes in the range[0, data_len)
. Dereferencing a new DemiBuffer is undefined behavior, as it violates the safety requirements ofslice::from_raw_parts
, namely (emphasis added):and from the
GlobalAlloc::alloc
documentation:This behavior is inconsistent with DPDK. The closest equivalent function is probably
rte_pktmbuf_alloc
, which initializesbuf_len
depending on the memory pool butdata_len
andpkt_len
are initially zero. The expected workflow here is to allocate an mbuf from the pool, then use method such asrte_pktmbuf_append
as bytes in the packet are initialized.Network transports used by LibOSes such as Catnap accept a
DemiBuffer
in theirpop
calls (e.g., in src\rust\catnap\win\transport.rs,SharedCatnapTransport::pop
). These methods depend onDemiBuffer::len()
method to determine how much data they may fill, however, as noted above, this relies ondata_len
, which should indicate the number of initialized/used bytes. Currently, these LibOSes rely on the UB described above.Proposed Solution
Make the following changes:
data_len
andpkt_len
fields ofMetaData
to zero for new (i.e., not cloned/copied)DemiBuffers
.buffer_len()
or equivalent method toDemiBuffer
, exposing thebuf_len
MetaData field.append
andprepend
methods for manipulating theDemiBuffer
length and content. There are many considerations here:append(content: &[u8])
-> copycontent
into the buffer); however, this is not a great solution for performance-sensitive applications.BorrowedBuf
could offer a safe idiom as follows (not taking into account chaining, lifetimes elided):Alternative Solutions
Alternatively, DemiBuffer might just initialize all bytes upfront. This still breaks consistency with DPDK, but provides a very simple solution which doesn't break anything else. Conceptually,
DemiBuffer
still lacks counterpart methods fortrim
andadjust
, so this approach likely just postpones a real solution here.The text was updated successfully, but these errors were encountered: