Rust can';t传递新类型包装的不安全C结构,而不会导致非法指令

Rust can';t传递新类型包装的不安全C结构,而不会导致非法指令,rust,ffi,Rust,Ffi,我正在包装不安全的FFI层,遇到了一个非常奇怪的问题。(最近一晚) 请注意,方法1和方法2仅在集群是传入fn还是在fn内部创建方面有所不同 运行时: trying method 1 trying method 2 Illegal instruction 无论是否调用method1,method2总是会失败,并发出非法指令 valgrind生成的stacktrace可能很有趣: trying method 2 ==19145== Invalid write of size 8 ==19145==

我正在包装不安全的FFI层,遇到了一个非常奇怪的问题。(最近一晚)

请注意,方法1和方法2仅在集群是传入fn还是在fn内部创建方面有所不同

运行时:

trying method 1
trying method 2
Illegal instruction
无论是否调用method1,method2总是会失败,并发出非法指令

valgrind生成的stacktrace可能很有趣:

trying method 2
==19145== Invalid write of size 8
==19145==    at 0x6A10ACF: std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==19145==    by 0x4F78367: std::list<std::string, std::allocator<std::string> >::_M_insert(std::_List_iterator<std::string>, std::string const&) (in /usr/local/lib/libcassandra.so.1.0.0.rc1)
==19145==    by 0x4F77E13: std::list<std::string, std::allocator<std::string> >::push_back(std::string const&) (in /usr/local/lib/libcassandra.so.1.0.0.rc1)
==19145==    by 0x4F8AE69: cass_cluster_set_contact_points (in /usr/local/lib/libcassandra.so.1.0.0.rc1)
==19145==    by 0x10DDC3: method2::h2b76fca37ae2e2878ba (test.rs:25)
==19145==    by 0x10DA1B: main::hc39cc26c65e20849maa (test.rs:13)
==19145==    by 0x11C198: rust_try_inner (in /home/tupshin/workspaces/rust/cql-ffi-safe/target/test)
==19145==    by 0x11C185: rust_try (in /home/tupshin/workspaces/rust/cql-ffi-safe/target/test)
==19145==    by 0x11988C: rt::lang_start::hd3d7c7415c447b9fdBB (in /home/tupshin/workspaces/rust/cql-ffi-safe/target/test)
==19145==    by 0x10DC04: main (in /home/tupshin/workspaces/rust/cql-ffi-safe/target/test)
==19145==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
尝试方法2
==19145==大小为8的无效写入
==19145==0x6A10ACF:std::_详细信息::_列表_节点_基础::_钩子(std:_详细信息::_列表_节点_基础*)(in/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==19145==by 0x4F78367:std::list::_M_insert(std::_list_迭代器,std::string const&)(在/usr/local/lib/libcassandra.so.1.0.0.rc1中)
==19145==by 0x4F77E13:std::list::push_back(std::string const&)(在/usr/local/lib/libcassandra.so.1.0.0.rc1中)
==19145==by 0x4F8AE69:cass\u cluster\u set\u contact\u points(在/usr/local/lib/libcassandra.so.1.0.rc1中)
==19145==0x10DDC3:method2::h2b76fca37ae2e2878ba(测试rs:25)
==19145==0x10DA1B:main::hc39cc26c65e20849maa(测试rs:13)
==19145==0x11C198:内部生锈(in/home/tupshin/workspace/rust/cql ffi-safe/target/test)
==19145==0x11C185:尝试生锈(在/home/tupshin/workspace/rust/cql ffi-safe/target/test中)
==19145==0x11988C:rt::lang_start::hd3d7c7415c447b9fdBB(in/home/tupshin/workspace/rust/cql-ffi-safe/target/test)
==19145==0x10DC04:main(in/home/tupshin/workspace/rust/cql-ffi-safe/target/test)
==19145==地址0x0不是堆栈、malloc或(最近)空闲

我认为您是从错误的角度进行的-您不应该取消对C库返回给您的指针的引用。你甚至不知道被指物体的构成

相反,您应该简单地保留指针并将其传递回预期的函数。我已经把你的一些代码重新整理过了。这里,我们有一个名为
Cluster
的结构,它将拥有
cass\u Cluster\u new
返回的指针。我们创建了一个小示例方法,该方法可以为集群做一些有趣的事情,并且我们还可以在使用
集群时释放资源

请注意,这个
集群
结构实际上占用了空间,而不是您当前拥有的空
集群
枚举。空枚举占用零空间,因此将以有趣的方式对其进行优化。但是,您实际上需要将指针放在某个位置

另一件事是,我们只是将库中的返回指针视为
c\u void
。这是因为我们永远不会去引用它,所以我们只是把它当作一个不透明的句柄

#![feature(libc)]

extern crate libc;

use libc::{c_void,c_int};

extern "C" {
    pub fn cass_cluster_new() -> *mut c_void;
    pub fn cass_cluster_free(cluster: *mut c_void);
    pub fn cass_cluster_set_port(cluster: *mut c_void, port: c_int); // ignoring return code
}

struct Cluster(*mut c_void);

impl Cluster {
    fn new() -> Cluster {
        Cluster(unsafe { cass_cluster_new() })
    }

    // N.B. Ports are better represented as u16! 
    fn set_port(&mut self, port: i32) {
        unsafe { cass_cluster_set_port(self.0, port) }
    }
}

impl Drop for Cluster {
    fn drop(&mut self) {
        unsafe { cass_cluster_free(self.0) }
    }
}

fn main() {
    let mut cluster = Cluster::new();
    cluster.set_port(5432);

    // cluster is automatically dropped when it goes out of scope
}

()

为什么要从cass\u cluster\u new返回的指针中读取?从中可以看出,此构造函数是返回指向不透明数据块的指针的常规C模式。你不应该阅读它,只需将它传递给期望它的方法即可。对不起,我被之前阻止我的ICE解决方案甩了。我把它切换回仅仅是一个解引用,这会导致完全相同的症状。这应该是有效的,还是我必须使用A&?你有多少经验与C/C++?这可能会帮助我更好地制定一个好的答案。足够理解这些结构,但不足以很好地解释它们。这些年来,我在这里和那里涉猎过,但大多来自java/perl/etc背景。我感谢你的努力。谢谢你的提问:)非常有帮助,谢谢。我认为这给了我进步所需的大部分精神工具。非常感谢。我猜bindgen生成pub fn cass_cluster_new()->mut cass cluster;而不是pub fn cass_cluster_new()->*mut c_void;pub fn cass_cluster_new()->*mut c_void;这意味着我必须选择将bindgen的输出修改为一种文档形式,或者使用*mut CassCluster,但确保我*只使用它,就好像它是您版本的c_void不透明blob一样。您提到您使用的是bindgen,我假设是这样。我没有使用它,但我猜他们使用指向空枚举的指针为原始指针提供某种类型安全性。当您开始使用多种类型的原始指针时,这非常有用。重要的一点是,你不应该自己去引用这些指针——让本机库来处理。换句话说,你可以使用
struct Cluster(*mut casscuster)
而不是
struct Cluster(*mut c_void)
,一切都会正常。千万不要试图偷看指针里面^_^是的,我想在交叉评论中说同样的话。完全有道理
#![feature(libc)]

extern crate libc;

use libc::{c_void,c_int};

extern "C" {
    pub fn cass_cluster_new() -> *mut c_void;
    pub fn cass_cluster_free(cluster: *mut c_void);
    pub fn cass_cluster_set_port(cluster: *mut c_void, port: c_int); // ignoring return code
}

struct Cluster(*mut c_void);

impl Cluster {
    fn new() -> Cluster {
        Cluster(unsafe { cass_cluster_new() })
    }

    // N.B. Ports are better represented as u16! 
    fn set_port(&mut self, port: i32) {
        unsafe { cass_cluster_set_port(self.0, port) }
    }
}

impl Drop for Cluster {
    fn drop(&mut self) {
        unsafe { cass_cluster_free(self.0) }
    }
}

fn main() {
    let mut cluster = Cluster::new();
    cluster.set_port(5432);

    // cluster is automatically dropped when it goes out of scope
}