Skip to content

Commit 834f191

Browse files
Merge pull request #212 from iqlusioninc/zeroize/explit-drop-derive
zeroize_derive: Require zeroize(drop) or zeroize(no_drop)
2 parents 225ff42 + 615fa32 commit 834f191

File tree

3 files changed

+106
-62
lines changed

3 files changed

+106
-62
lines changed

zeroize/src/lib.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,42 +52,38 @@
5252
//!
5353
//! ## Custom Derive Support
5454
//!
55+
//! **NOTICE**: Previous versions of `zeroize` automatically derived
56+
//! `Drop`. This has been *REMOVED* and you now *MUST* explicitly specify
57+
//! either `zeroize(drop)` or `zeroize(no_drop)` (see below).
58+
//!
5559
//! This crate has custom derive support for the `Zeroize` trait, which
56-
//! automatically calls `zeroize()` on all members of a struct or tuple struct,
57-
//! and adds a `Drop` impl which calls `zeroize()` when the item is dropped:
60+
//! automatically calls `zeroize()` on all members of a struct or tuple struct.
5861
//!
59-
//! ```
60-
//! use zeroize::Zeroize;
62+
//! Additionally it supports the following attributes (you *MUST* pick one):
6163
//!
62-
//! // This struct will be zeroized on drop
63-
//! #[derive(Zeroize)]
64-
//! struct MyStruct([u8; 64]);
65-
//! ```
64+
//! - `#[zeroize(no_drop)]`: derive only `Zeroize` without adding a `Drop` impl
65+
//! - `#[zeroize(drop)]`: call `zeroize()` when this item is dropped
6666
//!
67-
//! If, for some reason, you only want `Zeroize` to be derived but *don't*
68-
//! want an automatic `Drop` impl, you can add the `zeroize(no_drop)`
69-
//! attribute:
67+
//! Example which derives `Drop`:
7068
//!
7169
//! ```
7270
//! use zeroize::Zeroize;
7371
//!
74-
//! // This struct will *NOT* be zeroized on drop
72+
//! // This struct will be zeroized on drop
7573
//! #[derive(Zeroize)]
76-
//! #[zeroize(no_drop)]
77-
//! struct MyStruct([u8; 64]);
74+
//! #[zeroize(drop)]
75+
//! struct MyStruct([u8; 32]);
7876
//! ```
7977
//!
80-
//! If you prefer explicitness, you can add the `#[zeroize(drop)]`
81-
//! attribute to signal intent to zeroize values on `Drop`. However note this
82-
//! syntax is not necessary as the `Drop` handler is added by default:
78+
//! Example which does not derive `Drop` (useful for e.g. `Copy` types)
8379
//!
8480
//! ```
8581
//! use zeroize::Zeroize;
8682
//!
87-
//! // This struct will be zeroized on drop
88-
//! #[derive(Zeroize)]
89-
//! #[zeroize(drop)]
90-
//! struct MyStruct([u8; 64]);
83+
//! // This struct will *NOT* be zeroized on drop
84+
//! #[derive(Copy, Clone, Zeroize)]
85+
//! #[zeroize(no_drop)]
86+
//! struct MyStruct([u8; 32]);
9187
//! ```
9288
//!
9389
//! ## `Zeroizing<Z>`: wrapper for zeroizing arbitrary values on drop

zeroize/tests/zeroize_derive.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod custom_derive_tests {
55
use zeroize::Zeroize;
66

77
#[derive(Zeroize)]
8+
#[zeroize(drop)]
89
struct ZeroizableTupleStruct([u8; 3]);
910

1011
#[test]
@@ -15,6 +16,7 @@ mod custom_derive_tests {
1516
}
1617

1718
#[derive(Zeroize)]
19+
#[zeroize(drop)]
1820
struct ZeroizableStruct {
1921
string: String,
2022
vec: Vec<u8>,

zeroize_derive/src/lib.rs

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,54 +14,26 @@ use synstructure::{decl_derive, BindStyle};
1414
const ZEROIZE_ATTR: &str = "zeroize";
1515

1616
/// Custom derive for `Zeroize`
17-
fn zeroize_derive(mut s: synstructure::Structure) -> TokenStream {
18-
s.bind_with(|_| BindStyle::RefMut);
19-
17+
fn derive_zeroize(s: synstructure::Structure) -> TokenStream {
2018
let attributes = ZeroizeDeriveAttrs::parse(&s);
2119

22-
let zeroizers = s.each(|bi| quote! { #bi.zeroize(); });
23-
24-
let zeroize_impl = s.bound_impl(
25-
quote!(zeroize::Zeroize),
26-
quote! {
27-
fn zeroize(&mut self) {
28-
match self {
29-
#zeroizers
30-
}
31-
}
32-
},
33-
);
34-
35-
if attributes.no_drop {
36-
return zeroize_impl;
37-
}
38-
39-
let drop_impl = s.gen_impl(quote! {
40-
gen impl Drop for @Self {
41-
fn drop(&mut self) {
42-
self.zeroize();
43-
}
44-
}
45-
});
46-
47-
quote! {
48-
#zeroize_impl
49-
50-
#[doc(hidden)]
51-
#drop_impl
20+
match attributes.drop {
21+
Some(true) => derive_zeroize_with_drop(s),
22+
Some(false) => derive_zeroize_without_drop(s),
23+
None => panic!("must specify either zeroize(drop) or zeroize(no_drop) attribute"),
5224
}
5325
}
54-
decl_derive!([Zeroize, attributes(zeroize)] => zeroize_derive);
26+
decl_derive!([Zeroize, attributes(zeroize)] => derive_zeroize);
5527

5628
/// Custom derive attributes for `Zeroize`
5729
struct ZeroizeDeriveAttrs {
58-
/// Disable the on-by-default `Drop` derive
59-
no_drop: bool,
30+
/// Derive a `Drop` impl which calls zeroize on this type
31+
drop: Option<bool>,
6032
}
6133

6234
impl Default for ZeroizeDeriveAttrs {
6335
fn default() -> Self {
64-
Self { no_drop: false }
36+
Self { drop: None }
6537
}
6638
}
6739

@@ -75,8 +47,8 @@ impl ZeroizeDeriveAttrs {
7547
if attr.path.is_ident(ZEROIZE_ATTR) {
7648
// TODO(tarcieri): hax, but probably good enough for now
7749
match attr.tts.to_string().as_ref() {
78-
"( drop )" => (), // enabled by default
79-
"( no_drop )" => result.no_drop = true,
50+
"( drop )" => result.drop = Some(true),
51+
"( no_drop )" => result.drop = Some(false),
8052
other => panic!("unknown zeroize attribute: {}", other),
8153
}
8254
}
@@ -87,15 +59,89 @@ impl ZeroizeDeriveAttrs {
8759
}
8860
}
8961

62+
/// Custom derive for `Zeroize` (without `Drop`)
63+
fn derive_zeroize_without_drop(mut s: synstructure::Structure) -> TokenStream {
64+
s.bind_with(|_| BindStyle::RefMut);
65+
66+
let zeroizers = s.each(|bi| quote! { #bi.zeroize(); });
67+
68+
s.bound_impl(
69+
quote!(zeroize::Zeroize),
70+
quote! {
71+
fn zeroize(&mut self) {
72+
match self {
73+
#zeroizers
74+
}
75+
}
76+
},
77+
)
78+
}
79+
80+
/// Custom derive for `Zeroize` and `Drop`
81+
fn derive_zeroize_with_drop(s: synstructure::Structure) -> TokenStream {
82+
let drop_impl = s.gen_impl(quote! {
83+
gen impl Drop for @Self {
84+
fn drop(&mut self) {
85+
self.zeroize();
86+
}
87+
}
88+
});
89+
90+
let zeroize_impl = derive_zeroize_without_drop(s);
91+
92+
quote! {
93+
#zeroize_impl
94+
95+
#[doc(hidden)]
96+
#drop_impl
97+
}
98+
}
99+
90100
#[cfg(test)]
91101
mod tests {
92102
use super::*;
93103
use synstructure::test_derive;
94104

95105
#[test]
96-
fn zeroize() {
106+
fn zeroize_without_drop() {
107+
test_derive! {
108+
derive_zeroize_without_drop {
109+
struct Z {
110+
a: String,
111+
b: Vec<u8>,
112+
c: [u8; 3],
113+
}
114+
}
115+
expands to {
116+
#[allow(non_upper_case_globals)]
117+
#[doc(hidden)]
118+
const _DERIVE_zeroize_Zeroize_FOR_Z: () = {
119+
extern crate zeroize;
120+
impl zeroize::Zeroize for Z {
121+
fn zeroize(&mut self) {
122+
match self {
123+
Z {
124+
a: ref mut __binding_0,
125+
b: ref mut __binding_1,
126+
c: ref mut __binding_2,
127+
} => {
128+
{ __binding_0.zeroize(); }
129+
{ __binding_1.zeroize(); }
130+
{ __binding_2.zeroize(); }
131+
}
132+
}
133+
}
134+
}
135+
};
136+
}
137+
no_build // tests the code compiles are in the `zeroize` crate
138+
}
139+
}
140+
141+
#[test]
142+
fn zeroize_with_drop() {
97143
test_derive! {
98-
zeroize_derive {
144+
derive_zeroize_with_drop {
99145
struct Z {
100146
a: String,
101147
b: Vec<u8>,

0 commit comments

Comments
 (0)