diff --git a/aya-log-common/src/lib.rs b/aya-log-common/src/lib.rs index 976462dd..9b85c65e 100644 --- a/aya-log-common/src/lib.rs +++ b/aya-log-common/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -use core::{cmp, mem, ptr, slice}; +use core::{mem, num, ptr, slice}; use num_enum::IntoPrimitive; @@ -8,8 +8,10 @@ pub const LOG_BUF_CAPACITY: usize = 8192; pub const LOG_FIELDS: usize = 6; -#[repr(usize)] -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub type LogValueLength = u16; + +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)] pub enum Level { /// The "error" level. /// @@ -33,7 +35,7 @@ pub enum Level { Trace, } -#[repr(usize)] +#[repr(u8)] #[derive(Copy, Clone, Debug)] pub enum RecordField { Target = 1, @@ -46,7 +48,7 @@ pub enum RecordField { /// Types which are supported by aya-log and can be safely sent from eBPF /// programs to userspace. -#[repr(usize)] +#[repr(u8)] #[derive(Copy, Clone, Debug)] pub enum Argument { DisplayHint, @@ -111,26 +113,29 @@ where } pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result { - let size = mem::size_of::() + mem::size_of::() + self.value.len(); - let remaining = cmp::min(buf.len(), LOG_BUF_CAPACITY); - // Check if the size doesn't exceed the buffer bounds. - if size > remaining { + // Break the abstraction to please the verifier. + if buf.len() > LOG_BUF_CAPACITY { + buf = &mut buf[..LOG_BUF_CAPACITY]; + } + let Self { tag, value } = self; + let len = value.len(); + let wire_len: LogValueLength = value + .len() + .try_into() + .map_err(|num::TryFromIntError { .. }| ())?; + let size = mem::size_of_val(tag) + mem::size_of_val(&wire_len) + len; + if size > buf.len() { return Err(()); } - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) }; - buf = &mut buf[mem::size_of::()..]; + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, *tag) }; + buf = &mut buf[mem::size_of_val(tag)..]; - unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) }; - buf = &mut buf[mem::size_of::()..]; + unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, wire_len) }; + buf = &mut buf[mem::size_of_val(&wire_len)..]; + + unsafe { ptr::copy_nonoverlapping(value.as_ptr(), buf.as_mut_ptr(), len) }; - let len = cmp::min(buf.len(), self.value.len()); - // The verifier isn't happy with `len` being unbounded, so compare it - // with `LOG_BUF_CAPACITY`. - if len > LOG_BUF_CAPACITY { - return Err(()); - } - buf[..len].copy_from_slice(&self.value[..len]); Ok(size) } } @@ -210,10 +215,11 @@ pub fn write_record_header( line: u32, num_args: usize, ) -> Result { + let level: u8 = level.into(); let mut size = 0; for attr in [ TagLenValue::::new(RecordField::Target, target.as_bytes()), - TagLenValue::::new(RecordField::Level, &(level as usize).to_ne_bytes()), + TagLenValue::::new(RecordField::Level, &level.to_ne_bytes()), TagLenValue::::new(RecordField::Module, module.as_bytes()), TagLenValue::::new(RecordField::File, file.as_bytes()), TagLenValue::::new(RecordField::Line, &line.to_ne_bytes()), @@ -224,3 +230,17 @@ pub fn write_record_header( Ok(size) } + +#[cfg(test)] +mod test { + use super::*; + + fn log_value_length_sufficient() { + assert!( + LOG_BUF_CAPACITY >= LogValueLength::MAX.into(), + "{} < {}", + LOG_BUF_CAPACITY, + LogValueLength::MAX + ); + } +} diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index affeae49..989f24d5 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -59,9 +59,11 @@ use std::{ const MAP_NAME: &str = "AYA_LOGS"; -use aya_log_common::{Argument, DisplayHint, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS}; +use aya_log_common::{ + Argument, DisplayHint, Level, LogValueLength, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS, +}; use bytes::BytesMut; -use log::{error, Level, Log, Record}; +use log::{error, Log, Record}; use thiserror::Error; use aya::{ @@ -116,9 +118,7 @@ impl BpfLogger { let log = logger.clone(); tokio::spawn(async move { - let mut buffers = (0..10) - .map(|_| BytesMut::with_capacity(LOG_BUF_CAPACITY)) - .collect::>(); + let mut buffers = vec![BytesMut::with_capacity(LOG_BUF_CAPACITY); 10]; loop { let events = buf.read_events(&mut buffers).await.unwrap(); @@ -360,7 +360,7 @@ pub enum Error { fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { let mut target = None; - let mut level = Level::Trace; + let mut level = None; let mut module = None; let mut file = None; let mut line = None; @@ -371,16 +371,25 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { match tag { RecordField::Target => { - target = Some(std::str::from_utf8(value).map_err(|_| ())?); + target = Some(str::from_utf8(value).map_err(|_| ())?); } RecordField::Level => { - level = unsafe { ptr::read_unaligned(value.as_ptr() as *const _) } + level = Some({ + let level = unsafe { ptr::read_unaligned(value.as_ptr() as *const _) }; + match level { + Level::Error => log::Level::Error, + Level::Warn => log::Level::Warn, + Level::Info => log::Level::Info, + Level::Debug => log::Level::Debug, + Level::Trace => log::Level::Trace, + } + }) } RecordField::Module => { - module = Some(std::str::from_utf8(value).map_err(|_| ())?); + module = Some(str::from_utf8(value).map_err(|_| ())?); } RecordField::File => { - file = Some(std::str::from_utf8(value).map_err(|_| ())?); + file = Some(str::from_utf8(value).map_err(|_| ())?); } RecordField::Line => { line = Some(u32::from_ne_bytes(value.try_into().map_err(|_| ())?)); @@ -505,7 +514,7 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { &Record::builder() .args(format_args!("{full_log_msg}")) .target(target.ok_or(())?) - .level(level) + .level(level.ok_or(())?) .module_path(module) .file(file) .line(line) @@ -516,16 +525,18 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> { } fn try_read(mut buf: &[u8]) -> Result<(T, &[u8], &[u8]), ()> { - if buf.len() < mem::size_of::() + mem::size_of::() { + if buf.len() < mem::size_of::() + mem::size_of::() { return Err(()); } let tag = unsafe { ptr::read_unaligned(buf.as_ptr() as *const T) }; buf = &buf[mem::size_of::()..]; - let len = usize::from_ne_bytes(buf[..mem::size_of::()].try_into().unwrap()); - buf = &buf[mem::size_of::()..]; + let len = + LogValueLength::from_ne_bytes(buf[..mem::size_of::()].try_into().unwrap()); + buf = &buf[mem::size_of::()..]; + let len: usize = len.into(); if buf.len() < len { return Err(()); } @@ -538,7 +549,7 @@ fn try_read(mut buf: &[u8]) -> Result<(T, &[u8], &[u8]), ()> { mod test { use super::*; use aya_log_common::{write_record_header, WriteToBuf}; - use log::logger; + use log::{logger, Level}; fn new_log(args: usize) -> Result<(usize, Vec), ()> { let mut buf = vec![0; 8192];