rust-bindgen
rust-bindgen copied to clipboard
Segmentation Fault in C++ operator caused by argument being NULL
Input C/C++ Header
class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler
{
public:
/// \brief Sends a block of data to the specified system that you are connected to.
///
/// Same as the above version, but takes a BitStream as input.
/// \param[in] bitStream Bitstream to send
/// \param[in] priority Priority level to send on. See PacketPriority.h
/// \param[in] reliability How reliably to send this data. See PacketPriority.h
/// \param[in] orderingChannel Channel to order the messages on, when using ordered or sequenced messages. Messages are only ordered relative to other messages on the same stream.
/// \param[in] systemIdentifier System Address or RakNetGUID to send this packet to, or in the case of broadcasting, the address not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none.
/// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to.
/// \param[in] forceReceipt If 0, will automatically determine the receipt number to return. If non-zero, will return what you give it.
/// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number
/// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration.
uint32_t Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber=0 );
}
Bindgen Invocation
let bindings = bindgen::Builder::default()
.header("wrapper.hpp")
.enable_cxx_namespaces()
.layout_tests(false)
.generate_inline_functions(true)
.allowlist_type("Rak.*")
.allowlist_type("DefaultMessageIDTypes")
.allowlist_type("UNASSIGNED.*")
.allowlist_function(".*Undefined.*")
.blocklist_function(".*BitStream_Write1")
.generate()
.expect("Unable to generate bindings");
Actual Results
Test crashes with
Signal: SIGSEGV (signal SIGSEGV: invalid address (fault address: 0x0))
Debugger stack at time of crash:
RakNet::RakNetGUID::operator!=(const RakNet::RakNetGUID &) const RakNetTypes.cpp:762
RakNet::RakPeer::IsLoopbackAddress(const RakNet::AddressOrGUID &, bool) const RakPeer.cpp:4008
RakNet::RakPeer::Send(const RakNet::BitStream *, PacketPriority, PacketReliability, char, RakNet::AddressOrGUID, bool, unsigned int) RakPeer.cpp:1391
chat_example lib.rs:75
chat_client lib.rs:127
{closure#0} lib.rs:126
call_once<raknet::tests::chat_client::{closure_env#0}, ()> function.rs:230
[Inlined] core::ops::function::FnOnce::call_once function.rs:230
__rust_begin_short_backtrace<fn()> lib.rs:573
[Inlined] _$LT$alloc..boxed..Box$LT$F$C$A$GT$$u20$as$u20$core..ops..function..FnOnce$LT$Args$GT$$GT$::call_once boxed.rs:1861
[Inlined] _$LT$core..panic..unwind_safe..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once unwind_safe.rs:271
[Inlined] std::panicking::try::do_call panicking.rs:492
[Inlined] std::panicking::try panicking.rs:456
[Inlined] std::panic::catch_unwind panic.rs:137
[Inlined] test::run_test_in_process lib.rs:596
{closure#0} lib.rs:490
[Inlined] test::run_test::run_test_inner::_$u7b$$u7b$closure$u7d$$u7d$ lib.rs:517
__rust_begin_short_backtrace<test::run_test::run_test_inner::{closure_env#1}, ()> backtrace.rs:122
[Inlined] std::thread::Builder::spawn_unchecked_::_$u7b$$u7b$closure$u7d$$u7d$::_$u7b$$u7b$closure$u7d$$u7d$ mod.rs:498
[Inlined] _$LT$core..panic..unwind_safe..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once unwind_safe.rs:271
[Inlined] std::panicking::try::do_call panicking.rs:492
[Inlined] std::panicking::try panicking.rs:456
[Inlined] std::panic::catch_unwind panic.rs:137
[Inlined] std::thread::Builder::spawn_unchecked_::_$u7b$$u7b$closure$u7d$$u7d$ mod.rs:497
call_once<std::thread::{impl#0}::spawn_unchecked_::{closure_env#1}<test::run_test::run_test_inner::{closure_env#1}, ()>, ()> function.rs:230
[Inlined] _$LT$alloc..boxed..Box$LT$F$C$A$GT$$u20$as$u20$core..ops..function..FnOnce$LT$Args$GT$$GT$::call_once boxed.rs:1861
[Inlined] _$LT$alloc..boxed..Box$LT$F$C$A$GT$$u20$as$u20$core..ops..function..FnOnce$LT$Args$GT$$GT$::call_once boxed.rs:1861
thread_start thread.rs:108
<unknown> 0x00007fb5da3e354d
__clone 0x00007fb5da468874
By looking at the debugger results, it seems the Send function is called with a NULL systemIdentifier even though it is instantiated and called in rust with this:
let system_identifier = AddressOrGUID::new2(&(*packet).systemAddress);
RakPeer_Send1(peer as *mut c_void, &bsOut as *const BitStream, PacketPriority_HIGH_PRIORITY, PacketReliability_RELIABLE_ORDERED, 0, system_identifier, false, 0);
The debugger also shows that all the other arguments are non-null and valid in the Send function.
I have tried other ways to instantiate the system_identifier but everything ends up with the same result, and I was able to verify that the system_identifier variable is valid on the rust side using the debugger.
Here are the generated bindings for some of the functions used:
#[link_name = "\u{1}_ZN6RakNet7RakPeer4SendEPKNS_9BitStreamE14PacketPriority17PacketReliabilitycNS_13AddressOrGUIDEbj"]
pub fn RakPeer_Send1(
this: *mut ::std::os::raw::c_void,
bitStream: *const root::RakNet::BitStream,
priority: root::PacketPriority,
reliability: root::PacketReliability,
orderingChannel: ::std::os::raw::c_char,
systemIdentifier: root::RakNet::AddressOrGUID,
broadcast: bool,
forceReceiptNumber: u32,
) -> u32;
// I use new2 in this example but I also tried new3 and new4
#[inline]
pub unsafe fn new() -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
AddressOrGUID_AddressOrGUID(__bindgen_tmp.as_mut_ptr());
__bindgen_tmp.assume_init()
}
#[inline]
pub unsafe fn new1(input: *const root::RakNet::AddressOrGUID) -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
AddressOrGUID_AddressOrGUID1(__bindgen_tmp.as_mut_ptr(), input);
__bindgen_tmp.assume_init()
}
#[inline]
pub unsafe fn new2(input: *const root::RakNet::SystemAddress) -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
AddressOrGUID_AddressOrGUID2(__bindgen_tmp.as_mut_ptr(), input);
__bindgen_tmp.assume_init()
}
#[inline]
pub unsafe fn new3(packet: *mut root::RakNet::Packet) -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
AddressOrGUID_AddressOrGUID3(__bindgen_tmp.as_mut_ptr(), packet);
__bindgen_tmp.assume_init()
}
#[inline]
pub unsafe fn new4(input: *const root::RakNet::RakNetGUID) -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
AddressOrGUID_AddressOrGUID4(__bindgen_tmp.as_mut_ptr(), input);
__bindgen_tmp.assume_init()
}
Expected Results
The send function is called with valid arguments and doesn't crash
How's AddressOrGUID defined? I think if it's not a POD type this might unfortunately be expected (because Rust doesn't have the C++ concept of non-trivial types).
Like this:
struct RAK_DLL_EXPORT AddressOrGUID
{
RakNetGUID rakNetGuid;
SystemAddress systemAddress;
SystemIndex GetSystemIndex(void) const {if (rakNetGuid!=UNASSIGNED_RAKNET_GUID) return rakNetGuid.systemIndex; else return systemAddress.systemIndex;}
bool IsUndefined(void) const {return rakNetGuid==UNASSIGNED_RAKNET_GUID && systemAddress==UNASSIGNED_SYSTEM_ADDRESS;}
void SetUndefined(void) {rakNetGuid=UNASSIGNED_RAKNET_GUID; systemAddress=UNASSIGNED_SYSTEM_ADDRESS;}
static unsigned long ToInteger( const AddressOrGUID &aog );
const char *ToString(bool writePort=true) const;
void ToString(bool writePort, char *dest) const;
AddressOrGUID() {}
AddressOrGUID( const AddressOrGUID& input )
{
rakNetGuid=input.rakNetGuid;
systemAddress=input.systemAddress;
}
AddressOrGUID( const SystemAddress& input )
{
rakNetGuid=UNASSIGNED_RAKNET_GUID;
systemAddress=input;
}
AddressOrGUID( Packet *packet );
AddressOrGUID( const RakNetGUID& input )
{
rakNetGuid=input;
systemAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
AddressOrGUID& operator = ( const AddressOrGUID& input )
{
rakNetGuid=input.rakNetGuid;
systemAddress=input.systemAddress;
return *this;
}
AddressOrGUID& operator = ( const SystemAddress& input )
{
rakNetGuid=UNASSIGNED_RAKNET_GUID;
systemAddress=input;
return *this;
}
AddressOrGUID& operator = ( const RakNetGUID& input )
{
rakNetGuid=input;
systemAddress=UNASSIGNED_SYSTEM_ADDRESS;
return *this;
}
inline bool operator==( const AddressOrGUID& right ) const {return (rakNetGuid!=UNASSIGNED_RAKNET_GUID && rakNetGuid==right.rakNetGuid) || (systemAddress!=UNASSIGNED_SYSTEM_ADDRESS && systemAddress==right.systemAddress);}
};
rakNetGuid and systemAddress are supposed to be set to UNASSIGNED_RAKNET_GUID and UNASSIGNED_SYSTEM_ADDRESS respectively when the other is used.
Here's the RaknetGUID definition:
struct RAK_DLL_EXPORT RakNetGUID
{
RakNetGUID();
explicit RakNetGUID(uint64_t _g) {g=_g; systemIndex=(SystemIndex)-1;}
// uint32_t g[6];
uint64_t g;
...
And the AddressOrGuid definition:
struct RAK_DLL_EXPORT SystemAddress
{
/// Constructors
SystemAddress();
SystemAddress(const char *str);
SystemAddress(const char *str, unsigned short port);
/// SystemAddress, with RAKNET_SUPPORT_IPV6 defined, holds both an sockaddr_in6 and a sockaddr_in
union// In6OrIn4
{
#if RAKNET_SUPPORT_IPV6==1
struct sockaddr_storage sa_stor;
sockaddr_in6 addr6;
#endif
sockaddr_in addr4;
} address;
/// This is not used internally, but holds a copy of the port held in the address union, so for debugging it's easier to check what port is being held
unsigned short debugPort;
...
And I might as well link the rest of the library if you want to look by yourself: https://github.com/facebookarchive/RakNet/blob/master/Source/RakNetTypes.h
Hello, Any update on this one ? Do you still have the solution, could you find a fix or a workaround ? Asking because I observe a very similar issue on my project: it ends up with signal: 11, SIGSEGV: invalid memory reference, where you have invalid address. And in more details, I observe the same as you on GDB, I have a 'self' which is valid and points to an address in Rust world, and when I arrive in the C++ world, the 'this' becomes 0x00 (null ptr -> crash)
Any hint would be appreciated. Thx
Hello! I switched to another solution so I did not have to fix that problem, I suppose though you could make a C wrapper around that C++ code and then use it in rust, or maybe modify the type to make it not POD.
As emilio said this is related to having a POD type so if that's not the case for you you should try another issue i guess. Good luck!