1use core::arch::asm;
2use core::hint::unreachable_unchecked;
3use core::mem::MaybeUninit;
4
5macro_rules! gen_checked {
6 (@read $name:ident $ty:ty => $reg_constraint:tt $reg_modifier:tt) => {
7 #[inline]
8 pub fn $name (ptr: *const MaybeUninit<$ty>) -> Option<MaybeUninit<$ty>> {
9 let r: MaybeUninit<_>;
10 let success: usize;
11 unsafe {
12 if crate::detect::__detected::smap() {
13 asm!(
14 "stac",
15 concat!("2: mov {", $reg_modifier, "}, [{}]"),
16 " mov {}, 1",
17 ".pushsection .popcorn.deref_handlers.check",
18 ".quad 2b",
19 ".popsection",
20 ".pushsection .popcorn.deref_handlers.handle",
21 ".quad 3f",
22 ".popsection",
23 "3: ",
24 "clac",
25 inout($reg_constraint) MaybeUninit::<$ty>::uninit() => r, in(reg) ptr,
27 inout(reg) 0usize => success,
28 options(nostack, preserves_flags, readonly)
29 );
30 } else {
31 asm!(
32 concat!("2: mov {", $reg_modifier, "}, [{}]"),
33 " mov {}, 1",
34 ".pushsection .popcorn.deref_handlers.check",
35 ".quad 2b",
36 ".popsection",
37 ".pushsection .popcorn.deref_handlers.handle",
38 ".quad 3f",
39 ".popsection",
40 "3: ",
41 inout($reg_constraint) MaybeUninit::<$ty>::uninit() => r, in(reg) ptr,
43 inout(reg) 0usize => success,
44 options(nostack, preserves_flags, readonly)
45 );
46 }
47 }
48
49 match success {
50 0 => None,
51 1 => Some(r),
52 _ => unsafe { unreachable_unchecked() }
53 }
54 }
55 };
56 (@write $name:ident $ty:ty => $reg_constraint:tt $reg_modifier:tt) => {
57 #[inline]
58 pub fn $name(ptr: *mut MaybeUninit<$ty>, val: MaybeUninit<$ty>) -> Option<()> {
59 let success: usize;
60 unsafe {
61 if crate::detect::__detected::smap() {
62 asm!(
63 "stac",
64 ".pushsection .popcorn.deref_handlers.check",
65 ".quad 2f",
66 ".popsection",
67 "2:",
68 concat!("mov [{}], {", $reg_modifier, "}"),
69 "mov {}, 1",
70 ".pushsection .popcorn.deref_handlers.handle",
71 ".quad 3f",
72 ".popsection",
73 "3:",
74 "clac",
75 in(reg) ptr,
76 in($reg_constraint) val,
77 inout(reg) 0usize => success, options(nostack, preserves_flags, readonly)
79 );
80 } else {
81 asm!(
82 ".pushsection .popcorn.deref_handlers.check",
83 ".quad 2f",
84 ".popsection",
85 "2:",
86 concat!("mov [{}], {", $reg_modifier, "}"),
87 "mov {}, 1",
88 ".pushsection .popcorn.deref_handlers.handle",
89 ".quad 3f",
90 ".popsection",
91 "3:",
92 in(reg) ptr,
93 in($reg_constraint) val,
94 inout(reg) 0usize => success, options(nostack, preserves_flags, readonly)
96 );
97 }
98 }
99
100 match success {
101 0 => None,
102 1 => Some(()),
103 _ => unsafe { unreachable_unchecked() }
104 }
105 }
106 };
107}
108
109gen_checked!(@read checked_read_1 u8 => reg_byte "");
110gen_checked!(@read checked_read_2 u16 => reg ":x");
111gen_checked!(@read checked_read_4 u32 => reg ":e");
112#[cfg(target_arch = "x86_64")] gen_checked!(@read checked_read_8 u64 => reg ":r");
113gen_checked!(@write checked_write_1 u8 => reg_byte "");
114gen_checked!(@write checked_write_2 u16 => reg ":x");
115gen_checked!(@write checked_write_4 u32 => reg ":e");
116#[cfg(target_arch = "x86_64")] gen_checked!(@write checked_write_8 u64 => reg ":r");
117
118#[inline]
119pub fn checked_memcpy(src: *const MaybeUninit<u8>, dest: *mut MaybeUninit<u8>, count: usize) -> Option<()> {
120 let success: usize;
121 unsafe {
122 if crate::detect::__detected::smap() {
123 asm!(
124 "stac",
125 ".pushsection .popcorn.deref_handlers.check",
126 ".quad 2f",
127 ".popsection",
128 "2:",
129 "rep movsb [rdi], [rsi]",
130 "mov {}, 1",
131 ".pushsection .popcorn.deref_handlers.handle",
132 ".quad 3f",
133 ".popsection",
134 "3:",
135 "clac",
136 inout(reg) 0usize => success, in("rdi") dest,
138 in("rsi") src,
139 inout("rcx") count => _,
140 options(nostack)
141 );
142 } else {
143 asm!(
144 ".pushsection .popcorn.deref_handlers.check",
145 ".quad 2f",
146 ".popsection",
147 "2:",
148 "rep movsb [rdi], [rsi]",
149 "mov {}, 1",
150 ".pushsection .popcorn.deref_handlers.handle",
151 ".quad 3f",
152 ".popsection",
153 "3:",
154 inout(reg) 0usize => success, in("rdi") dest,
156 in("rsi") src,
157 inout("rcx") count => _,
158 options(nostack)
159 );
160 }
161 }
162
163 match success {
164 0 => None,
165 1 => Some(()),
166 _ => unsafe { unreachable_unchecked() }
167 }
168}
169
170
171#[inline]
172pub fn checked_fill(val: MaybeUninit<u8>, dest: *mut MaybeUninit<u8>, count: usize) -> Option<()> {
173 let success: usize;
174 unsafe {
175 if crate::detect::__detected::smap() {
176 asm!(
177 "stac",
178 ".pushsection .popcorn.deref_handlers.check",
179 ".quad 2f",
180 ".popsection",
181 "2:",
182 "rep stosb [rdi]",
183 "mov {}, 1",
184 ".pushsection .popcorn.deref_handlers.handle",
185 ".quad 3f",
186 ".popsection",
187 "3:",
188 "clac",
189 inout(reg) 0usize => success, in("rdi") dest,
191 in("al") val,
192 inout("rcx") count => _,
193 options(nostack)
194 );
195 } else {
196 asm!(
197 ".pushsection .popcorn.deref_handlers.check",
198 ".quad 2f",
199 ".popsection",
200 "2:",
201 "rep stosb [rdi]",
202 "mov {}, 1",
203 ".pushsection .popcorn.deref_handlers.handle",
204 ".quad 3f",
205 ".popsection",
206 "3:",
207 inout(reg) 0usize => success, in("rdi") dest,
209 in("al") val,
210 inout("rcx") count => _,
211 options(nostack)
212 );
213 }
214 }
215
216 match success {
217 0 => None,
218 1 => Some(()),
219 _ => unsafe { unreachable_unchecked() }
220 }
221}