Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve ffi safety #247

Merged
merged 19 commits into from
Apr 28, 2022
Merged

fix: improve ffi safety #247

merged 19 commits into from
Apr 28, 2022

Conversation

dignifiedquire
Copy link
Contributor

@dignifiedquire dignifiedquire commented Mar 18, 2022

  • *mut ptr to indicate ownership
  • Boxed slices instead of vecs for safe transfers
  • expanded checks for null ptrs
  • refactor to use no more CStrings
  • improve api and safety
  • C -> go bindings: remove c-for-go in favor of manual bindings
  • rust -> C bindings: use safer_ffi instead of bindgen
  • check for enum values passed in the ffi interface

Closes filecoin-project/rust-fil-ffi-toolkit#9, #231

TODOs

  • update low level go bindings
  • update high level go bindings
  • green CI
  • Apply CR
  • Cleanup pass
  • Rebase on master with fvm integration
  • Update fvm bindings (rust)
  • Update fvm bindings(go)

Depends on filecoin-project/rust-fil-ffi-toolkit#11

rust/src/bls/api.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
@Stebalien
Copy link
Member

I think we're also missing some null checks inside fil_verify_aggregate_seal_proof.

rust/src/proofs/api.rs Outdated Show resolved Hide resolved
rust/src/proofs/api.rs Show resolved Hide resolved
rust/src/proofs/api.rs Outdated Show resolved Hide resolved
Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments, but otherwise this looks good to go.

Comment on lines 39 to 40
let devices: Vec<fil_Bytes> = devices.iter().map(|d| d.name().into()).collect();
let devices: fil_Array<fil_Bytes> = devices.into();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could implement FromIterator for fil_Array.

rust/src/proofs/api.rs Outdated Show resolved Hide resolved
rust/src/proofs/api.rs Outdated Show resolved Hide resolved
rust/src/proofs/api.rs Outdated Show resolved Hide resolved
#[no_mangle]
pub unsafe extern "C" fn fil_generate_fallback_sector_challenges(
registered_proof: fil_RegisteredPoStProof,
randomness: fil_32ByteArray,
sector_ids_ptr: *const u64,
sector_ids_len: libc::size_t,
sector_ids: &fil_Array<u64>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

future note: with a repr(transparent), we may be able to use a fil_Array<SectorId> here. But that's a future change.

rust/src/proofs/api.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
rust/src/util/types.rs Outdated Show resolved Hide resolved
@dignifiedquire dignifiedquire marked this pull request as draft March 23, 2022 09:14
rust/src/bls/api.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@magik6k magik6k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not seeing anything very wrong here, but I feel like this probably deserves at least one more look from someone who in more into rust.

Comment on lines 172 to 175
/// * `messages_sizes_ptr` - pointer to an array containing the lengths of the messages
/// * `messages_len` - length of the two messages arrays
/// * `flattened_public_keys_ptr` - pointer to a byte array containing public keys
/// * `flattened_public_keys_len` - length of the array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// * `messages_sizes_ptr` - pointer to an array containing the lengths of the messages
/// * `messages_len` - length of the two messages arrays
/// * `flattened_public_keys_ptr` - pointer to a byte array containing public keys
/// * `flattened_public_keys_len` - length of the array
/// * `messages_sizes` - pointer to an array containing the lengths of the messages
/// * `flattened_public_keys` - pointer to a byte array containing public keys

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// * `messages_sizes_ptr` - pointer to an array containing the lengths of the messages
/// * `messages_len` - length of the two messages arrays
/// * `flattened_public_keys_ptr` - pointer to a byte array containing public keys
/// * `flattened_public_keys_len` - length of the array
/// * `signature` - byte array containing the signature
/// * `messages` - array containing the pointers to the messages
/// * `messages_sizes` - array containing the lengths of the messages
/// * `flattened_public_keys` - byte array containing public keys

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some ideas/comments. Honestly, the unsafety of things like ptr.Slice() is probably fine given the context.

cgo/types.go Outdated Show resolved Hide resolved
cgo/types.go Outdated
Comment on lines 18 to 20
func (ptr SliceBoxedUint8) Slice() []byte {
return unsafe.Slice((*byte)(ptr.ptr), int(ptr.len))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call this something like UnsafeSlice (and do we actually need it?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to make it super clear that the underlying slice may be freed.

cgo/types.go Outdated Show resolved Hide resolved
cgo/types.go Outdated Show resolved Hide resolved
cgo/proofs.go Show resolved Hide resolved
cgo/types.go Outdated Show resolved Hide resolved
Copy link
Contributor

@vmx vmx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only small things and some of them that could/should be done on subsequent PRs.

@@ -12,32 +10,36 @@ use rand::rngs::OsRng;
use rand::SeedableRng;
use rand_chacha::ChaChaRng;
use rayon::prelude::*;
use safer_ffi::prelude::*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of star imports. When you're not familiar with safer-ffi, I'd wonder "where does that derive_ReprC come from? Is that a new builtin I haven't heard of?" when reading the source. As this PR is already huge, I'd be happy to change it myself in a later PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is especially confusing because we have an str module in here.

rust/src/bls/api.rs Outdated Show resolved Hide resolved
Comment on lines 172 to 175
/// * `messages_sizes_ptr` - pointer to an array containing the lengths of the messages
/// * `messages_len` - length of the two messages arrays
/// * `flattened_public_keys_ptr` - pointer to a byte array containing public keys
/// * `flattened_public_keys_len` - length of the array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// * `messages_sizes_ptr` - pointer to an array containing the lengths of the messages
/// * `messages_len` - length of the two messages arrays
/// * `flattened_public_keys_ptr` - pointer to a byte array containing public keys
/// * `flattened_public_keys_len` - length of the array
/// * `signature` - byte array containing the signature
/// * `messages` - array containing the pointers to the messages
/// * `messages_sizes` - array containing the lengths of the messages
/// * `flattened_public_keys` - byte array containing public keys

rust/src/bls/api.rs Outdated Show resolved Hide resolved
rust/src/bls/api.rs Outdated Show resolved Hide resolved
rust/src/bls/api.rs Outdated Show resolved Hide resolved
rust/src/bls/api.rs Outdated Show resolved Hide resolved
@dignifiedquire dignifiedquire marked this pull request as ready for review March 30, 2022 12:47
@raulk raulk requested review from vmx, Stebalien, Kubuxu and magik6k March 31, 2022 16:08
Copy link
Contributor

@vmx vmx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not a fan of glob imports, but other than that it looks good. I've only reviewed the Rust part of it btw.

@dignifiedquire dignifiedquire force-pushed the safer-ffi branch 2 times, most recently from 89f57b6 to 55b02e4 Compare April 12, 2022 18:37
Copy link
Contributor

@vmx vmx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only two minor, non blocking things.

I've only reviewed the Rust side of things. Great work! This is really a massive improvement and also a huge amount of tiresome work. Wow.

rust/src/fvm/blockstore/cgo.rs Show resolved Hide resolved
rust/src/fvm/machine.rs Show resolved Hide resolved
@Kubuxu
Copy link

Kubuxu commented Apr 13, 2022

I will restate the question regarding changes to function signatures. filecoin-ffi is used by multiple downstream projects, and changes to function signatures will have painful side-effects.

cgo/helpers.go Outdated
if len == 0 {
// can't take element 0 of an empty slice
return SliceRefUint8{
ptr: (*C.uint8_t)(unsafe.Pointer(&goBytes)),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would feel better if we had a global uint8 variable and were taking pointer to it instead of the slice.
Reinterpreting the slice pointer to C.uint8_t feels wrong.

Copy link

@Kubuxu Kubuxu Apr 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it would make the code more verbose (all different types).
Alternative would be the pointer to be nil which AFAIK is acceptable from Go's perspective.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the concern/solution I am afraid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kubuxu can you re-explain this if it's still a concern?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explained in person, fix just pushed

@Kubuxu
Copy link

Kubuxu commented Apr 13, 2022

Same comment as previously on Slack: as the bindings are written for specific headers, I would would prefer if header file itself was checked in. I can be convinced otherwise but from my perspective, it makes changes more auditable as most Go types not are aliases to C types.

@dignifiedquire
Copy link
Contributor Author

I will restate the question regarding changes to function signatures. filecoin-ffi is used by multiple downstream projects, and changes to function signatures will have painful side-effects.

Can you clarify which part of the api. The C-API or the go-api?

@dignifiedquire
Copy link
Contributor Author

Same comment as previously on Slack: as the bindings are written for specific headers, I would would prefer if header file itself was checked in. I can be convinced otherwise but from my perspective, it makes changes more auditable as most Go types not are aliases to C types.

I don't mind either way, happy to do as others think makes the most sense

@Kubuxu
Copy link

Kubuxu commented Apr 13, 2022

Can you clarify which part of the api. The C-API or the go-api?

I know of few projects using the Go side of filecoin-ffi. I'm not aware of any using the C/rust side.

@raulk
Copy link
Member

raulk commented Apr 25, 2022

Unfortunately this requires a merge from master again following the merge of execution traces. @dignifiedquire do you have a few minutes to look at this?

cgo/helpers.go Outdated Show resolved Hide resolved
cgo/helpers.go Outdated Show resolved Hide resolved
cgo/types.go Show resolved Hide resolved
distributed.go Outdated Show resolved Hide resolved
distributed.go Outdated Show resolved Hide resolved
distributed.go Outdated Show resolved Hide resolved
proofs.go Show resolved Hide resolved
@@ -12,32 +10,36 @@ use rand::rngs::OsRng;
use rand::SeedableRng;
use rand_chacha::ChaChaRng;
use rayon::prelude::*;
use safer_ffi::prelude::*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is especially confusing because we have an str module in here.

pub(crate) machine: Option<Mutex<CgoExecutor>>,
}

pub type FvmMachine = Option<repr_c::Box<InnerFvmMachine>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very confusing type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a great type, but necessary, I'll add a comment

if err := CheckErr(resp); err != nil {
faults := resp.value.faulty_sectors.copy()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is invalid memory otherwise? Maybe we should fix it in rust?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it wasn’t an issue, just wanted to make the code more defensive on both sides

@@ -64,6 +64,10 @@ main() {
find . -type f -name "lib$1.a"
fi

# generate filcrypto.h
RUSTFLAGS="${__rust_flags}" HEADER_DIR="." \
cargo test build_headers --features c-headers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dignifiedquire why build with a test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, it means we need to rebuild everything (and we should just be using cargo run with a bin target).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly because that is how the safer-ffi docs do it, and it didn't quite work when doing it differently due to how that project currently works.

@Stebalien
Copy link
Member

Ok. I've now synced the chain with the address sanitizer enabled (both in rust and go) with both the FVM and the lotus VM.

Now I'm running the lotus tests with the address sanitizer enabled.

@Stebalien
Copy link
Member

I'm getting:

    github.com/filecoin-project/lotus/storage.(*WindowPoStScheduler).runPoStCycle
        /var/lib/lotus/src/storage/wdpost_run.go:725
  - merge windowPoSt partition proofs:
    github.com/filecoin-project/lotus/extern/sector-storage.(*Manager).generateWindowPoSt
        /var/lib/lotus/src/extern/sector-storage/manager_post.go:207
  - SlicedBoxedUint8 must not be empty

Why can this not be empty?

They're fine. We can't represent them as null, but rust will internally
represent them as 0x1. We could just _assume_ that, but it's safer to
call into rust and let it handle this case.
@laser
Copy link
Contributor

laser commented May 19, 2022

C -> go bindings: remove c-for-go in favor of manual bindings

Wow!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Slices may not use null pointers
8 participants