Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
linux bug workaround, never unload native modules
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay committed Sep 23, 2018
1 parent 6f583da commit cc320fb
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 23 deletions.
3 changes: 0 additions & 3 deletions src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,9 @@ impl Bank {
// TODO: the runtime should be checking read/write access to memory
// we are trusting the hard coded contracts not to clobber or allocate
BudgetState::process_transaction(&tx, accounts)
<<<<<<< HEAD
} else if StorageProgram::check_id(&tx.program_id) {
StorageProgram::process_transaction(&tx, accounts)
=======
} else if self.loaded_contract(&tx, accounts) {
>>>>>>> Integration of native dynamic programs
} else {
return Err(BankError::UnknownContractId(tx.program_id));
}
Expand Down
72 changes: 60 additions & 12 deletions src/dynamic_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ extern crate bincode;
extern crate generic_array;

use bank::Account;
use libloading::{Library, Symbol};
use libc;
use libloading;
use signature::Pubkey;
use std::path::PathBuf;

Expand Down Expand Up @@ -55,7 +56,10 @@ pub enum DynamicProgram {
/// * Transaction::keys[0..] - program dependent
/// * name - name of the program, translated to a file path of the program module
/// * userdata - program specific user data
Native { name: String, library: Library },
Native {
name: String,
library: libloading::Library,
},
/// Bpf program
/// * Transaction::keys[0..] - program dependent
/// * TODO BPF specific stuff
Expand All @@ -66,23 +70,28 @@ pub enum DynamicProgram {
impl DynamicProgram {
pub fn new(name: String) -> Self {
// TODO determine what kind of module to load

// create native program
println!("loading {}", name);
let path = create_library_path(&name);
let library = Library::new(&path).expect("Failed to load library");
// TODO linux tls bug can cause crash on dlclose, workaround by never unloading
let os_lib =
libloading::os::unix::Library::open(Some(path), libc::RTLD_NODELETE | libc::RTLD_NOW)
.unwrap();
let library = libloading::Library::from(os_lib);
DynamicProgram::Native { name, library }
}

pub fn call(&self, infos: &mut Vec<KeyedAccount>, data: &[u8]) {
match self {
DynamicProgram::Native { name, library } => unsafe {
let entrypoint: Symbol<Entrypoint> = match library.get(ENTRYPOINT.as_bytes()) {
Ok(s) => s,
Err(e) => panic!(
"{:?} Unable to find {:?} in program {}",
e, ENTRYPOINT, name
),
};
let entrypoint: libloading::Symbol<Entrypoint> =
match library.get(ENTRYPOINT.as_bytes()) {
Ok(s) => s,
Err(e) => panic!(
"{:?} Unable to find {:?} in program {}",
e, ENTRYPOINT, name
),
};
entrypoint(infos, data);
},
DynamicProgram::Bpf { .. } => {
Expand All @@ -100,9 +109,10 @@ mod tests {
use bincode::serialize;
use signature::Pubkey;
use std::path::Path;
use std::thread;

#[test]
fn test_create_library_path_1() {
fn test_create_library_path() {
let path = create_library_path("noop");
assert_eq!(true, Path::new(&path).exists());
let path = create_library_path("print");
Expand Down Expand Up @@ -198,6 +208,44 @@ mod tests {
assert_eq!(1, accounts[1].tokens);
}

#[test]
fn test_program_move_funds_succes_many_threads() {
let num_threads = 42; // number of threads to spawn
let num_iters = 100; // number of iterations of test in each thread
let mut threads = Vec::new();
for _t in 0..num_threads {
threads.push(thread::spawn(move || {
for _i in 0..num_iters {
{
let tokens: i64 = 100;
let data: Vec<u8> = serialize(&tokens).unwrap();
let keys = vec![Pubkey::default(); 2];
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].tokens = 100;
accounts[1].tokens = 1;

{
let mut infos: Vec<_> = (&keys)
.into_iter()
.zip(&mut accounts)
.map(|(key, account)| KeyedAccount { key, account })
.collect();

let dp = DynamicProgram::new("move_funds".to_string());
dp.call(&mut infos, &data);
}
assert_eq!(0, accounts[0].tokens);
assert_eq!(101, accounts[1].tokens);
}
}
}));
}

for thread in threads {
thread.join().unwrap();
}
}

// TODO add more tests to validate the Userdata and Account data is
// moving across the boundary correctly

Expand Down
76 changes: 68 additions & 8 deletions src/system_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ impl SystemProgram {
accounts[1].tokens += tokens;
}
SystemProgram::Load { program_id, name } => {
println!("Load program: {}", name);
let mut hashmap = loaded_programs.write().unwrap();
hashmap.insert(program_id, DynamicProgram::new(name));
}
Expand All @@ -105,6 +104,7 @@ mod test {
use signature::{Keypair, KeypairUtil, Pubkey};
use std::collections::HashMap;
use std::sync::RwLock;
use std::thread;
use system_program::SystemProgram;
use transaction::Transaction;

Expand Down Expand Up @@ -240,27 +240,88 @@ mod test {
accounts[0].tokens = 100;
accounts[1].tokens = 1;
let tokens: i64 = 100;
let _data: Vec<u8> = serialize(&tokens).unwrap();
let data: Vec<u8> = serialize(&tokens).unwrap();
{
let hash = loaded_programs.write().unwrap();
match hash.get(&program_id) {
Some(_dp) => {
let mut _infos: Vec<_> = (&keys)
Some(dp) => {
let mut infos: Vec<_> = (&keys)
.into_iter()
.zip(&mut accounts)
.map(|(key, account)| KeyedAccount { key, account })
.collect();

//dp.call(&mut infos, &data);
dp.call(&mut infos, &data);
}
None => panic!("failed to find program in hash"),
}
}
//assert_eq!(0, accounts[0].tokens);
//assert_eq!(101, accounts[1].tokens);
assert_eq!(0, accounts[0].tokens);
assert_eq!(101, accounts[1].tokens);
}
}
#[test]
fn test_load_call_many_threads() {
let num_threads = 42;
let num_iters = 100;
let mut threads = Vec::new();
for _t in 0..num_threads {
threads.push(thread::spawn(move || {
let _tid = thread::current().id();
for _i in 0..num_iters {
// first load the program
let loaded_programs = RwLock::new(HashMap::new());
{
let from = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
let program_id = Pubkey::default(); // same program id for both
let tx = Transaction::system_load(
&from,
Hash::default(),
0,
program_id,
"move_funds".to_string(),
);

SystemProgram::process_transaction(&tx, &mut accounts, &loaded_programs);
}
// then call the program
{
let program_id = Pubkey::default(); // same program id for both
let keys = vec![Pubkey::default(), Pubkey::default()];
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].tokens = 100;
accounts[1].tokens = 1;
let tokens: i64 = 100;
let data: Vec<u8> = serialize(&tokens).unwrap();
{
let hash = loaded_programs.write().unwrap();
match hash.get(&program_id) {
Some(dp) => {
let mut infos: Vec<_> = (&keys)
.into_iter()
.zip(&mut accounts)
.map(|(key, account)| KeyedAccount { key, account })
.collect();

dp.call(&mut infos, &data);
}
None => panic!("failed to find program in hash"),
}
}
assert_eq!(0, accounts[0].tokens);
assert_eq!(101, accounts[1].tokens);
}
}
}));
}

for thread in threads {
thread.join().unwrap();
}

}
#[test]
fn test_create_assign() {
let from = Keypair::new();
let program = Keypair::new();
Expand All @@ -282,7 +343,6 @@ mod test {
assert_eq!(accounts[0].tokens, 0);
assert_eq!(accounts[1].tokens, 1);
}

/// Detect binary changes in the serialized program userdata, which could have a downstream
/// affect on SDKs and DApps
#[test]
Expand Down

0 comments on commit cc320fb

Please sign in to comment.