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

Add AlignType and use it for buffer allocations #141

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions no_std_test/src/main.rs
Expand Up @@ -82,9 +82,9 @@ impl RngCore for FakeRng {

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
let mut buf = [0u8; 600_000];
let mut buf = [0; 75_000];
let size = Secp256k1::preallocate_size();
unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };
unsafe { libc::printf("needed size: %zu\n\0".as_ptr() as _, size) };

let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
secp.randomize(&mut FakeRng);
Expand Down
36 changes: 26 additions & 10 deletions secp256k1-sys/src/lib.rs
Expand Up @@ -39,6 +39,18 @@ pub mod recovery;
use core::{hash, slice, ptr};
use types::*;

/// A type that represents the type with the biggest alignment we can get in rust.
/// Trying to match what `malloc` does in C, this should be aligned enough to contain pointers too.
/// This type can have different size/alignment depending on the architecture.
#[cfg(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8"))]
pub type AlignType = u64;

/// A type that represents the type with the biggest alignment we can get in rust.
/// Trying to match what `malloc` does in C, this should be aligned enough to contain pointers too.
/// This type can have different size/alignment depending on the architecture.
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8")))]
pub type AlignType = usize;

/// Flag for context to enable no precomputation
pub const SECP256K1_START_NONE: c_uint = 1;
/// Flag for context to enable verification precomputation
Expand Down Expand Up @@ -300,14 +312,18 @@ pub unsafe extern "C" fn rustsecp256k1_v0_1_1_context_create(flags: c_uint) -> *
use std::mem;
assert!(mem::align_of::<usize>() >= mem::align_of::<u8>());
assert_eq!(mem::size_of::<usize>(), mem::size_of::<&usize>());
assert!(mem::align_of::<AlignType>() >= mem::align_of::<u8>());
assert!(mem::align_of::<AlignType>() >= mem::align_of::<usize>());
assert!(mem::size_of::<AlignType>() >= mem::size_of::<usize>());
assert!(mem::align_of::<AlignType>() >= mem::align_of::<&AlignType>());

let word_size = mem::size_of::<usize>();
let word_size = mem::size_of::<AlignType>();
let n_words = (secp256k1_context_preallocated_size(flags) + word_size - 1) / word_size;

let buf = vec![0usize; n_words + 1].into_boxed_slice();
let ptr = Box::into_raw(buf) as *mut usize;
::core::ptr::write(ptr, n_words);
let ptr: *mut usize = ptr.offset(1);
let buf = vec![0 as AlignType; n_words + 1].into_boxed_slice();
let ptr: *mut AlignType = Box::into_raw(buf) as *mut AlignType;
::core::ptr::write(ptr, n_words as AlignType);
let ptr: *mut AlignType = ptr.offset(1);

secp256k1_context_preallocated_create(ptr as *mut c_void, flags)
}
Expand All @@ -327,12 +343,12 @@ pub unsafe fn secp256k1_context_create(flags: c_uint) -> *mut Context {
#[cfg(all(feature = "std", not(feature = "external-symbols")))]
pub unsafe extern "C" fn rustsecp256k1_v0_1_1_context_destroy(ctx: *mut Context) {
secp256k1_context_preallocated_destroy(ctx);
let ctx: *mut usize = ctx as *mut usize;
let ctx: *mut AlignType = ctx as *mut AlignType;

let n_words_ptr: *mut usize = ctx.offset(-1);
let n_words: usize = ::core::ptr::read(n_words_ptr);
let slice: &mut [usize] = slice::from_raw_parts_mut(n_words_ptr , n_words+1);
let _ = Box::from_raw(slice as *mut [usize]);
let n_words_ptr: *mut AlignType = ctx.offset(-1);
let n_words: AlignType = ::core::ptr::read(n_words_ptr);
let slice: &mut [AlignType] = slice::from_raw_parts_mut(n_words_ptr , (n_words+1) as usize);
let _ = Box::from_raw(slice as *mut [AlignType]);
}

#[cfg(all(feature = "std", not(feature = "external-symbols")))]
Expand Down
75 changes: 54 additions & 21 deletions src/context.rs
@@ -1,7 +1,7 @@
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use ptr;
use ffi::{self, CPtr};
use ffi::{self, CPtr, AlignType};
use ffi::types::{c_uint, c_void};
use Error;
use Secp256k1;
Expand All @@ -17,7 +17,7 @@ pub unsafe trait Context : private::Sealed {
/// A constant description of the context.
const DESCRIPTION: &'static str;
/// A function to deallocate the memory when the context is dropped.
unsafe fn deallocate(ptr: *mut [u8]);
unsafe fn deallocate(ptr: *mut [AlignType]);
}

/// Marker trait for indicating that an instance of `Secp256k1` can be used for signing.
Expand Down Expand Up @@ -80,7 +80,7 @@ mod std_only {
const FLAGS: c_uint = ffi::SECP256K1_START_SIGN;
const DESCRIPTION: &'static str = "signing only";

unsafe fn deallocate(ptr: *mut [u8]) {
unsafe fn deallocate(ptr: *mut [AlignType]) {
let _ = Box::from_raw(ptr);
}
}
Expand All @@ -89,7 +89,7 @@ mod std_only {
const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY;
const DESCRIPTION: &'static str = "verification only";

unsafe fn deallocate(ptr: *mut [u8]) {
unsafe fn deallocate(ptr: *mut [AlignType]) {
let _ = Box::from_raw(ptr);
}
}
Expand All @@ -98,7 +98,7 @@ mod std_only {
const FLAGS: c_uint = VerifyOnly::FLAGS | SignOnly::FLAGS;
const DESCRIPTION: &'static str = "all capabilities";

unsafe fn deallocate(ptr: *mut [u8]) {
unsafe fn deallocate(ptr: *mut [AlignType]) {
let _ = Box::from_raw(ptr);
}
}
Expand All @@ -109,7 +109,7 @@ mod std_only {
#[cfg(target_arch = "wasm32")]
ffi::types::sanity_checks_for_wasm();

let buf = vec![0u8; Self::preallocate_size_gen()].into_boxed_slice();
let buf = vec![0 as AlignType; Self::preallocate_size_gen()].into_boxed_slice();
let ptr = Box::into_raw(buf);
Secp256k1 {
ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr as *mut c_void, C::FLAGS) },
Expand Down Expand Up @@ -149,7 +149,7 @@ mod std_only {
impl<C: Context> Clone for Secp256k1<C> {
fn clone(&self) -> Secp256k1<C> {
let clone_size = unsafe {ffi::secp256k1_context_preallocated_clone_size(self.ctx)};
let ptr_buf = Box::into_raw(vec![0u8; clone_size].into_boxed_slice());
let ptr_buf = Box::into_raw(vec![0 as AlignType; clone_size].into_boxed_slice());
Secp256k1 {
ctx: unsafe { ffi::secp256k1_context_preallocated_clone(self.ctx, ptr_buf as *mut c_void) },
phantom: PhantomData,
Expand All @@ -169,7 +169,7 @@ unsafe impl<'buf> Context for SignOnlyPreallocated<'buf> {
const FLAGS: c_uint = ffi::SECP256K1_START_SIGN;
const DESCRIPTION: &'static str = "signing only";

unsafe fn deallocate(_ptr: *mut [u8]) {
unsafe fn deallocate(_ptr: *mut [AlignType]) {
// Allocated by the user
}
}
Expand All @@ -178,7 +178,7 @@ unsafe impl<'buf> Context for VerifyOnlyPreallocated<'buf> {
const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY;
const DESCRIPTION: &'static str = "verification only";

unsafe fn deallocate(_ptr: *mut [u8]) {
unsafe fn deallocate(_ptr: *mut [AlignType]) {
// Allocated by the user
}
}
Expand All @@ -187,14 +187,14 @@ unsafe impl<'buf> Context for AllPreallocated<'buf> {
const FLAGS: c_uint = SignOnlyPreallocated::FLAGS | VerifyOnlyPreallocated::FLAGS;
const DESCRIPTION: &'static str = "all capabilities";

unsafe fn deallocate(_ptr: *mut [u8]) {
unsafe fn deallocate(_ptr: *mut [AlignType]) {
// Allocated by the user
}
}

impl<'buf, C: Context + 'buf> Secp256k1<C> {
/// Lets you create a context with preallocated buffer in a generic manner(sign/verify/all)
pub fn preallocated_gen_new(buf: &'buf mut [u8]) -> Result<Secp256k1<C>, Error> {
pub fn preallocated_gen_new(buf: &'buf mut [AlignType]) -> Result<Secp256k1<C>, Error> {
#[cfg(target_arch = "wasm32")]
ffi::types::sanity_checks_for_wasm();

Expand All @@ -208,17 +208,28 @@ impl<'buf, C: Context + 'buf> Secp256k1<C> {
C::FLAGS)
},
phantom: PhantomData,
buf: buf as *mut [u8],
buf: buf as *mut [AlignType],
})
}
}

impl<'buf> Secp256k1<AllPreallocated<'buf>> {
/// Creates a new Secp256k1 context with all capabilities
pub fn preallocated_new(buf: &'buf mut [u8]) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> {
pub fn preallocated_new(buf: &'buf mut [AlignType]) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> {
Secp256k1::preallocated_gen_new(buf)
}
/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for a context
/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all)
///
/// Notice that the memory returned is in [AlignType](type.AlignType.html)
///
/// ## Examples
/// ```rust
/// # use secp256k1::*;
/// let buf_size = Secp256k1::preallocate_size();
/// let mut buf = vec![0; buf_size];
Copy link
Collaborator

Choose a reason for hiding this comment

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

0 as AlignType?

Copy link
Member Author

Choose a reason for hiding this comment

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

rust automatically assign the right type because it's then passed to Secp256k1::preallocated_new which accepts [AlignType]

/// let secp = Secp256k1::preallocated_new(&mut buf).unwrap();
///
/// ```
pub fn preallocate_size() -> usize {
Self::preallocate_size_gen()
}
Expand All @@ -238,18 +249,29 @@ impl<'buf> Secp256k1<AllPreallocated<'buf>> {
ManuallyDrop::new(Secp256k1 {
ctx: raw_ctx,
phantom: PhantomData,
buf: ptr::null_mut::<[u8;0]>() as *mut [u8] ,
buf: ptr::null_mut::<[AlignType;0]>() as *mut [AlignType] ,
})
}
}

impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> {
/// Creates a new Secp256k1 context that can only be used for signing
pub fn preallocated_signing_only(buf: &'buf mut [u8]) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> {
pub fn preallocated_signing_only(buf: &'buf mut [AlignType]) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> {
Secp256k1::preallocated_gen_new(buf)
}

/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for the context
/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all)
///
/// Notice that the memory returned is in [AlignType](type.AlignType.html)
///
/// ## Examples
/// ```rust
/// # use secp256k1::*;
/// let buf_size = Secp256k1::preallocate_signing_size();
/// let mut buf = vec![0; buf_size];
/// let secp = Secp256k1::preallocated_signing_only(&mut buf).unwrap();
///
/// ```
#[inline]
pub fn preallocate_signing_size() -> usize {
Self::preallocate_size_gen()
Expand All @@ -270,18 +292,29 @@ impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> {
ManuallyDrop::new(Secp256k1 {
ctx: raw_ctx,
phantom: PhantomData,
buf: ptr::null_mut::<[u8;0]>() as *mut [u8] ,
buf: ptr::null_mut::<[AlignType;0]>() as *mut [AlignType] ,
})
}
}

impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> {
/// Creates a new Secp256k1 context that can only be used for verification
pub fn preallocated_verification_only(buf: &'buf mut [u8]) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> {
pub fn preallocated_verification_only(buf: &'buf mut [AlignType]) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> {
Secp256k1::preallocated_gen_new(buf)
}

/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for the context
/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all)
///
/// Notice that the memory returned is in [AlignType](type.AlignType.html)
///
/// ## Examples
/// ```rust
/// # use secp256k1::*;
/// let buf_size = Secp256k1::preallocate_verification_size();
/// let mut buf = vec![0; buf_size];
/// let secp = Secp256k1::preallocated_verification_only(&mut buf).unwrap();
///
/// ```
#[inline]
pub fn preallocate_verification_size() -> usize {
Self::preallocate_size_gen()
Expand All @@ -302,7 +335,7 @@ impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> {
ManuallyDrop::new(Secp256k1 {
ctx: raw_ctx,
phantom: PhantomData,
buf: ptr::null_mut::<[u8;0]>() as *mut [u8] ,
buf: ptr::null_mut::<[AlignType;0]>() as *mut [AlignType] ,
})
}
}
31 changes: 22 additions & 9 deletions src/lib.rs
Expand Up @@ -155,7 +155,9 @@ pub use secp256k1_sys as ffi;
#[cfg(any(test, feature = "rand"))] use rand::Rng;
#[cfg(any(test, feature = "std"))] extern crate core;

use core::{fmt, ptr, str};
use core::{fmt, mem, ptr, str};

pub use ffi::AlignType;

#[macro_use]
mod macros;
Expand Down Expand Up @@ -530,7 +532,7 @@ impl std::error::Error for Error {
pub struct Secp256k1<C: Context> {
ctx: *mut ffi::Context,
phantom: PhantomData<C>,
buf: *mut [u8],
buf: *mut [AlignType],
}

// The underlying secp context does not contain any references to memory it does not own
Expand Down Expand Up @@ -601,8 +603,19 @@ impl<C: Context> Secp256k1<C> {
}

/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all)
Copy link
Contributor

@laanwj laanwj Aug 8, 2019

Choose a reason for hiding this comment

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

please add comment that this returns the size in AlignType units

(also for the specific methods Secp256k1::preallocate_size_XX(), otherwise someone might think they return bytes!)

Copy link
Member Author

Choose a reason for hiding this comment

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

Good Point!

Copy link
Member

Choose a reason for hiding this comment

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

I think maybe they should return bytes.

Copy link
Member Author

Choose a reason for hiding this comment

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

why?

///
/// Notice that the memory returned is in [AlignType](type.AlignType.html)
///
pub fn preallocate_size_gen() -> usize {
unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) }
assert!(mem::align_of::<AlignType>() >= mem::align_of::<u8>());
assert!(mem::align_of::<AlignType>() >= mem::align_of::<usize>());
assert!(mem::size_of::<AlignType>() >= mem::size_of::<usize>());
assert!(mem::align_of::<AlignType>() >= mem::align_of::<&AlignType>());

let bytes_size = unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) };
let word_size = mem::size_of::<AlignType>();
let n_words = (bytes_size + word_size - 1) / word_size;
n_words
}

/// (Re)randomizes the Secp256k1 context for cheap sidechannel resistance;
Expand Down Expand Up @@ -718,7 +731,7 @@ mod tests {
use super::constants;
use super::{Secp256k1, Signature, Message};
use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature};
use ffi;
use ffi::{self, AlignType};
use context::*;

macro_rules! hex {
Expand All @@ -736,7 +749,7 @@ mod tests {
let ctx_sign = unsafe { ffi::secp256k1_context_create(SignOnlyPreallocated::FLAGS) };
let ctx_vrfy = unsafe { ffi::secp256k1_context_create(VerifyOnlyPreallocated::FLAGS) };

let buf: *mut [u8] = &mut [0u8;0] as _;
let buf: *mut [AlignType] = &mut [0 as AlignType;0] as _;
let full: Secp256k1<AllPreallocated> = Secp256k1{ctx: ctx_full, phantom: PhantomData, buf};
let sign: Secp256k1<SignOnlyPreallocated> = Secp256k1{ctx: ctx_sign, phantom: PhantomData, buf};
let vrfy: Secp256k1<VerifyOnlyPreallocated> = Secp256k1{ctx: ctx_vrfy, phantom: PhantomData, buf};
Expand Down Expand Up @@ -795,10 +808,10 @@ mod tests {

#[test]
fn test_preallocation() {
let mut buf_ful = vec![0u8; Secp256k1::preallocate_size()];
let mut buf_sign = vec![0u8; Secp256k1::preallocate_signing_size()];
let mut buf_vfy = vec![0u8; Secp256k1::preallocate_verification_size()];
//
let mut buf_ful = vec![0; Secp256k1::preallocate_size()];
let mut buf_sign = vec![0; Secp256k1::preallocate_signing_size()];
let mut buf_vfy = vec![0; Secp256k1::preallocate_verification_size()];

let full = Secp256k1::preallocated_new(&mut buf_ful).unwrap();
let sign = Secp256k1::preallocated_signing_only(&mut buf_sign).unwrap();
let vrfy = Secp256k1::preallocated_verification_only(&mut buf_vfy).unwrap();
Expand Down