Skip to content

Commit 4528d56

Browse files
committed
Fix soundness of ContextWithCallbacks.
Change to use `NonNull` instead of `Box` due to the requirement that boxed values are not aliased. See rust-lang/unsafe-code-guidelines#326.
1 parent 4b016f7 commit 4528d56

File tree

8 files changed

+43
-30
lines changed

8 files changed

+43
-30
lines changed

src/bin/pinentry.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn main() {
77
let stdin = io::stdin();
88
let mut lines = stdin.lock().lines();
99
while let Some(Ok(cmd)) = lines.next() {
10-
match cmd.split(' ').nth(0) {
10+
match cmd.split(' ').next() {
1111
Some("GETPIN") => {
1212
println!("D abc");
1313
println!("OK");

src/context.rs

+23-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
iter::FusedIterator,
66
mem::ManuallyDrop,
77
ops::{Deref, DerefMut},
8-
ptr,
8+
ptr::{self, NonNull as CoreNonNull},
99
str::Utf8Error,
1010
time::Duration,
1111
};
@@ -1875,7 +1875,7 @@ impl Context {
18751875
}
18761876
}
18771877

1878-
fn get_result<R: crate::OpResult>(&self) -> Option<R> {
1878+
fn get_result<R: results::OpResult>(&self) -> Option<R> {
18791879
R::from_context(self)
18801880
}
18811881
}
@@ -2028,9 +2028,9 @@ impl fmt::Debug for Signers<'_> {
20282028
/// [`gpgme_ctx_t`](https://www.gnupg.org/documentation/manuals/gpgme/Contexts.html#Contexts)
20292029
pub struct ContextWithCallbacks<'a> {
20302030
inner: Context,
2031-
passphrase_hook: Option<Box<dyn Send + 'a>>,
2032-
progress_hook: Option<Box<dyn Send + 'a>>,
2033-
status_hook: Option<Box<dyn Send + 'a>>,
2031+
passphrase_hook: Option<CoreNonNull<dyn Send + 'a>>,
2032+
progress_hook: Option<CoreNonNull<dyn Send + 'a>>,
2033+
status_hook: Option<CoreNonNull<dyn Send + 'a>>,
20342034
}
20352035

20362036
impl Drop for ContextWithCallbacks<'_> {
@@ -2046,7 +2046,11 @@ impl<'a> ContextWithCallbacks<'a> {
20462046
/// [`gpgme_set_passphrase_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Passphrase-Callback.html#index-gpgme_005fset_005fpassphrase_005fcb)
20472047
pub fn clear_passphrase_provider(&mut self) {
20482048
(**self).clear_passphrase_provider();
2049-
self.passphrase_hook.take();
2049+
if let Some(hook) = self.passphrase_hook.take() {
2050+
unsafe {
2051+
drop(Box::from_raw(hook.as_ptr()));
2052+
}
2053+
}
20502054
}
20512055

20522056
/// Upstream documentation:
@@ -2062,15 +2066,19 @@ impl<'a> ContextWithCallbacks<'a> {
20622066
Some(callbacks::passphrase_cb::<P>),
20632067
ptr::addr_of_mut!(*hook).cast(),
20642068
);
2069+
self.passphrase_hook = Some(CoreNonNull::new_unchecked(Box::into_raw(hook)));
20652070
}
2066-
self.passphrase_hook = Some(hook);
20672071
}
20682072

20692073
/// Upstream documentation:
20702074
/// [`gpgme_set_progress_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Progress-Meter-Callback.html#index-gpgme_005fset_005fprogress_005fcb)
20712075
pub fn clear_progress_reporter(&mut self) {
20722076
(**self).clear_progress_reporter();
2073-
self.progress_hook.take();
2077+
if let Some(hook) = self.progress_hook.take() {
2078+
unsafe {
2079+
drop(Box::from_raw(hook.as_ptr()));
2080+
}
2081+
}
20742082
}
20752083

20762084
/// Upstream documentation:
@@ -2086,15 +2094,19 @@ impl<'a> ContextWithCallbacks<'a> {
20862094
Some(callbacks::progress_cb::<H>),
20872095
ptr::addr_of_mut!(*hook).cast(),
20882096
);
2097+
self.progress_hook = Some(CoreNonNull::new_unchecked(Box::into_raw(hook)));
20892098
}
2090-
self.progress_hook = Some(hook);
20912099
}
20922100

20932101
/// Upstream documentation:
20942102
/// [`gpgme_set_status_cb`](https://www.gnupg.org/documentation/manuals/gpgme/Status-Message-Callback.html#index-gpgme_005fset_005fstatus_005fcb)
20952103
pub fn clear_status_handler(&mut self) {
20962104
(**self).clear_status_handler();
2097-
self.status_hook.take();
2105+
if let Some(hook) = self.status_hook.take() {
2106+
unsafe {
2107+
drop(Box::from_raw(hook.as_ptr()));
2108+
}
2109+
}
20982110
}
20992111

21002112
/// Upstream documentation:
@@ -2110,8 +2122,8 @@ impl<'a> ContextWithCallbacks<'a> {
21102122
Some(callbacks::status_cb::<H>),
21112123
ptr::addr_of_mut!(*hook).cast(),
21122124
);
2125+
self.status_hook = Some(CoreNonNull::new_unchecked(Box::into_raw(hook)));
21132126
}
2114-
self.status_hook = Some(hook);
21152127
}
21162128

21172129
/// Returns the inner `Context` object.

src/data.rs

+4
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ impl<'data> Data<'data> {
228228

229229
/// Upstream documentation:
230230
/// [`gpgme_data_new_from_stream`](https://www.gnupg.org/documentation/manuals/gpgme/File-Based-Data-Buffers.html#index-gpgme_005fdata_005fnew_005ffrom_005fstream)
231+
///
232+
/// # Safety
233+
///
234+
/// The provided `FILE` object must be valid.
231235
#[inline]
232236
pub unsafe fn from_raw_file(file: *mut libc::FILE) -> Result<Self> {
233237
crate::init();

src/engine.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
use std::{
2-
ffi::CStr,
3-
fmt,
4-
marker::PhantomData,
5-
ptr,
6-
str::Utf8Error,
7-
sync::{RwLock, RwLockReadGuard},
8-
};
1+
use std::{ffi::CStr, fmt, marker::PhantomData, str::Utf8Error};
92

103
use ffi;
114

12-
use crate::{utils::convert_err, Context, NonNull, Protocol, Result};
5+
use crate::{Context, NonNull, Protocol, Result};
136

147
/// Upstream documentation:
158
/// [`gpgme_engine_info_t`](https://www.gnupg.org/documentation/manuals/gpgme/Engine-Information.html#index-gpgme_005fengine_005finfo_005ft)

src/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,4 @@ impl Gpgme {
345345
}
346346
}
347347

348-
unsafe trait OpResult: Clone {
349-
fn from_context(ctx: &Context) -> Option<Self>;
350-
}
351-
352348
type NonNull<T> = ptr::NonNull<<T as utils::Ptr>::Inner>;

src/results.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ use libc;
1313

1414
use crate::{
1515
notation::SignatureNotations, utils::convert_err, Context, Error, HashAlgorithm, ImportFlags,
16-
KeyAlgorithm, NonNull, OpResult, Result, SignMode, SignatureNotation, SignatureSummary,
17-
Validity,
16+
KeyAlgorithm, NonNull, Result, SignMode, SignatureSummary, Validity,
1817
};
1918

19+
pub(crate) trait OpResult: Clone {
20+
fn from_context(ctx: &Context) -> Option<Self>;
21+
}
22+
2023
macro_rules! impl_result {
2124
($(#[$Attr:meta])* $Name:ident : $T:ty = $Constructor:expr) => {
2225
$(#[$Attr])*
@@ -44,7 +47,7 @@ macro_rules! impl_result {
4447
}
4548
}
4649

47-
unsafe impl OpResult for $Name {
50+
impl OpResult for $Name {
4851
fn from_context(ctx: &Context) -> Option<Self> {
4952
unsafe {
5053
$Constructor(ctx.as_raw()).as_mut().map(|r| {

src/utils.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use std::io::{self, prelude::*};
33
use crate::Error;
44

55
pub use cstr_argument::CStrArgument;
6-
pub type SmallVec<T> = ::smallvec::SmallVec<[T; 4]>;
6+
pub type SmallVec<T> = ::smallvec::SmallVec<[T; 8]>;
77

88
macro_rules! impl_wrapper {
99
($T:ty$(, $Args:expr)*) => {
10+
/// # Safety
11+
/// The provided instance must be valid.
1012
#[inline]
1113
pub unsafe fn from_raw(raw: $T) -> Self {
1214
Self(NonNull::<$T>::new(raw).unwrap()$(, $Args)*)
@@ -30,6 +32,8 @@ macro_rules! impl_list_iterator {
3032
$Vis struct $Name<'a>(Option<$Item<'a>>);
3133

3234
impl $Name<'_> {
35+
/// # Safety
36+
/// The provided instance must be valid.
3337
#[inline]
3438
pub unsafe fn from_list(first: $Raw) -> Self {
3539
$Name(first.as_mut().map(|r| $Item::from_raw(r)))

tests/verify.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const TEST_MSG1: &[u8] = b"-----BEGIN PGP MESSAGE-----\n\
1111
=Crq6\n\
1212
-----END PGP MESSAGE-----\n";
1313

14-
#[sealed_test(before = common::setup(), after = common::teardown())]
14+
#[sealed_test(before = common::setup())]
1515
fn test_signature_key() {
1616
let mut ctx = common::create_context();
1717
let mut output = Vec::new();
@@ -25,5 +25,6 @@ fn test_signature_key() {
2525
return;
2626
}
2727
}
28+
common::teardown();
2829
panic!();
2930
}

0 commit comments

Comments
 (0)