Repository: rust-lang/rust-bindgen (rust)

Issue:
Subtract underflow when generating explicit paddings
<!-- Thanks for filing a bindgen issue! We appreciate it :-) -->

### Input C/C++ Header

```C
#pragma pack(2)

struct Test {
        unsigned long x;
        char a;
        char b;
        char c;
};

```

### Bindgen Invocation

```
$ bindgen input.h --explicit-padding
```

### Actual Results

```
panicked at bindgen/codegen/struct_layout.rs:309:20:
attempt to subtract with overflow
```

### Analysis

The reason this happens is because in `saw_field_with_layout`, padding bytes size is always calculated using the specified pack alignment, i.e. 2 bytes. however, if the field itself has a smaller alignment requirement, that should be used instead.




Pull request:
Use field align if it's smaller than pack align
Fixes #2695

Patch:
diff --git a/bindgen/codegen/struct_layout.rs b/bindgen/codegen/struct_layout.rs
index 83d78361..a62da695 100644
--- a/bindgen/codegen/struct_layout.rs
+++ b/bindgen/codegen/struct_layout.rs
@@ -212,7 +212,10 @@ impl<'a> StructLayoutTracker<'a> {
                     0
                 } else if !self.is_packed {
                     self.padding_bytes(field_layout)
-                } else if let Some(l) = self.known_type_layout {
+                } else if let Some(mut l) = self.known_type_layout {
+                    if field_layout.align < l.align {
+                        l.align = field_layout.align;
+                    }
                     self.padding_bytes(l)
                 } else {
                     0


Test patch:
diff --git a/bindgen-tests/tests/expectations/tests/issue-2695.rs b/bindgen-tests/tests/expectations/tests/issue-2695.rs
new file mode 100644
index 00000000..ed559fb6
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/issue-2695.rs
@@ -0,0 +1,45 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(C, packed(2))]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct Test {
+    pub x: ::std::os::raw::c_ulong,
+    pub a: ::std::os::raw::c_char,
+    pub b: ::std::os::raw::c_char,
+    pub c: ::std::os::raw::c_char,
+    pub __bindgen_padding_0: u8,
+}
+#[test]
+fn bindgen_test_layout_Test() {
+    const UNINIT: ::std::mem::MaybeUninit<Test> = ::std::mem::MaybeUninit::uninit();
+    let ptr = UNINIT.as_ptr();
+    assert_eq!(
+        ::std::mem::size_of::<Test>(),
+        12usize,
+        concat!("Size of: ", stringify!(Test)),
+    );
+    assert_eq!(
+        ::std::mem::align_of::<Test>(),
+        2usize,
+        concat!("Alignment of ", stringify!(Test)),
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).x) as usize - ptr as usize },
+        0usize,
+        concat!("Offset of field: ", stringify!(Test), "::", stringify!(x)),
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).a) as usize - ptr as usize },
+        8usize,
+        concat!("Offset of field: ", stringify!(Test), "::", stringify!(a)),
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).b) as usize - ptr as usize },
+        9usize,
+        concat!("Offset of field: ", stringify!(Test), "::", stringify!(b)),
+    );
+    assert_eq!(
+        unsafe { ::std::ptr::addr_of!((*ptr).c) as usize - ptr as usize },
+        10usize,
+        concat!("Offset of field: ", stringify!(Test), "::", stringify!(c)),
+    );
+}
diff --git a/bindgen-tests/tests/headers/issue-2695.h b/bindgen-tests/tests/headers/issue-2695.h
new file mode 100644
index 00000000..4fbcc39b
--- /dev/null
+++ b/bindgen-tests/tests/headers/issue-2695.h
@@ -0,0 +1,10 @@
+// bindgen-flags: --explicit-padding
+
+#pragma pack(2)
+
+struct Test {
+        unsigned long x;
+        char a;
+        char b;
+        char c;
+};