GETTING INTO THE RUSTY BUCKET

Lessons from Integrating Rust with Existing C

 

WILLIAM BROWN

Software Engineer - RHDS

firstyear@redhat.com

Follow along live:
http://bit.ly/2iBdEc0

what even is "DS"?

 

 

 

plugin architecture

 

 

 

DS plugins ...

 

 

 

  • dlopen(plugin.so)
  • Passes in a pointer to a fat struct (pblock)
  • Registration of callbacks for hooks.
// !! THIS IS REAL C CODE !!
int addn_init(Slapi_PBlock *pb)
{
    int result = 0;
    result = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03);
    if (result != LDAP_SUCCESS) {
        goto out;
    }
...
    result = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void*)addn_prebind);
    if (result != LDAP_SUCCESS) {
        goto out;
    }
...

I'm safe here at the top!

  • We can't be complacent due to our history
  • ~500,000 lines of C
  • C can not easily be proven or reasoned about

why IS RUST good?

 

 

 

  • Type safe
  • Memory safe
  • Compiler checked
  • Mozilla have evidence of it's stability in Firefox

rust .... with c?

 

 

 

  • Don't need to rewrite existing code.
  • Safety of rust.
  • Links with C.
  • C links to Rust.

why not use corrode?

 

 

 

  • Corrode converts C to Rust code automatically.
  • Existing code base.
  • Not converting C to rust.
  • Writing plugins that can be called from C plugin infrastructure

lessons gained so far

  • Will take investment of time and mental energy

firstyear@redhat.com

http://bit.ly/2iBdEc0

it can't be that hard ...

 

 

 

  • Link rust to C
  • Rust compile a .so
  • Make the types match
  • Call the libslapd apis as needed
#include <slapi-plugin.h>
...

    result = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void*)addn_prebind);
    if (result != LDAP_SUCCESS) {
        goto out;
    }
}
...

int   
addn_prebind(Slapi_PBlock *pb)
{
...
 

how to link rust to c

 

 

 

# cat Cargo.toml
[package]
name = "slapi_r_plugin"
version = "0.1.0"
authors = ["william"]
; This line is the magic!
links = "slapd"

# cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
 Downloading libc v0.2.10
   Compiling libc v0.2.10
   Compiling slapi_r_plugin v0.1.0 (file://.../ds_rust/slapi_r_plugin)
    Finished debug [unoptimized + debuginfo] target(s) in 1.3 secs

this fine contraption, so I'm told

 

 

 

[package]
name = "slapi_r_plugin"
version = "0.1.0"
authors = ["william"]

[dependencies]
libc = "0.2.0"

[lib]
name = "slapi_r_plugin"
crate-type = ["dylib"]

  • Will take investment of time and mental energy
  • Can build a Rust .so that links to a C .so

lessons gained so far

firstyear@redhat.com

http://bit.ly/2iBdEc0

it's too easy, so I fear ...

 

 

 

  • Make the types match:
    • How can we import all the integer defines?
    • How can we use C structs and members?
    • How can we call C functions passing rust data?
    • Expose rust functions for C to call at initialisation?
pub extern fn awesome_fun(slapi_pblock: ???) -> isize {
    ...
}

pub extern fn slapi_r_plugin_init_fn(slapi_pblock: ???) -> isize {
    let value_ptr ???
    // from slapi-plugin.h SLAPI_PLUGIN_PRE_BIND_FN:
    slapi_pblock_set(slapi_pblock, ??? , value_ptr);
}

redefine reality

 

 

 

  • Tedious and boring.
/// A successful operation. Most Directory Server fuctions expect this.
pub const LDAP_SUCCESS: isize = 0;

/// PBlock constant to retrieve the current operation
pub const SLAPI_OPERATION: isize = 132;

/// PBlock constant for retrieving the next entry of an entry result list.
pub const SLAPI_SEARCH_RESULT_ENTRY: isize = 194;
/// PBlock constant for registering the close function for a plugin.
pub const SLAPI_PLUGIN_CLOSE_FN: isize = 210;
/// PBlock constant for registering the start function for a plugin.
pub const SLAPI_PLUGIN_START_FN: isize = 212;

/// PBlock constant for registering a pre BIND operation.
pub const SLAPI_PLUGIN_PRE_BIND_FN: isize = 401;
/// PBlock constant for registering a pre UNBIND operation.
pub const SLAPI_PLUGIN_PRE_UNBIND_FN: isize = 402;

step it up a gear




  • Just use the pointer as a cvoid, and rely on C functions to access members.
  • Use our constants
pub extern fn awesome_fun(slapi_pblock: *mut libc::c_void) -> isize {
    ...
}

pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
    let value_ptr ???
    // from slapi-plugin.h SLAPI_PLUGIN_PRE_BIND_FN:
    slapi_pblock_set(slapi_pblock, SLAPI_PLUGIN_PRE_BIND_FN, value_ptr);
}
  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants or structs.

lessons gained so far

firstyear@redhat.com

http://bit.ly/2iBdEc0

tell me who it is or where!

 

 

 

dn: cn=MemberOf Plugin,cn=plugins,cn=config
objectClass: top
objectClass: nsSlapdPlugin
objectClass: extensibleObject
cn: MemberOf Plugin
nsslapd-pluginPath: libmemberof-plugin
nsslapd-pluginInitfunc: memberof_postop_init
nsslapd-pluginType: betxnpostoperation
nsslapd-pluginEnabled: off
nsslapd-plugin-depends-on-type: database
memberofgroupattr: member
memberofattr: memberOf
nsslapd-pluginId: none
nsslapd-pluginVersion: none
nsslapd-pluginVendor: none
nsslapd-pluginDescription: none

tell me who it is or where!

 

 

 

readelf -Ws libmemberof-plugin.so
...
    54: 000000000000d2b2    22 FUNC    LOCAL  DEFAULT   12 memberof_set_plugin_id
    55: 00000000000086ce  1394 FUNC    LOCAL  DEFAULT   12 memberof_postop_del
    56: 0000000000009bed  2324 FUNC    LOCAL  DEFAULT   12 memberof_postop_modrdn
    57: 000000000000ab82  2185 FUNC    LOCAL  DEFAULT   12 memberof_postop_modify
    58: 000000000000b40b  1376 FUNC    LOCAL  DEFAULT   12 memberof_postop_add
    59: 0000000000008006  1292 FUNC    LOCAL  DEFAULT   12 memberof_postop_start
    60: 0000000000008512   216 FUNC    LOCAL  DEFAULT   12 memberof_postop_close
    61: 0000000000007f1e   232 FUNC    LOCAL  DEFAULT   12 memberof_internal_postop_init
    62: 0000000000007e8b   147 FUNC    LOCAL  DEFAULT   12 memberof_postop_init
...

through all my tricks and traps

 

 

 

...   23 _ZN9hellorust15HellorustPlugin8pre_bind15__STATIC_FMTSTR17h6a652c41884b47d6E
...   23 _ZN9hellorust15HellorustPlugin10pre_unbind15__STATIC_FMTSTR17h144170ee22883289E
...   23 _ZN9hellorust15HellorustPlugin10pre_modrdn15__STATIC_FMTSTR17h7ee488ed766841edE
...   23 _ZN9hellorust15HellorustPlugin9pre_entry15__STATIC_FMTSTR17h24e84107c28088e3E
...   23 _ZN9hellorust15HellorustPlugin10pre_search15__STATIC_FMTSTR17h4adb1821cb0bb7f2E
...   23 _ZN9hellorust15HellorustPlugin10pre_delete15__STATIC_FMTSTR17ha8d8eccdb35aabfbE
...   23 _ZN9hellorust15HellorustPlugin7pre_add15__STATIC_FMTSTR17ha7058ed384a4a7e7E
...   23 _ZN9hellorust15HellorustPlugin10pre_result15__STATIC_FMTSTR17hd5e1882f7a5e7d5eE
...   23 _ZN9hellorust15HellorustPlugin11pre_compare15__STATIC_FMTSTR17h3df49ff3c17c6c29E
...   23 _ZN9hellorust15HellorustPlugin10pre_modify15__STATIC_FMTSTR17h605fa2161914c01dE
...   23 _ZN9hellorust15HellorustPlugin9pre_extop15__STATIC_FMTSTR17h38658b188161e3d9E

through all my tricks and traps

 

 

 

  • no_mangle the exported name for the initial callback.
pub extern fn awesome_fun(slapi_pblock: *mut libc::c_void) -> isize {
    ...
}

#[no_mangle]
pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
    let value_ptr ???
    // from slapi-plugin.h SLAPI_PLUGIN_PRE_BIND_FN:
    slapi_pblock_set(slapi_pblock, SLAPI_PLUGIN_PRE_BIND_FN, value_ptr);
}

through all my tricks and traps

 

 

 

...   23 _ZN9hellorust15HellorustPlugin8pre_bind15__STATIC_FMTSTR17h6a652c41884b47d6E
...   23 _ZN9hellorust15HellorustPlugin10pre_unbind15__STATIC_FMTSTR17h144170ee22883289E
...   23 _ZN9hellorust15HellorustPlugin10pre_modrdn15__STATIC_FMTSTR17h7ee488ed766841edE
...   23 _ZN9hellorust15HellorustPlugin9pre_entry15__STATIC_FMTSTR17h24e84107c28088e3E
...   23 _ZN9hellorust15HellorustPlugin10pre_search15__STATIC_FMTSTR17h4adb1821cb0bb7f2E
...   23 _ZN9hellorust15HellorustPlugin10pre_delete15__STATIC_FMTSTR17ha8d8eccdb35aabfbE
...   23 _ZN9hellorust15HellorustPlugin7pre_add15__STATIC_FMTSTR17ha7058ed384a4a7e7E
...   23 _ZN9hellorust15HellorustPlugin10pre_result15__STATIC_FMTSTR17hd5e1882f7a5e7d5eE
...   23 _ZN9hellorust15HellorustPlugin11pre_compare15__STATIC_FMTSTR17h3df49ff3c17c6c29E
...   23 _ZN9hellorust15HellorustPlugin10pre_modify15__STATIC_FMTSTR17h605fa2161914c01dE
...   23 _ZN9hellorust15HellorustPlugin9pre_extop15__STATIC_FMTSTR17h38658b188161e3d9E
...   12 slapi_r_plugin_init_fn
  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants or structs.
  • #[no_mangle] on selected exported functions (entry points).

lessons gained so far

firstyear@redhat.com

http://bit.ly/2iBdEc0

crazy spellcasting

 

 

 

extern {
    fn slapi_pblock_set(pb: *const libc::c_void, arg: isize, value: *const libc::c_void);
}

pub extern fn awesome_fun(slapi_pblock: *mut libc::c_void) -> isize {
    ...
}

#[no_mangle]
pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
    let value_ptr *const libc::c_void = awesome_fun as *const libc::c_void; 
    unsafe {
        slapi_pblock_set(slapi_pblock, SLAPI_PLUGIN_PRE_BIND_FN, value_ptr);
    }
}
  • I've learned this spell, it's really neat, it lets us call back to rust without a cheat.

crazy spellcasting

 

 

 

extern {
    fn slapi_pblock_set(pb: *const libc::c_void, arg: isize, value: *const libc::c_void);
}

pub extern fn awesome_fun(slapi_pblock: *mut libc::c_void) -> isize {
    ...
}

#[no_mangle]
pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
    let value_ptr *const libc::c_void = awesome_fun as *const libc::c_void; 
    unsafe {
        slapi_pblock_set(slapi_pblock, SLAPI_PLUGIN_PRE_BIND_FN, value_ptr);
    }
}
  • I've learned this spell, it's really neat, it lets us call back to rust without a cheat.

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we must wrap this!

firstyear@redhat.com

http://bit.ly/2iBdEc0

gdb with rust!?

 

 

 

(gdb) set args -d 0 -D /opt/dirsrv/etc/dirsrv/slapd-localhost
(gdb) br hellorust/src/lib.rs:215
(gdb) run
Starting program: /opt/dirsrv/sbin/ns-slapd -d 0 -D /opt/dirsrv/etc/dirsrv/slapd-localhost
Breakpoint 1, hellorust::{{impl}}::init<slapi_r_plugin::pblock::Slapi_R_PBlock> (pb=...)
    at /home/william/development/389ds/ds_rust/plugins/hellorust/src/lib.rs:215
215	    fn init<T: Slapi_PBlock_Init_V3>( pb: T ) -> Result<(), PluginRegistrationError> {


(gdb) bt
#0  hellorust::{{impl}}::init<slapi_r_plugin::pblock::Slapi_R_PBlock> (pb=...)
    at /home/william/development/389ds/ds_rust/plugins/hellorust/src/lib.rs:215
#1  0x00007fffe96209de in hellorust::slapi_r_plugin_init_fn (slapi_pblock=0x7fffffffd0e0)
    at /home/william/development/389ds/ds_rust/plugins/hellorust/src/lib.rs:266
#2  0x00007ffff4a4a68d in plugin_setup (plugin_entry=0x6020001f6840, group=0x0, p_initfunc=0x0, add_entry=1, returntext=0x7fffffffd960 "")
    at /home/william/development/389ds/ds/ldap/servers/slapd/plugin.c:3097
#3  0x000000000041a01a in load_plugin_entry (pb=0x7fffffffdd10, e=0x6020001f6840, ignored=0x0, returncode=0x7fffffffd740, returntext=0x7fffffffd960 "", arg=0x0)
    at /home/william/development/389ds/ds/ldap/servers/slapd/configdse.c:350
#4  0x00007ffff4989387 in dse_call_callback (pdse=0x600e00012f50, pb=0x7fffffffdd10, operation=256, flags=1, entryBefore=0x6020001f6840, entryAfter=0x0, returncode=0x7fffffffd740, returntext=0x7fffffffd960 "")

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we must wrap this!
  • We can use GDB like normal (with some tricks)

firstyear@redhat.com

http://bit.ly/2iBdEc0

it's alive!

 

 

 

[04/Dec/2016:14:53:17.012801278 +1000] - NOTICE - plugins::hellorust - Hello rust!
[04/Dec/2016:14:53:17.404802344 +1000] - INFO - main - 389-Directory/1.3.6.1 B2016.339.357 starting up
[04/Dec/2016:14:53:17.774854540 +1000] - NOTICE - plugins::hellorust - Calling the hellorust start callback 

"It's alive, we've taken off,

    I really hope that none of you scoff!"

we can do better!

 

 

 

  • Proof of concept
  • Abstract!
  • Better design
  • Minimise calling to C, safety barriers.
extern {
    fn slapi_pblock_set(pb: *const libc::c_void, arg: isize, value: *const libc::c_void);
}

pub extern fn awesome_fun(slapi_pblock: *mut libc::c_void) -> isize {
    ...
}

#[no_mangle]
pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
    let value_ptr: *const libc::c_void = awesome_fun as *const libc::c_void;
    unsafe {
        slapi_pblock_set(slapi_pblock, SLAPI_PLUGIN_PRE_BIND_FN, value_ptr);
    }
}

what do we want?

 

 

 

  • Clean rust types.
  • Traits to enforce plugin behaviour.
  • No unsafe of c_void exposed to developers.
impl Slapi_Plugin_V3 for HellorustPlugin {

    ...

    fn start<T: Slapi_PBlock_V3>( _: &T ) -> Result<(), PluginOperationError> {
        slapi_r_log_error_plugin!(LogLevel::INFO, SUBSYSTEM, 
            format!("Calling the hellorust start callback \n") );
        Ok(())
    }
}
extern {
    fn slapi_pblock_get(pb: *const libc::c_void, arg: isize, value: *const libc::c_void);
    fn slapi_pblock_set(pb: *const libc::c_void, arg: isize, value: *const libc::c_void);
}

impl Slapi_R_PBlock {
    fn _set_pb_fn_ptr(&self, pblock_type: isize, ptr: extern fn(*const libc::c_void) -> isize) {
        let value_ptr: *const libc::c_void = ptr as *const libc::c_void;
        unsafe {
            slapi_pblock_set(self.slapi_pblock, pblock_type, value_ptr);
        }
    }

    fn set_plugin_start_fn(&self, func: extern fn(*const libc::c_void) -> isize) {
        self._set_pb_fn_ptr(SLAPI_PLUGIN_START_FN, func)
    }

    fn set_plugin_pre_bind_fn(&self, func: extern fn(*const libc::c_void) -> isize) {
        self._set_pb_fn_ptr(SLAPI_PLUGIN_PRE_BIND_FN, func)
    }
}

underneath this shield I'll hide!

extern fn register(ptr: fn(&Slapi_R_PBlock) -> Result<(), PluginOperationError>) {
...
    pb.set_plugin_pre_bind_fn(ptr)
...

extern fn slapi_r_plugin_pre_bind_cb(slapi_pblock: *const libc::c_void) -> isize {

    let pb: Slapi_R_PBlock = Slapi_R_PBlock::build(slapi_pblock);

    let func = match fn_ptrs.pre_bind {
        Some(f) => f,
        None => return PluginOperationError::Unknown.as_ds_isize(),
    };

    let result: Result<(), PluginOperationError> = func(&pb);
    // Unwrap the result, and give it to DS in a way it can understand.
    match result {
        Ok(_) => constants::LDAP_SUCCESS,
        Err(err) => err.as_ds_isize(),
    }
}

underneath this shield I'll hide!

impl Slapi_PBlock_V3 for Slapi_R_PBlock {
    /// This will retrieve the current slapi_operation if one is present
    fn get_operation(&self) -> Option<Slapi_R_Operation> {
        match self._get_void_ptr(SLAPI_OPERATION) {
            Some(p) => Some(Slapi_R_Operation::new(p)),
            None => None,
        }
    }
}

impl Slapi_R_Operation {
    /// Build a new Slapi_R_Operation from the pointer to the slapi_operation
    /// you should not need to do this yourself, you should be getting the
    /// Slapi_R_Operation from the pblock methods.
    pub fn new(slapi_operation: *const libc::c_void) -> Slapi_R_Operation {
        Slapi_R_Operation {
            slapi_operation: slapi_operation
        }
    }
}

you're back to try...

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we must wrap this!
  • We can use GDB like normal (with some tricks)
  • "Functional C" that takes context pointers.

firstyear@redhat.com

http://bit.ly/2iBdEc0
/// This macro is imported by plugins to wrap their rust init functions.
/// This allows complete transparency to C types to the rust plugin.
#[macro_export]
macro_rules! slapi_r_plugin_init {
    ( $plugin_type:ident ) => (
        extern crate libc;
        use slapi_r_plugin::pblock::Slapi_R_PBlock;
        /// A static C function exported from the .so that Directory Server can
        /// find to complete plugin registration.
        #[no_mangle]
        pub extern fn slapi_r_plugin_init_fn(slapi_pblock: *mut libc::c_void) -> isize {
            let pb: Slapi_R_PBlock = Slapi_R_PBlock::build(slapi_pblock);
            match <$plugin_type as Slapi_Plugin_V3>::init(pb) {
                Ok(_) => constants::LDAP_SUCCESS,
                Err(e) => return e.as_ds_isize(),
            }
        }
    );
}

// This is the magic that links a static no_mangle fn into the .so, and the rust
// init types
slapi_r_plugin_init!(RoReplicaPlugin);

see it go through this!

 

 

 

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we should wrap this!
  • We can use GDB like normal (with some tricks)
  • "Functional C" that takes context pointers.
  • Rust macros are type checked and can hide ugly details.

firstyear@redhat.com

http://bit.ly/2iBdEc0
impl RoReplicaPlugin {
    fn intercept_operation<T: Slapi_PBlock_V3>( pb: &T ) -> Result<(), PluginOperationError> {
        let operation = pb.get_operation();
        match operation {
            Some(op) => {
                if op.is_replicated() || op.is_internal() {
                    Ok(())
                } else {
                    pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "Can not modify readonly database.");
                    Err(PluginOperationError::UnwillingToPerform)
                }
            }
            None => {
                pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "An error occured processing this operation.");
                Err(PluginOperationError::Unknown)
            }
        }
    }
}

impl Slapi_Plugin_V3 for RoReplicaPlugin {
    fn init<T: Slapi_PBlock_Init_V3>( pb: T ) -> Result<(), PluginRegistrationError> {
        let mut p_manager: Slapi_R_Plugin_Manager = Slapi_R_Plugin_Manager::new();
        p_manager.functions.pre_modify = Some(RoReplicaPlugin::intercept_operation);
    }
}

slapi_r_plugin_init!(RoReplicaPlugin);
impl RoReplicaPlugin {
    fn intercept_operation<T: Slapi_PBlock_V3>( pb: &T ) -> Result<(), PluginOperationError> {
        let operation = pb.get_operation();
        match operation {
            Some(op) => {
                if op.is_replicated() || op.is_internal() {
                    Ok(())
                } else {
                    pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "Can not modify readonly database.");
                    Err(PluginOperationError::UnwillingToPerform)
                }
            }
            None => {
                pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "An error occured processing this operation.");
                Err(PluginOperationError::Unknown)
            }
        }
    }
}

impl Slapi_Plugin_V3 for RoReplicaPlugin {
    fn init<T: Slapi_PBlock_Init_V3>( pb: T ) -> Result<(), PluginRegistrationError> {
        let mut p_manager: Slapi_R_Plugin_Manager = Slapi_R_Plugin_Manager::new();
        p_manager.functions.pre_modify = Some(RoReplicaPlugin::intercept_operation);
    }
}

slapi_r_plugin_init!(RoReplicaPlugin);
impl RoReplicaPlugin {
    fn intercept_operation<T: Slapi_PBlock_V3>( pb: &T ) -> Result<(), PluginOperationError> {
        let operation = pb.get_operation();
        match operation {
            Some(op) => {
                if op.is_replicated() || op.is_internal() {
                    Ok(())
                } else {
                    pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "Can not modify readonly database.");
                    Err(PluginOperationError::UnwillingToPerform)
                }
            }
            None => {
                pb.send_ldap_result(PluginOperationError::UnwillingToPerform, "An error occured processing this operation.");
                Err(PluginOperationError::Unknown)
            }
        }
    }
}

impl Slapi_Plugin_V3 for RoReplicaPlugin {
    fn init<T: Slapi_PBlock_Init_V3>( pb: T ) -> Result<(), PluginRegistrationError> {
        let mut p_manager: Slapi_R_Plugin_Manager = Slapi_R_Plugin_Manager::new();
        p_manager.functions.pre_modify = Some(RoReplicaPlugin::intercept_operation);
    }
}

slapi_r_plugin_init!(RoReplicaPlugin);

autotools ...

 

 

 

  • Cargo can't "install" extra data or objects
  • Autotools is the defacto build system of OSS
  • Can we combine the two?

autotools ...

 

 

 

// configure.ac

AC_PROG_CC_C99
AM_PROG_CC_C_O

AC_CHECK_PROG(CARGO, [cargo], [yes], [no])
AC_CHECK_PROG(RUSTC, [rustc], [yes], [no])

AS_IF([test "$CARGO" != "yes" -o "$RUSTC" != "yes"], [
  AC_MSG_FAILURE("Rust based plugins cannot be built $CARGO $RUSTC")
])

LT_INIT

autotools ...

 

 

 

// Makefile.am

helloexampledir = $(exampledir)/hellorust/
helloexample_DATA = plugins/hellorust/enable.ldif

# Hello Rust!
am_libhellorust_la_OBJECTS = hellorust.o
libhellorust_la_SOURCES = ""
libhellorust_la_LIBADD = libhellorust.a
libhellorust_la_LDFLAGS = -release $(hellorelease)

hellorust.o:
	cd $(srcdir)/plugins/hellorust; cargo rustc -- --emit obj=$(abs_builddir)/$@

libhellorust.a:
	cd $(srcdir)/plugins/hellorust; cargo rustc -- --crate-type=staticlib --emit link -o $(abs_builddir)/$@

clean-local:
	cd $(srcdir)/plugins/hellorust; cargo clean

autotools ...

 

 

 

// Makefile.am

helloexampledir = $(exampledir)/hellorust/
helloexample_DATA = plugins/hellorust/enable.ldif

# Hello Rust!
am_libhellorust_la_OBJECTS = hellorust.o
libhellorust_la_SOURCES = ""
libhellorust_la_LIBADD = libhellorust.a
libhellorust_la_LDFLAGS = -release $(hellorelease)

hellorust.o:
	cd $(srcdir)/plugins/hellorust; cargo rustc -- --emit obj=$(abs_builddir)/$@

libhellorust.a:
	cd $(srcdir)/plugins/hellorust; cargo rustc -- --crate-type=staticlib --emit link -o $(abs_builddir)/$@

clean-local:
	cd $(srcdir)/plugins/hellorust; cargo clean

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we must wrap this!
  • We can use GDB like normal (with some tricks)
  • "Functional C" that takes context pointers.
  • Rust macros are type checked and can hide ugly details.
  • Autotools works with cargo and Rust
    • Helps us achieve more advanced build scenarios

firstyear@redhat.com

http://bit.ly/2iBdEc0

Demo time ...

lessons gained so far

  • Will take investment of time and mental energy
  • Can build a rust .so that links to a C .so
  • Can not use C headers in Rust - for constants *or* structs.
  • #[no_mangle] on selected exported functions (entry points).
  • Calling to C is unsafe: So we must wrap this!
  • We can use GDB like normal (with some tricks)
  • "Functional C" that takes context pointers.
  • Rust macros are type checked and can hide ugly details.
  • Autotools works with cargo and Rust
    • Helps us achieve more advanced build scenarios

RUST WITH C works!

firstyear@redhat.com

http://bit.ly/2iBdEc0

What next?

  • Write bindings to libraries like NSPR4 and NSS3.
  • Rewrite existing critical paths in Rust (password hashing etc).
  • Rust ASN.1 parsing for BER.
  • Rust LDAP libraries.
  • Write new plugins in Rust.

https://github.com/Firstyear/ds_rust

  • Bindgen has improved - use it!
  • Anonymous structs instead of c_void pointers.

Thanks to:

Josh Driver

keeper@blackhats.net.au

GETTING INTO THE RUSTY BUCKET

Lessons from Integrating Rust with Existing C

 

WILLIAM BROWN

Software Engineer - RHDS

firstyear@redhat.com

Getting into the Rusty Bucket

By William Brown

Private

Getting into the Rusty Bucket

Rust is a modern language developed by Mozilla pursuing the trifecta: Safety, Speed, and Concurrency. With such promise for the future, how can we use this now? We are not always in the beautiful open green fields; we must contend with our existing grunty applications! 389 Directory Server is based on code now more than 20 years old. We cannot throw this out and replace it, but we want the benefits Rust gives us - especially for authentication and security critical code. We will explore the challenges of security in engineering, and behaviours of the modern programmer. I will discuss why we have spiraled down a mountain of failure as an engineering discipline - and why we need tools like Rust to validate our work. I will show faults in Directory Server that could have been prevented with safe language techniques. Finally, I will explore the Directory Server plugin interface, where we have the ability to provide pure Rust plugins, allowing safe, fast extensibility to a core piece of systems authentication. This will explore the challenges to build it, and the design patterns needed to make sure that the rewrite of the application in Rust is possible, today.