x86_alignment_check/
lib.rs

1/*!
2`x86-alignment-check` is set `ac` flag in `eflags` on `x86` or `x86_64`
3
4# Features
5- set `ac` flag bit into ON, its included `eflags` of `x86`.
6- `x86_64` are supported too.
7- `#![no_std]`
8
9# Example 1: If your code is correctly controlled by alignment
10First, add the following to `Cargo.toml`:
11
12```text
13[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dev-dependencies]
14x86-alignment-check = "*"
15```
16
17Second, enclose your test code with `x86_alignment_check()` as follows:
18
19```rust
20    use x86_alignment_check::x86_alignment_check;
21    //
22    let old_flag = x86_alignment_check(true);
23    //
24    // here your test codes, processing anythings, a bus error may occur.
25    //
26    let _ = x86_alignment_check(old_flag);
27```
28
29Finally execute `cargo test`
30
31# Example 2: call_once style
32```rust
33    let val = x86_alignment_check::ac_call_once(|| {
34        // here is alignment check
35        // processing anythings
36        // return value for assertion
37        1
38    });
39    assert_eq!(val, 1);
40```
41For now, assertions such as `assert_eq!()` cannot be included inside `FnOnce`,
42because of the rust runtime bug.
43
44# Example 3: call_once style, but not alignment check
45```rust
46    let val = x86_alignment_check::no_ac_call_once(|| {
47        // here is not alignment check
48        // processing anythings
49        // return value for assertion
50        1
51    });
52    assert_eq!(val, 1);
53```
54
55*/
56#![no_std]
57
58/// alignment check flag manipulation
59#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
60pub fn x86_alignment_check(b: bool) -> bool {
61    let old_eflags = unsafe { __read_eflags() };
62    let new_eflags = if b {
63        old_eflags | EFLAGS_AC_BIT
64    } else {
65        old_eflags & !EFLAGS_AC_BIT
66    };
67    unsafe { __write_eflags(new_eflags) };
68    //
69    (old_eflags & EFLAGS_AC_BIT) != 0
70}
71
72#[cfg(target_arch = "x86")]
73const EFLAGS_AC_BIT: u32 = 1 << 18; // 0x0004_0000
74
75#[cfg(target_arch = "x86_64")]
76const EFLAGS_AC_BIT: u64 = 1 << 18; // 0x0004_0000
77
78#[cfg(target_arch = "x86")]
79#[inline(always)]
80unsafe fn __read_eflags() -> u32 {
81    let mut eflags: u32;
82    core::arch::asm!("pushfd; pop {eflags:e}", eflags = out(reg) eflags);
83    eflags
84}
85
86#[cfg(target_arch = "x86")]
87#[inline(always)]
88unsafe fn __write_eflags(eflags: u32) {
89    core::arch::asm!("push {eflags:e}; popfd", eflags = in(reg) eflags);
90}
91
92#[cfg(target_arch = "x86_64")]
93#[inline(always)]
94unsafe fn __read_eflags() -> u64 {
95    let mut rflags: u64;
96    core::arch::asm!("pushfq; pop {rflags}", rflags = out(reg) rflags);
97    rflags
98}
99
100#[cfg(target_arch = "x86_64")]
101#[inline(always)]
102unsafe fn __write_eflags(rflags: u64) {
103    core::arch::asm!("push {rflags}; popfq", rflags = in(reg) rflags);
104}
105
106/// execute under alignment check
107pub fn ac_call_once<F, T>(f: F) -> T
108where
109    F: FnOnce() -> T,
110{
111    let old = x86_alignment_check(true);
112    let r = f();
113    let _ = x86_alignment_check(old);
114    r
115}
116
117/// execute under no alignment check
118pub fn no_ac_call_once<F, T>(f: F) -> T
119where
120    F: FnOnce() -> T,
121{
122    let old = x86_alignment_check(false);
123    let r = f();
124    let _ = x86_alignment_check(old);
125    r
126}
127
128// reference:
129// https://www.felixcloutier.com/x86/pushf:pushfd:pushfq
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn it_works_0() {
137        let old_0 = x86_alignment_check(true);
138        let old_1 = x86_alignment_check(true);
139        let old_2 = x86_alignment_check(false);
140        let old_3 = x86_alignment_check(true);
141        let old_4 = x86_alignment_check(false);
142        let _old_5 = x86_alignment_check(old_0);
143        //
144        assert!(old_1);
145        assert!(old_2);
146        assert!(!old_3);
147        assert!(old_4);
148    }
149    #[test]
150    fn it_works_1() {
151        let val = ac_call_once(|| 1);
152        assert_eq!(val, 1);
153    }
154    #[test]
155    fn it_works_2() {
156        let val = no_ac_call_once(|| 1);
157        assert_eq!(val, 1);
158    }
159    #[test]
160    fn it_works_3() {
161        let buf = [0_u8; 100];
162        //
163        let val = ac_call_once(|| {
164            let val = no_ac_call_once(|| {
165                let ptr = buf.as_ptr();
166                let ptr = unsafe { ptr.add(3) };
167                // next should "(signal: 7, SIGBUS: access to undefined memory)"
168                // under alignment check, but here is not alignment check
169                let _v: u32 = unsafe { (ptr as *const u32).read() };
170                1
171            });
172            val + 1
173        });
174        assert_eq!(val, 2);
175    }
176    #[test]
177    #[ignore]
178    fn it_works_ignore_0() {
179        let buf = [0_u8; 100];
180        //
181        let _old_0 = x86_alignment_check(true);
182        {
183            let ptr = buf.as_ptr();
184            let ptr = unsafe { ptr.add(3) };
185            // next should "(signal: 7, SIGBUS: access to undefined memory)"
186            let _v: u32 = unsafe { (ptr as *const u32).read() };
187        }
188    }
189}