Expand description
§Safer-Ring: Safe io_uring for Rust
A comprehensive, safe Rust wrapper around io_uring that provides zero-cost abstractions
while preventing common memory safety issues. The library uses Rust’s type system,
lifetime management, and pinning to ensure that buffers remain valid during asynchronous
I/O operations, eliminating use-after-free bugs and data races.
§Key Features
§Safety Guarantees
- Memory Safety: Compile-time guarantees that buffers outlive their operations
- Type Safety: State machines prevent operations in invalid states
- Lifetime Safety: Automatic enforcement of buffer and operation lifetimes
- Thread Safety: Safe sharing of resources across async tasks
§Performance Optimizations
- Zero-Cost Abstractions: No runtime overhead compared to raw
io_uring - Batch Operations: Submit multiple operations efficiently with dependency support
- Buffer Pooling: Efficient buffer reuse to minimize allocations
- Advanced Features: Support for buffer selection, multi-shot operations, and more
§Developer Experience
- Async/Await: Seamless integration with Rust’s async ecosystem
- Comprehensive Logging: Structured logging and performance metrics
- Flexible Configuration: Optimized presets for different use cases
- Graceful Degradation: Automatic fallback for older kernel versions
§Quick Start
§Recommended: Ownership Transfer API
use safer_ring::{Ring, OwnedBuffer};
// Create a new ring with 32 entries
let mut ring = Ring::new(32)?;
// Create a buffer - ownership will be transferred during I/O
let buffer = OwnedBuffer::new(1024);
// Safe read with ownership transfer ("hot potato" pattern)
let (bytes_read, buffer) = ring.read_owned(0, buffer).await?;
println!("Read {} bytes", bytes_read);
// Buffer is safely returned and can be reused
let (bytes_written, _buffer) = ring.write_owned(1, buffer).await?;
println!("Wrote {} bytes", bytes_written);
§A Note on the PinnedBuffer API (Educational / Not Recommended)
You may see a PinnedBuffer type and methods like read() or write() in the codebase.
This API is considered educational and is not suitable for practical use. It suffers from
fundamental lifetime constraints in Rust that make it impossible to use in loops or for
concurrent operations on the same Ring instance. It exists to demonstrate the complexities
that the OwnedBuffer model successfully solves. For all applications, please use the
OwnedBuffer API.
The example below is provided for completeness but is not a recommended pattern:
use safer_ring::{Ring, PinnedBuffer};
let mut ring = Ring::new(32)?;
let mut buffer = PinnedBuffer::with_capacity(1024);
// This pattern works for a single, one-shot operation but fails in loops.
let (bytes_read, buffer) = ring.read(0, buffer.as_mut_slice())?.await?;
println!("Read {} bytes", bytes_read);
§High-Performance Batch Operations
For high-throughput applications, submit multiple operations in a single batch:
use safer_ring::{Ring, Batch, Operation, PinnedBuffer, BatchConfig};
// Note: Batch operations currently require careful lifetime management
// See examples/async_demo.rs for working patterns
let mut ring = Ring::new(128)?;
let mut batch = Batch::new();
// Prepare multiple buffers
let mut read_buffer = PinnedBuffer::with_capacity(4096);
let mut write_buffer = PinnedBuffer::from_slice(b"Hello, world!");
// Add operations to the batch
batch.add_operation(Operation::read().fd(0).buffer(read_buffer.as_mut_slice()))?;
batch.add_operation(Operation::write().fd(1).buffer(write_buffer.as_mut_slice()))?;
// Submit all operations at once
let results = ring.submit_batch(batch)?.await?;
println!("Batch completed: {} operations", results.results.len());
Note: Batch operations are fully implemented but have some API ergonomics limitations. See
examples/async_demo.rsfor working usage patterns.
§Network Server Example
use safer_ring::{Ring, PinnedBuffer, BufferPool};
use std::os::unix::io::RawFd;
let ring = Ring::new(256)?;
let buffer_pool = BufferPool::new(100, 4096);
let listening_fd: RawFd = 3; // Assume we have a listening socket
loop {
// Accept a new connection
let client_fd = ring.accept(listening_fd)?.await?;
// Get a buffer from the pool
let buffer = buffer_pool.get().unwrap();
// Read data from the client
let (bytes_read, buffer) = ring.recv(client_fd, buffer.as_mut_slice())?.await?;
// Echo the data back
let (bytes_written, _buffer) = ring.send(client_fd, buffer.as_mut_slice())?.await?;
println!("Echoed {} bytes", bytes_written);
// Buffer is automatically returned to pool when dropped
}§Configuration and Optimization
Safer-ring provides pre-configured setups for different use cases:
use safer_ring::{Ring, SaferRingConfig};
// Low-latency configuration for real-time applications
let config = SaferRingConfig::low_latency();
let ring = Ring::with_config(config)?;
// High-throughput configuration for batch processing
let config = SaferRingConfig::high_throughput();
let ring = Ring::with_config(config)?;
// Auto-detect optimal configuration for current system
let config = SaferRingConfig::auto_detect()?;
let ring = Ring::with_config(config)?;
§Advanced Features
§Buffer Selection and Provided Buffers
use safer_ring::{Ring, BufferGroup, AdvancedConfig};
// Create a buffer group for kernel buffer selection
let mut buffer_group = BufferGroup::new(1, 64, 4096)?;
// Configure ring with advanced features
let mut config = AdvancedConfig::default();
config.buffer_selection = true;
config.provided_buffers = true;
let ring = Ring::with_advanced_config(config)?;
// Use buffer selection for zero-copy reads§Comprehensive Logging and Metrics
use safer_ring::{Ring, SaferRingConfig, LogLevel};
// Enable detailed logging and metrics
let mut config = SaferRingConfig::development();
config.logging.enabled = true;
config.logging.level = LogLevel::Debug;
config.logging.metrics = true;
let mut ring = Ring::with_config(config)?;
// Operations are automatically logged with timing information
let mut buffer = safer_ring::PinnedBuffer::with_capacity(1024);
let (bytes_read, _) = ring.read(0, buffer.as_mut_slice())?.await?;
§Platform Support
- Linux: Full
io_uringsupport with all advanced features - Other platforms: Graceful degradation with stub implementations for testing
§Minimum Kernel Requirements
- Basic functionality: Linux 5.1+
- Advanced features: Linux 5.19+ (buffer selection, multi-shot operations)
- Optimal performance: Linux 6.0+ (cooperative task running, defer taskrun)
The library automatically detects available features and gracefully degrades functionality on older kernels while maintaining API compatibility.
§Security Considerations
§File Descriptor Responsibility
⚠️ CRITICAL SECURITY NOTICE: This library accepts raw file descriptors (RawFd)
from user code and does NOT perform any validation, permission checks, or access
control. The application is entirely responsible for ensuring file descriptor security.
§Security Responsibilities
The calling application must ensure that all file descriptors:
- Are valid and owned by the current process
- Have appropriate permissions for the intended operation (read/write/accept)
- Are not subject to race conditions from concurrent access
- Point to intended resources (files, sockets, devices)
- Have been properly authenticated and authorized
- Are not being used maliciously by untrusted code
§Potential Security Risks
Using invalid, unauthorized, or malicious file descriptors can result in:
- Reading from or writing to unintended files, sockets, or devices
- Information disclosure or data corruption
- Privilege escalation or unauthorized system access
- Buffer overflow attacks or memory corruption (from network data)
- Denial of service or system instability
§Security Best Practices
Always implement these security controls at the application level:
- Validate file descriptors at security boundaries
- Use proper access control and permission checks
- Implement input validation for all received data
- Consider using sandboxing or privilege isolation
- Apply principle of least privilege for file access
- Use secure network protocols and authentication
- Monitor and log suspicious file descriptor usage
§Data Validation
For network operations, the application must:
- Validate all received data before processing
- Implement proper input sanitization and bounds checking
- Use secure parsing for untrusted input data
- Consider data confidentiality and integrity requirements
- Protect against injection attacks and protocol exploitation
This library provides memory safety and async safety but does NOT provide security boundaries or access control. Security must be implemented at the application layer.
Re-exports§
pub use advanced::AdvancedConfig;pub use advanced::BufferGroup;pub use advanced::FeatureDetector;pub use advanced::MultiShotConfig;pub use buffer::PinnedBuffer;pub use compat::AsyncCompat;pub use compat::AsyncReadAdapter;pub use compat::AsyncWriteAdapter;pub use compat::File;pub use compat::Socket;pub use config::BufferConfig;pub use config::ConfigBuilder;pub use config::ErrorHandlingConfig;pub use config::LoggingConfig;pub use config::PerformanceConfig;pub use config::RingConfig;pub use config::SaferRingConfig;pub use error::Result;pub use error::SaferRingError;pub use logging::LogLevel;pub use logging::Logger;pub use logging::PerformanceMetrics;pub use operation::Operation;pub use operation::OperationState;pub use ownership::BufferOwnership;pub use ownership::OwnedBuffer;pub use ownership::SafeBuffer;pub use pool::BufferPool;pub use pool::PooledBuffer;pub use registry::FixedFile;pub use registry::RegisteredBufferSlot;pub use registry::Registry;pub use ring::Batch;pub use ring::BatchConfig;pub use ring::BatchResult;pub use ring::CompletionResult;pub use ring::OperationResult;pub use ring::Ring;pub use runtime::get_environment_info;pub use runtime::is_io_uring_available;pub use runtime::Backend;pub use runtime::EnvironmentInfo;pub use runtime::Runtime;pub use safety::OrphanTracker;pub use safety::SafeOperation;pub use safety::SafeOperationFuture;pub use safety::SubmissionId;
Modules§
- advanced
- Advanced io_uring features and optimizations.
- backend
- Backend abstraction for different I/O mechanisms.
- buffer
- Pinned buffer management for educational and benchmarking purposes.
- compat
- Compatibility adapters for AsyncRead/AsyncWrite traits.
- config
- Configuration options for different safer-ring use cases.
- error
- Error types and handling for safer-ring operations.
- future
- Future implementations for async/await support.
- logging
- Comprehensive logging and debugging support for safer-ring.
- operation
- Type-safe operation state management.
- ownership
- Buffer ownership management for safe io_uring operations.
- perf
- Performance profiling and optimization utilities.
- pool
- Buffer pool for efficient buffer reuse.
- registry
- File descriptor and buffer registration for performance optimization.
- ring
- Safe io_uring wrapper with compile-time safety guarantees.
- runtime
- Runtime detection and fallback system for safer-ring.
- safety
- Cancellation safety mechanisms for io_uring operations.
Macros§
- log_
debug - Log a debug-level message using the global logger.
- log_
error - Log an error-level message using the global logger.
- log_
info - Log an info-level message using the global logger.
- log_
trace - Convenience macros for logging.
- log_
warn - Log a warning-level message using the global logger.
- time_
async_ operation - Macro for timing async operations.
- time_
operation - Macro for easy performance timing.
Type Aliases§
- Accept
Future - Future type for accept operations that can be awaited.
- Batch
Future - Future type for batch operations that can be awaited.
- Read
Future - Future type for read operations that can be awaited.
- Recv
Future - Future type for receive operations that can be awaited.
- Send
Future - Future type for send operations that can be awaited.
- Standalone
Batch Future - Standalone future type for batch operations that doesn’t hold Ring references.
- Write
Future - Future type for write operations that can be awaited.