Crate safer_ring

Crate safer_ring 

Source
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

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);

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.rs for 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_uring support 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§

AcceptFuture
Future type for accept operations that can be awaited.
BatchFuture
Future type for batch operations that can be awaited.
ReadFuture
Future type for read operations that can be awaited.
RecvFuture
Future type for receive operations that can be awaited.
SendFuture
Future type for send operations that can be awaited.
StandaloneBatchFuture
Standalone future type for batch operations that doesn’t hold Ring references.
WriteFuture
Future type for write operations that can be awaited.