Raw pointer Segfault

Greetings, please note this is me learning, i know how ppl feel about unsafe code in rust, but i need to learn it than just sweep it under the rug.

Question,
I decided to implement Queue Data structure and using unsafe. the data structure seems to be working fine and all my test passed. but including it in another project i was working on, i keep getting error

Here is my Queue DS

// Queue Data Structure implementation. NOTE:: For learning purposes only, 
// would not recommend using it in anything other than testing it 

use std::ptr;
use std::mem;
use tracing::info;


pub type Result<T> = std::result::Result<T, QueueError>;

pub trait QueueInterface {
    type Item;

    /// add item unto the queue
    fn enqueue(&mut self, item: Self::Item) -> Result<()>;

    /// remove item unto the queue
    fn dequeue(&mut self) -> Option<Self::Item>;

    /// returns the size of the queue
    fn size(&self) -> isize;

    /// return `true` is queue is empty otherwise return `false`
    fn is_empty(&self) -> bool;

    /// return `true` is queue is empty otherwise return `false`
    fn is_full(&self) -> bool;
}


#[derive(Clone, Copy)]
pub struct Queue<T: Sized + Clone> {
    buf: QueueBuf<T>,
    head: *mut T,
    tail: *mut T,
}

unsafe impl<T> Send for Queue<T> where T: Clone {}

impl<T> Queue<T> where T: Sized + Clone {
    /// create an instance of Queue with a default capacity
    ///
    /// default capaity is set to 100
    pub fn new(capacity: usize) -> Queue<T> {
        let mut buf = Vec::<T>::with_capacity(capacity);
        let buf_ptr = buf.as_mut_ptr();
        //let raw_f2 = ptr::addr_of!(buf_ptr);
        let ff = format!("{:?}", buf_ptr);

        tracing::info!(address_of_buf_ptr = ff.as_str());
        let buffer: QueueBuf<T>;

        buffer = unsafe {
            QueueBuf {
                start: buf_ptr,
                end: buf_ptr.offset((buf.capacity() - 1).try_into().unwrap()),
            }
        };

        // prevent rust from dropping the vec memory
        // intentionally leaking memory
        mem::forget(buf);

        Queue {
            buf: buffer,
            head: ptr::null_mut(),
            tail: ptr::null_mut(),
        }
    }
}

impl<T> QueueInterface for Queue<T> where T: Sized + Clone {
    type Item = T;


    /// returns a new Queue object large enough to of
    /// data of size: size.
    fn enqueue(&mut self, item: Self::Item) -> Result<()> {
        //let tail = ptr::addr_of!(self.tail);
        //let head = ptr::addr_of!(self.head);
        //let ph = format!("{:?}", head);
        //let pt = format!("{:?}", tail);

        //tracing::info!(address_of_buf_ptr = ff.as_str());
        if self.is_full() {
            return Err(QueueError::Full);
        }

        // check if queue is empty, if queue is empty return with an error
        // an ideal situation would be to just return None
        if self.is_empty() {
            self.head = self.buf.start as *mut T;
            self.tail = self.buf.start as *mut T;

            let ph = format!("{:?}", self.head);
            let pt = format!("{:?}", self.tail);

            unsafe {
                tracing::info!(address_of_buf_ptr = ph.as_str());
                tracing::info!(address_of_buf_ptr = pt.as_str());
                *(self.tail) = item;     // <= this is where it panics, but works fine during testing
                tracing::info!("Let see");
            }

            return Ok(());
        }

        self.tail = unsafe { self.tail.offset(1) };
        unsafe {
            *(self.tail) = item;
        }
        return Ok(());
    }

    fn size(&self) -> isize {
        unsafe {
            // adding one to the offset gives the actual size of the data
            // on the buffer
            self.tail.offset_from(self.head) + 1 as isize
        }
    }

    fn dequeue(&mut self) -> Option<Self::Item> {
        let item: Self::Item;

        // first check if it is empty
        if self.is_empty() {
            return None;
        }

        if self.head == self.tail {
            item = unsafe { (*self.head).clone() };

            self.head = ptr::null_mut();
            self.tail = ptr::null_mut();

            return Some(item);
        }

        unsafe {
            item = (*self.head).clone();
            self.head = self.head.offset(1);
        }

        return Some(item);
    }

    fn is_empty(&self) -> bool {
        if (self.head == ptr::null_mut()) && (self.tail == ptr::null_mut()) {
            return true;
        }
        false
    }

    fn is_full(&self) -> bool {
        if self.tail as *const T == self.buf.end {
            return true;
        } else {
            false
        }
    }
}

#[derive(Debug)]
pub enum QueueError {
    Full,
}

/// struct with pointer to the start and end of the buffer
#[derive(Clone, Copy)]
struct QueueBuf<T> {
    start: *const T,
    end: *const T,
}


#[cfg(test)]
mod test {
    use tracing::Instrument;
    use crate::{Queue, QueueInterface};

    #[derive(Clone)]
    struct SampleStrucType {
        random_string: String,
        another_random_string: String,
    }

    #[test]
    fn queue_size() {
        let mut queue = Queue::<u8>::new(10);

        queue.enqueue(10).unwrap();
        queue.enqueue(20).unwrap();
        queue.enqueue(30).unwrap();
        queue.enqueue(40).unwrap();

        let size = queue.size();
        assert_eq!(4 as isize, size);
    }

    #[test]
    fn empty() {
        let mut queue = Queue::<u8>::new(10);

        assert!(queue.is_empty())
    }

    #[test]
    fn empty_after_complete_dequeue() {
        let mut queue = Queue::<u8>::new(10);

        queue.enqueue(10).unwrap();
        queue.enqueue(20).unwrap();
        queue.enqueue(30).unwrap();
        queue.enqueue(40).unwrap();

        queue.dequeue();
        queue.dequeue();
        queue.dequeue();
        queue.dequeue();

        assert!(queue.is_empty());
    }


    #[test]
    fn dequeue() {
        let mut queue = Queue::<u8>::new(10);

        // fill up the queue
        queue.enqueue(10).unwrap();
        queue.enqueue(20).unwrap();
        queue.enqueue(30).unwrap();
        queue.enqueue(40).unwrap();


        assert_eq!(Some(10), queue.dequeue());
        assert_eq!(Some(20), queue.dequeue());
        assert_eq!(Some(30), queue.dequeue());
        assert_eq!(1 as isize, queue.size());

        queue.enqueue(50).unwrap();
        assert_eq!(2 as isize, queue.size());
    }

    #[test]
    fn is_full() {
        let mut queue = Queue::<u8>::new(4);

        queue.enqueue(10).unwrap();
        queue.enqueue(20).unwrap();
        queue.enqueue(30).unwrap();
        queue.enqueue(40).unwrap();

        assert!(queue.is_full());
    }

    #[test]
    fn enqueue() {
        let task = SampleStrucType {
            random_string: "STB_5500_UA".to_string(),
            another_random_string: "C055566".to_string(),
        };

        let mut queue = Queue::<SampleStrucType>::new(10);

        queue.enqueue(task).unwrap();

       assert_eq!(1 as isize, queue.size());

    }
}

All my test passed, no i decided to include it another project for the sake of using it

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv().ok();

    let subscriber = tracing_subscriber::FmtSubscriber::builder()
        .with_max_level(Level::DEBUG)
        .with_level(true)
        .finish();

    // set up our global subscriber
    tracing::subscriber::set_global_default(subscriber);

    // create a task Queue
    let mut queue = Queue::<UpgradeTask>::new(10);
    queue.enqueue(UpgradeTask {     //  <== Issue occur inside the function (check above for function defination)
        model: "stb_5500_UA".to_string(),
        navision_code: "66666".to_string(),
        endpoint_type: None,
    });

    // ........skipped ....

    Ok(())
}

Error I was getting, I also added pointer addresses it is also same and did not change. not sure if that help

2021-11-21T22:57:50.721058Z  INFO queue: main_buf_ptr_address_when_created="0x555555cd69e0"
2021-11-21T22:57:56.324303Z  INFO queue: address_of_ptr_head="0x555555cd69e0"
2021-11-21T22:58:01.880832Z  INFO queue: address_of_ptr_tail="0x555555cd69e0"
double free or corruption (out)
Signal: SIGABRT (Aborted)

Process finished with exit code 1

I believe what is going on -- but didn't confirm -- is that when you do

*(self.tail) = item;     // <= this is where it panics

You are dealing with basically v[0] of an uninitialized Vec. By using asignment, you attempt to drop the old value, but the old value is uninitialized garbage.

You should use assignment to overwrite initialized values but use ptr::write to overwrite uninitialized values, like the docs say.

1 Like

Thanks will try that and get back to you

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.