]> git.lizzy.rs Git - rust.git/blob - src/shims/sync.rs
Store layouts of i32 and u32 inside Evaluator
[rust.git] / src / shims / sync.rs
1 use rustc_middle::ty::{TyKind, TypeAndMut};
2 use rustc_target::abi::{LayoutOf, Size};
3
4 use crate::stacked_borrows::Tag;
5 use crate::*;
6
7 fn assert_ptr_target_min_size<'mir, 'tcx: 'mir>(
8     ecx: &MiriEvalContext<'mir, 'tcx>,
9     operand: OpTy<'tcx, Tag>,
10     min_size: u64,
11 ) -> InterpResult<'tcx, ()> {
12     let target_ty = match operand.layout.ty.kind {
13         TyKind::RawPtr(TypeAndMut { ty, mutbl: _ }) => ty,
14         _ => panic!("Argument to pthread function was not a raw pointer"),
15     };
16     let target_layout = ecx.layout_of(target_ty)?;
17     assert!(target_layout.size.bytes() >= min_size);
18     Ok(())
19 }
20
21 // pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform.
22
23 // Our chosen memory layout: store an i32 in the first four bytes equal to the
24 // corresponding libc mutex kind constant (i.e. PTHREAD_MUTEX_NORMAL)
25
26 fn mutexattr_get_kind<'mir, 'tcx: 'mir>(
27     ecx: &MiriEvalContext<'mir, 'tcx>,
28     attr_op: OpTy<'tcx, Tag>,
29 ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
30     // Ensure that the following read at an offset to the attr pointer is within bounds
31     assert_ptr_target_min_size(ecx, attr_op, 4)?;
32     let attr_place = ecx.deref_operand(attr_op)?;
33     let kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, ecx.i32_layout()?, ecx)?;
34     ecx.read_scalar(kind_place.into())
35 }
36
37 fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
38     ecx: &mut MiriEvalContext<'mir, 'tcx>,
39     attr_op: OpTy<'tcx, Tag>,
40     kind: impl Into<ScalarMaybeUndef<Tag>>,
41 ) -> InterpResult<'tcx, ()> {
42     // Ensure that the following write at an offset to the attr pointer is within bounds
43     assert_ptr_target_min_size(ecx, attr_op, 4)?;
44     let attr_place = ecx.deref_operand(attr_op)?;
45     let kind_place = attr_place.offset(Size::ZERO, MemPlaceMeta::None, ecx.i32_layout()?, ecx)?;
46     ecx.write_scalar(kind.into(), kind_place.into())
47 }
48
49 // pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
50
51 // Our chosen memory layout:
52 // bytes 0-3: reserved for signature on macOS
53 // (need to avoid this because it is set by static initializer macros)
54 // bytes 4-7: count of how many times this mutex has been locked, as a u32
55 // bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32
56 // (the kind has to be at its offset for compatibility with static initializer macros)
57
58 fn mutex_get_locked_count<'mir, 'tcx: 'mir>(
59     ecx: &MiriEvalContext<'mir, 'tcx>,
60     mutex_op: OpTy<'tcx, Tag>,
61 ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
62     // Ensure that the following read at an offset to the mutex pointer is within bounds
63     assert_ptr_target_min_size(ecx, mutex_op, 20)?;
64     let mutex_place = ecx.deref_operand(mutex_op)?;
65     let locked_count_place =
66         mutex_place.offset(Size::from_bytes(4), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
67     ecx.read_scalar(locked_count_place.into())
68 }
69
70 fn mutex_set_locked_count<'mir, 'tcx: 'mir>(
71     ecx: &mut MiriEvalContext<'mir, 'tcx>,
72     mutex_op: OpTy<'tcx, Tag>,
73     locked_count: impl Into<ScalarMaybeUndef<Tag>>,
74 ) -> InterpResult<'tcx, ()> {
75     // Ensure that the following write at an offset to the mutex pointer is within bounds
76     assert_ptr_target_min_size(ecx, mutex_op, 20)?;
77     let mutex_place = ecx.deref_operand(mutex_op)?;
78     let locked_count_place =
79         mutex_place.offset(Size::from_bytes(4), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
80     ecx.write_scalar(locked_count.into(), locked_count_place.into())
81 }
82
83 fn mutex_get_kind<'mir, 'tcx: 'mir>(
84     ecx: &mut MiriEvalContext<'mir, 'tcx>,
85     mutex_op: OpTy<'tcx, Tag>,
86 ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
87     // Ensure that the following read at an offset to the mutex pointer is within bounds
88     assert_ptr_target_min_size(ecx, mutex_op, 20)?;
89     let mutex_place = ecx.deref_operand(mutex_op)?;
90     let kind_offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
91     let kind_place = mutex_place.offset(
92         Size::from_bytes(kind_offset),
93         MemPlaceMeta::None,
94         ecx.i32_layout()?,
95         ecx,
96     )?;
97     ecx.read_scalar(kind_place.into())
98 }
99
100 fn mutex_set_kind<'mir, 'tcx: 'mir>(
101     ecx: &mut MiriEvalContext<'mir, 'tcx>,
102     mutex_op: OpTy<'tcx, Tag>,
103     kind: impl Into<ScalarMaybeUndef<Tag>>,
104 ) -> InterpResult<'tcx, ()> {
105     // Ensure that the following write at an offset to the mutex pointer is within bounds
106     assert_ptr_target_min_size(ecx, mutex_op, 20)?;
107     let mutex_place = ecx.deref_operand(mutex_op)?;
108     let kind_offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
109     let kind_place = mutex_place.offset(
110         Size::from_bytes(kind_offset),
111         MemPlaceMeta::None,
112         ecx.i32_layout()?,
113         ecx,
114     )?;
115     ecx.write_scalar(kind.into(), kind_place.into())
116 }
117
118 // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
119
120 // Our chosen memory layout:
121 // bytes 0-3: reserved for signature on macOS
122 // (need to avoid this because it is set by static initializer macros)
123 // bytes 4-7: reader count, as a u32
124 // bytes 8-11: writer count, as a u32
125
126 fn rwlock_get_readers<'mir, 'tcx: 'mir>(
127     ecx: &MiriEvalContext<'mir, 'tcx>,
128     rwlock_op: OpTy<'tcx, Tag>,
129 ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
130     // Ensure that the following read at an offset to the rwlock pointer is within bounds
131     assert_ptr_target_min_size(ecx, rwlock_op, 12)?;
132     let rwlock_place = ecx.deref_operand(rwlock_op)?;
133     let readers_place =
134         rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
135     ecx.read_scalar(readers_place.into())
136 }
137
138 fn rwlock_set_readers<'mir, 'tcx: 'mir>(
139     ecx: &mut MiriEvalContext<'mir, 'tcx>,
140     rwlock_op: OpTy<'tcx, Tag>,
141     readers: impl Into<ScalarMaybeUndef<Tag>>,
142 ) -> InterpResult<'tcx, ()> {
143     // Ensure that the following write at an offset to the rwlock pointer is within bounds
144     assert_ptr_target_min_size(ecx, rwlock_op, 12)?;
145     let rwlock_place = ecx.deref_operand(rwlock_op)?;
146     let readers_place =
147         rwlock_place.offset(Size::from_bytes(4), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
148     ecx.write_scalar(readers.into(), readers_place.into())
149 }
150
151 fn rwlock_get_writers<'mir, 'tcx: 'mir>(
152     ecx: &MiriEvalContext<'mir, 'tcx>,
153     rwlock_op: OpTy<'tcx, Tag>,
154 ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
155     // Ensure that the following read at an offset to the rwlock pointer is within bounds
156     assert_ptr_target_min_size(ecx, rwlock_op, 12)?;
157     let rwlock_place = ecx.deref_operand(rwlock_op)?;
158     let writers_place =
159         rwlock_place.offset(Size::from_bytes(8), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
160     ecx.read_scalar(writers_place.into())
161 }
162
163 fn rwlock_set_writers<'mir, 'tcx: 'mir>(
164     ecx: &mut MiriEvalContext<'mir, 'tcx>,
165     rwlock_op: OpTy<'tcx, Tag>,
166     writers: impl Into<ScalarMaybeUndef<Tag>>,
167 ) -> InterpResult<'tcx, ()> {
168     // Ensure that the following write at an offset to the rwlock pointer is within bounds
169     assert_ptr_target_min_size(ecx, rwlock_op, 12)?;
170     let rwlock_place = ecx.deref_operand(rwlock_op)?;
171     let writers_place =
172         rwlock_place.offset(Size::from_bytes(8), MemPlaceMeta::None, ecx.u32_layout()?, ecx)?;
173     ecx.write_scalar(writers.into(), writers_place.into())
174 }
175
176 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
177 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
178     fn pthread_mutexattr_init(&mut self, attr_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
179         let this = self.eval_context_mut();
180
181         let attr = this.read_scalar(attr_op)?.not_undef()?;
182         if this.is_null(attr)? {
183             return this.eval_libc_i32("EINVAL");
184         }
185
186         let default_kind = this.eval_libc("PTHREAD_MUTEX_DEFAULT")?;
187         mutexattr_set_kind(this, attr_op, default_kind)?;
188
189         Ok(0)
190     }
191
192     fn pthread_mutexattr_settype(
193         &mut self,
194         attr_op: OpTy<'tcx, Tag>,
195         kind_op: OpTy<'tcx, Tag>,
196     ) -> InterpResult<'tcx, i32> {
197         let this = self.eval_context_mut();
198
199         let attr = this.read_scalar(attr_op)?.not_undef()?;
200         if this.is_null(attr)? {
201             return this.eval_libc_i32("EINVAL");
202         }
203
204         let kind = this.read_scalar(kind_op)?.not_undef()?;
205         if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")?
206             || kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
207             || kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")?
208         {
209             mutexattr_set_kind(this, attr_op, kind)?;
210         } else {
211             let einval = this.eval_libc_i32("EINVAL")?;
212             return Ok(einval);
213         }
214
215         Ok(0)
216     }
217
218     fn pthread_mutexattr_destroy(&mut self, attr_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
219         let this = self.eval_context_mut();
220
221         let attr = this.read_scalar(attr_op)?.not_undef()?;
222         if this.is_null(attr)? {
223             return this.eval_libc_i32("EINVAL");
224         }
225
226         mutexattr_set_kind(this, attr_op, ScalarMaybeUndef::Undef)?;
227
228         Ok(0)
229     }
230
231     fn pthread_mutex_init(
232         &mut self,
233         mutex_op: OpTy<'tcx, Tag>,
234         attr_op: OpTy<'tcx, Tag>,
235     ) -> InterpResult<'tcx, i32> {
236         let this = self.eval_context_mut();
237
238         let mutex = this.read_scalar(mutex_op)?.not_undef()?;
239         if this.is_null(mutex)? {
240             return this.eval_libc_i32("EINVAL");
241         }
242
243         let attr = this.read_scalar(attr_op)?.not_undef()?;
244         let kind = if this.is_null(attr)? {
245             this.eval_libc("PTHREAD_MUTEX_DEFAULT")?
246         } else {
247             mutexattr_get_kind(this, attr_op)?.not_undef()?
248         };
249
250         mutex_set_locked_count(this, mutex_op, Scalar::from_u32(0))?;
251         mutex_set_kind(this, mutex_op, kind)?;
252
253         Ok(0)
254     }
255
256     fn pthread_mutex_lock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
257         let this = self.eval_context_mut();
258
259         let mutex = this.read_scalar(mutex_op)?.not_undef()?;
260         if this.is_null(mutex)? {
261             return this.eval_libc_i32("EINVAL");
262         }
263
264         let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
265         let locked_count = mutex_get_locked_count(this, mutex_op)?.to_u32()?;
266
267         if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? {
268             if locked_count == 0 {
269                 mutex_set_locked_count(this, mutex_op, Scalar::from_u32(1))?;
270                 Ok(0)
271             } else {
272                 throw_machine_stop!(TerminationInfo::Deadlock);
273             }
274         } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? {
275             if locked_count == 0 {
276                 mutex_set_locked_count(this, mutex_op, Scalar::from_u32(1))?;
277                 Ok(0)
278             } else {
279                 this.eval_libc_i32("EDEADLK")
280             }
281         } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
282             match locked_count.checked_add(1) {
283                 Some(new_count) => {
284                     mutex_set_locked_count(this, mutex_op, Scalar::from_u32(new_count))?;
285                     Ok(0)
286                 }
287                 None => this.eval_libc_i32("EAGAIN"),
288             }
289         } else {
290             this.eval_libc_i32("EINVAL")
291         }
292     }
293
294     fn pthread_mutex_trylock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
295         let this = self.eval_context_mut();
296
297         let mutex = this.read_scalar(mutex_op)?.not_undef()?;
298         if this.is_null(mutex)? {
299             return this.eval_libc_i32("EINVAL");
300         }
301
302         let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
303         let locked_count = mutex_get_locked_count(this, mutex_op)?.to_u32()?;
304
305         if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")?
306             || kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
307         {
308             if locked_count == 0 {
309                 mutex_set_locked_count(this, mutex_op, Scalar::from_u32(1))?;
310                 Ok(0)
311             } else {
312                 this.eval_libc_i32("EBUSY")
313             }
314         } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
315             match locked_count.checked_add(1) {
316                 Some(new_count) => {
317                     mutex_set_locked_count(this, mutex_op, Scalar::from_u32(new_count))?;
318                     Ok(0)
319                 }
320                 None => this.eval_libc_i32("EAGAIN"),
321             }
322         } else {
323             this.eval_libc_i32("EINVAL")
324         }
325     }
326
327     fn pthread_mutex_unlock(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
328         let this = self.eval_context_mut();
329
330         let mutex = this.read_scalar(mutex_op)?.not_undef()?;
331         if this.is_null(mutex)? {
332             return this.eval_libc_i32("EINVAL");
333         }
334
335         let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
336         let locked_count = mutex_get_locked_count(this, mutex_op)?.to_u32()?;
337
338         if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? {
339             if locked_count != 0 {
340                 mutex_set_locked_count(this, mutex_op, Scalar::from_u32(0))?;
341                 Ok(0)
342             } else {
343                 throw_ub_format!(
344                     "Attempted to unlock a PTHREAD_MUTEX_NORMAL mutex that was not locked"
345                 );
346             }
347         } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? {
348             if locked_count != 0 {
349                 mutex_set_locked_count(this, mutex_op, Scalar::from_u32(0))?;
350                 Ok(0)
351             } else {
352                 this.eval_libc_i32("EPERM")
353             }
354         } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
355             match locked_count.checked_sub(1) {
356                 Some(new_count) => {
357                     mutex_set_locked_count(this, mutex_op, Scalar::from_u32(new_count))?;
358                     Ok(0)
359                 }
360                 None => {
361                     // locked_count was already zero
362                     this.eval_libc_i32("EPERM")
363                 }
364             }
365         } else {
366             this.eval_libc_i32("EINVAL")
367         }
368     }
369
370     fn pthread_mutex_destroy(&mut self, mutex_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
371         let this = self.eval_context_mut();
372
373         let mutex = this.read_scalar(mutex_op)?.not_undef()?;
374         if this.is_null(mutex)? {
375             return this.eval_libc_i32("EINVAL");
376         }
377
378         if mutex_get_locked_count(this, mutex_op)?.to_u32()? != 0 {
379             return this.eval_libc_i32("EBUSY");
380         }
381
382         mutex_set_kind(this, mutex_op, ScalarMaybeUndef::Undef)?;
383         mutex_set_locked_count(this, mutex_op, ScalarMaybeUndef::Undef)?;
384
385         Ok(0)
386     }
387
388     fn pthread_rwlock_rdlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
389         let this = self.eval_context_mut();
390
391         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
392         if this.is_null(rwlock)? {
393             return this.eval_libc_i32("EINVAL");
394         }
395
396         let readers = rwlock_get_readers(this, rwlock_op)?.to_u32()?;
397         let writers = rwlock_get_writers(this, rwlock_op)?.to_u32()?;
398         if writers != 0 {
399             throw_unsup_format!(
400                 "Deadlock due to read-locking a pthreads read-write lock while it is already write-locked"
401             );
402         } else {
403             match readers.checked_add(1) {
404                 Some(new_readers) => {
405                     rwlock_set_readers(this, rwlock_op, Scalar::from_u32(new_readers))?;
406                     Ok(0)
407                 }
408                 None => this.eval_libc_i32("EAGAIN"),
409             }
410         }
411     }
412
413     fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
414         let this = self.eval_context_mut();
415
416         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
417         if this.is_null(rwlock)? {
418             return this.eval_libc_i32("EINVAL");
419         }
420
421         let readers = rwlock_get_readers(this, rwlock_op)?.to_u32()?;
422         let writers = rwlock_get_writers(this, rwlock_op)?.to_u32()?;
423         if writers != 0 {
424             this.eval_libc_i32("EBUSY")
425         } else {
426             match readers.checked_add(1) {
427                 Some(new_readers) => {
428                     rwlock_set_readers(this, rwlock_op, Scalar::from_u32(new_readers))?;
429                     Ok(0)
430                 }
431                 None => this.eval_libc_i32("EAGAIN"),
432             }
433         }
434     }
435
436     fn pthread_rwlock_wrlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
437         let this = self.eval_context_mut();
438
439         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
440         if this.is_null(rwlock)? {
441             return this.eval_libc_i32("EINVAL");
442         }
443
444         let readers = rwlock_get_readers(this, rwlock_op)?.to_u32()?;
445         let writers = rwlock_get_writers(this, rwlock_op)?.to_u32()?;
446         if readers != 0 {
447             throw_unsup_format!(
448                 "Deadlock due to write-locking a pthreads read-write lock while it is already read-locked"
449             );
450         } else if writers != 0 {
451             throw_unsup_format!(
452                 "Deadlock due to write-locking a pthreads read-write lock while it is already write-locked"
453             );
454         } else {
455             rwlock_set_writers(this, rwlock_op, Scalar::from_u32(1))?;
456             Ok(0)
457         }
458     }
459
460     fn pthread_rwlock_trywrlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
461         let this = self.eval_context_mut();
462
463         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
464         if this.is_null(rwlock)? {
465             return this.eval_libc_i32("EINVAL");
466         }
467
468         let readers = rwlock_get_readers(this, rwlock_op)?.to_u32()?;
469         let writers = rwlock_get_writers(this, rwlock_op)?.to_u32()?;
470         if readers != 0 || writers != 0 {
471             this.eval_libc_i32("EBUSY")
472         } else {
473             rwlock_set_writers(this, rwlock_op, Scalar::from_u32(1))?;
474             Ok(0)
475         }
476     }
477
478     fn pthread_rwlock_unlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
479         let this = self.eval_context_mut();
480
481         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
482         if this.is_null(rwlock)? {
483             return this.eval_libc_i32("EINVAL");
484         }
485
486         let readers = rwlock_get_readers(this, rwlock_op)?.to_u32()?;
487         let writers = rwlock_get_writers(this, rwlock_op)?.to_u32()?;
488         if let Some(new_readers) = readers.checked_sub(1) {
489             rwlock_set_readers(this, rwlock_op, Scalar::from_u32(new_readers))?;
490             Ok(0)
491         } else if writers != 0 {
492             rwlock_set_writers(this, rwlock_op, Scalar::from_u32(0))?;
493             Ok(0)
494         } else {
495             this.eval_libc_i32("EPERM")
496         }
497     }
498
499     fn pthread_rwlock_destroy(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
500         let this = self.eval_context_mut();
501
502         let rwlock = this.read_scalar(rwlock_op)?.not_undef()?;
503         if this.is_null(rwlock)? {
504             return this.eval_libc_i32("EINVAL");
505         }
506
507         if rwlock_get_readers(this, rwlock_op)?.to_u32()? != 0 {
508             return this.eval_libc_i32("EBUSY");
509         }
510         if rwlock_get_writers(this, rwlock_op)?.to_u32()? != 0 {
511             return this.eval_libc_i32("EBUSY");
512         }
513
514         rwlock_set_readers(this, rwlock_op, ScalarMaybeUndef::Undef)?;
515         rwlock_set_writers(this, rwlock_op, ScalarMaybeUndef::Undef)?;
516
517         Ok(0)
518     }
519 }