< 返回版块

ruby 发表于 2021-03-12 13:16

Tags:ffi

我在调用libsystemd.sosd_journal_sendv遇到一个问题

use std::os::raw::{c_int, c_void};

#[link(name="systemd", kind="dylib")]
extern "C" {
    fn sd_journal_sendv(iov: *const iovec, n: c_int) -> c_int;
}

#[repr(C)]
struct iovec {
    iov_base: *const c_void,
    iov_len: usize
}

impl From<&str> for iovec {
    fn from(s: &str) -> Self {
        Self {
            iov_base: s.as_ptr() as *const c_void,
            iov_len: s.len()
        }
    }
}

#[test]
fn test_sd_journal_sendv() {
    let priority_iovec: iovec = "PRIORITY=3".into();
    // 错误写法: `format!("").as_str().into()` 或 `"MESSAGE=log1".to_string().as_str().into()`
    // 错误写法不能正确得到字符串地址
    // 正确写法: let msg_iovec: iovec = "MESSAGE=log1".into();
    let msg_iovec: iovec = format!("MESSAGE={}", "log1").as_str().into();
    let iovecs = vec![priority_iovec, msg_iovec];

    let ret: Vec<iovec> = vec!["PRIORITY=3", "MESSAGE=log2", "UNIT=api"].into_iter().map(|x| x.into()).collect();
    unsafe {
        sd_journal_sendv(iovecs.as_ptr(), iovecs.len() as c_int);
        sd_journal_sendv(ret.as_ptr(), ret.len() as c_int);
    }
}

上面代码中只有log2会被打印,log1则不会(调用失败,也没报错)

经过我调查发现问题出在这行:

let msg_iovec: iovec = format!("MESSAGE={}", "log1").as_str().into();

改成以下形式就对了

let msg_iovec: iovec = "MESSAGE=log1".into();

所以我怀疑String::as_str().as_ptr() as *const c_void 在FFI时可能导致C语言那边不能找到正确的字符串内存地址?

评论区

写评论
Aya0wind 2021-03-12 14:19

粗看了一眼,你的iovec是引用format之后生成的String,这个String在执行完这条语句之后便释放了。而第二种用的是static str,这个不存在释放,所以结果正确,所以是一个经典的UAF问题。

1 共 1 条评论, 1 页