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