Skip to content

Commit 67538b6

Browse files
authored
Allow explicit padding (#2060)
If a struct needs to be serialized in its native format (padding bytes and all), for example writing it to a file or sending it on the network, then explicit padding fields are necessary, as anything reading the padding bytes of a struct may lead to Undefined Behavior.
1 parent 14a8d29 commit 67538b6

File tree

6 files changed

+147
-5
lines changed

6 files changed

+147
-5
lines changed

src/codegen/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,14 @@ impl CodeGenerator for CompInfo {
17971797
(),
17981798
);
17991799
}
1800+
// Check whether an explicit padding field is needed
1801+
// at the end.
1802+
if let Some(comp_layout) = layout {
1803+
fields.extend(
1804+
struct_layout
1805+
.add_tail_padding(&canonical_name, comp_layout),
1806+
);
1807+
}
18001808
}
18011809

18021810
if is_opaque {

src/codegen/struct_layout.rs

+37-5
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,11 @@ impl<'a> StructLayoutTracker<'a> {
217217
let padding_layout = if self.is_packed || is_union {
218218
None
219219
} else {
220+
let force_padding = self.ctx.options().force_explicit_padding;
221+
220222
// Otherwise the padding is useless.
221-
let need_padding = padding_bytes >= field_layout.align ||
223+
let need_padding = force_padding ||
224+
padding_bytes >= field_layout.align ||
222225
field_layout.align > MAX_GUARANTEED_ALIGN;
223226

224227
debug!(
@@ -236,11 +239,14 @@ impl<'a> StructLayoutTracker<'a> {
236239
field_layout
237240
);
238241

242+
let padding_align = if force_padding {
243+
1
244+
} else {
245+
cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN)
246+
};
247+
239248
if need_padding && padding_bytes != 0 {
240-
Some(Layout::new(
241-
padding_bytes,
242-
cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN),
243-
))
249+
Some(Layout::new(padding_bytes, padding_align))
244250
} else {
245251
None
246252
}
@@ -262,6 +268,32 @@ impl<'a> StructLayoutTracker<'a> {
262268
padding_layout.map(|layout| self.padding_field(layout))
263269
}
264270

271+
pub fn add_tail_padding(
272+
&mut self,
273+
comp_name: &str,
274+
comp_layout: Layout,
275+
) -> Option<proc_macro2::TokenStream> {
276+
// Only emit an padding field at the end of a struct if the
277+
// user configures explicit padding.
278+
if !self.ctx.options().force_explicit_padding {
279+
return None;
280+
}
281+
282+
if self.latest_offset == comp_layout.size {
283+
// This struct does not contain tail padding.
284+
return None;
285+
}
286+
287+
trace!(
288+
"need a tail padding field for {}: offset {} -> size {}",
289+
comp_name,
290+
self.latest_offset,
291+
comp_layout.size
292+
);
293+
let size = comp_layout.size - self.latest_offset;
294+
Some(self.padding_field(Layout::new(size, 0)))
295+
}
296+
265297
pub fn pad_struct(
266298
&mut self,
267299
layout: Layout,

src/lib.rs

+19
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ impl Builder {
562562
output_vector.push("--c-naming".into());
563563
}
564564

565+
if self.options.force_explicit_padding {
566+
output_vector.push("--explicit-padding".into());
567+
}
568+
565569
// Add clang arguments
566570

567571
output_vector.push("--".into());
@@ -1419,6 +1423,17 @@ impl Builder {
14191423
self
14201424
}
14211425

1426+
/// If true, always emit explicit padding fields.
1427+
///
1428+
/// If a struct needs to be serialized in its native format (padding bytes
1429+
/// and all), for example writing it to a file or sending it on the network,
1430+
/// then this should be enabled, as anything reading the padding bytes of
1431+
/// a struct may lead to Undefined Behavior.
1432+
pub fn explicit_padding(mut self, doit: bool) -> Self {
1433+
self.options.force_explicit_padding = doit;
1434+
self
1435+
}
1436+
14221437
/// Generate the Rust bindings using the options built up thus far.
14231438
pub fn generate(mut self) -> Result<Bindings, ()> {
14241439
// Add any extra arguments from the environment to the clang command line.
@@ -1937,6 +1952,9 @@ struct BindgenOptions {
19371952

19381953
/// Generate types with C style naming.
19391954
c_naming: bool,
1955+
1956+
/// Always output explicit padding fields
1957+
force_explicit_padding: bool,
19401958
}
19411959

19421960
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -2079,6 +2097,7 @@ impl Default for BindgenOptions {
20792097
respect_cxx_access_specs: false,
20802098
translate_enum_integer_types: false,
20812099
c_naming: false,
2100+
force_explicit_padding: false,
20822101
}
20832102
}
20842103
}

src/options.rs

+7
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ where
516516
Arg::with_name("c-naming")
517517
.long("c-naming")
518518
.help("Generate types with C style naming."),
519+
Arg::with_name("explicit-padding")
520+
.long("explicit-padding")
521+
.help("Always output explicit padding fields."),
519522
]) // .args()
520523
.get_matches_from(args);
521524

@@ -960,6 +963,10 @@ where
960963
builder = builder.c_naming(true);
961964
}
962965

966+
if matches.is_present("explicit-padding") {
967+
builder = builder.explicit_padding(true);
968+
}
969+
963970
let verbose = matches.is_present("verbose");
964971

965972
Ok((builder, output, verbose))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
#[repr(C)]
9+
#[derive(Debug, Default, Copy, Clone)]
10+
pub struct pad_me {
11+
pub first: u8,
12+
pub __bindgen_padding_0: [u8; 3usize],
13+
pub second: u32,
14+
pub third: u16,
15+
pub __bindgen_padding_1: [u8; 2usize],
16+
}
17+
#[test]
18+
fn bindgen_test_layout_pad_me() {
19+
assert_eq!(
20+
::std::mem::size_of::<pad_me>(),
21+
12usize,
22+
concat!("Size of: ", stringify!(pad_me))
23+
);
24+
assert_eq!(
25+
::std::mem::align_of::<pad_me>(),
26+
4usize,
27+
concat!("Alignment of ", stringify!(pad_me))
28+
);
29+
assert_eq!(
30+
unsafe {
31+
&(*(::std::ptr::null::<pad_me>())).first as *const _ as usize
32+
},
33+
0usize,
34+
concat!(
35+
"Offset of field: ",
36+
stringify!(pad_me),
37+
"::",
38+
stringify!(first)
39+
)
40+
);
41+
assert_eq!(
42+
unsafe {
43+
&(*(::std::ptr::null::<pad_me>())).second as *const _ as usize
44+
},
45+
4usize,
46+
concat!(
47+
"Offset of field: ",
48+
stringify!(pad_me),
49+
"::",
50+
stringify!(second)
51+
)
52+
);
53+
assert_eq!(
54+
unsafe {
55+
&(*(::std::ptr::null::<pad_me>())).third as *const _ as usize
56+
},
57+
8usize,
58+
concat!(
59+
"Offset of field: ",
60+
stringify!(pad_me),
61+
"::",
62+
stringify!(third)
63+
)
64+
);
65+
}

tests/headers/explicit-padding.h

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-flags: --explicit-padding
2+
3+
typedef unsigned char uint8_t;
4+
typedef unsigned short uint16_t;
5+
typedef unsigned int uint32_t;
6+
7+
struct pad_me {
8+
uint8_t first;
9+
uint32_t second;
10+
uint16_t third;
11+
};

0 commit comments

Comments
 (0)