1use rustc_abi::Size;
2use rustc_target::spec::Os;
3
4use crate::concurrency::sync::{AccessKind, SyncObj};
5use crate::*;
6
7fn bytewise_equal<'tcx>(
12 ecx: &MiriInterpCx<'tcx>,
13 left: &MPlaceTy<'tcx>,
14 right: &MPlaceTy<'tcx>,
15 prefix: Option<u64>,
16) -> InterpResult<'tcx, bool> {
17 let size = left.layout.size;
18 assert_eq!(size, right.layout.size);
19 let cmp_size = prefix.map(Size::from_bytes).unwrap_or(size);
20
21 let left_bytes = ecx.read_bytes_ptr_strip_provenance(left.ptr(), cmp_size)?;
22 let right_bytes = ecx.read_bytes_ptr_strip_provenance(right.ptr(), cmp_size)?;
23
24 interp_ok(left_bytes == right_bytes)
25}
26
27const PTHREAD_UNINIT: u8 = 0;
29const PTHREAD_INIT: u8 = 1;
30
31#[inline]
36fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
37 interp_ok(match &ecx.tcx.sess.target.os {
38 Os::Linux
39 | Os::Illumos
40 | Os::Solaris
41 | Os::MacOs
42 | Os::FreeBsd
43 | Os::Android
44 | Os::NetBsd => 0,
45 os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
46 })
47}
48
49fn mutexattr_get_kind<'tcx>(
50 ecx: &MiriInterpCx<'tcx>,
51 attr_ptr: &OpTy<'tcx>,
52) -> InterpResult<'tcx, i32> {
53 ecx.deref_pointer_and_read(
54 attr_ptr,
55 mutexattr_kind_offset(ecx)?,
56 ecx.libc_ty_layout("pthread_mutexattr_t"),
57 ecx.machine.layouts.i32,
58 )?
59 .to_i32()
60}
61
62fn mutexattr_set_kind<'tcx>(
63 ecx: &mut MiriInterpCx<'tcx>,
64 attr_ptr: &OpTy<'tcx>,
65 kind: i32,
66) -> InterpResult<'tcx, ()> {
67 ecx.deref_pointer_and_write(
68 attr_ptr,
69 mutexattr_kind_offset(ecx)?,
70 Scalar::from_i32(kind),
71 ecx.libc_ty_layout("pthread_mutexattr_t"),
72 ecx.machine.layouts.i32,
73 )
74}
75
76const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000;
81
82fn mutexattr_translate_kind<'tcx>(
84 ecx: &MiriInterpCx<'tcx>,
85 kind: i32,
86) -> InterpResult<'tcx, MutexKind> {
87 interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) {
88 MutexKind::Normal
89 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
90 MutexKind::ErrorCheck
91 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
92 MutexKind::Recursive
93 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
94 || kind == PTHREAD_MUTEX_KIND_UNCHANGED
95 {
96 MutexKind::Default
99 } else {
100 throw_unsup_format!("unsupported type of mutex: {kind}");
101 })
102}
103
104#[derive(Debug, Clone, Copy)]
110enum MutexKind {
111 Normal,
112 Default,
113 Recursive,
114 ErrorCheck,
115}
116
117#[derive(Debug, Clone)]
118struct PthreadMutex {
119 mutex_ref: MutexRef,
120 kind: MutexKind,
121}
122
123impl SyncObj for PthreadMutex {
124 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
125 if !self.mutex_ref.queue_is_empty() {
126 throw_ub_format!(
127 "{access_kind} of `pthread_mutex_t` is forbidden while the queue is non-empty"
128 );
129 }
130 interp_ok(())
131 }
132
133 fn delete_on_write(&self) -> bool {
134 true
135 }
136}
137
138fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
142 let offset = match &ecx.tcx.sess.target.os {
143 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
144 Os::MacOs | Os::NetBsd => 4,
146 os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
147 };
148 let offset = Size::from_bytes(offset);
149
150 if !ecx.machine.pthread_mutex_sanity.replace(true) {
153 let check_static_initializer = |name| {
154 let static_initializer = ecx.eval_path(&["libc", name]);
155 let init_field =
156 static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
157 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
158 assert_eq!(
159 init, PTHREAD_UNINIT,
160 "{name} is incompatible with our initialization logic"
161 );
162 };
163
164 check_static_initializer("PTHREAD_MUTEX_INITIALIZER");
165 match &ecx.tcx.sess.target.os {
167 Os::Linux => {
168 check_static_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP");
169 check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP");
170 check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP");
171 }
172 Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => {
173 }
175 os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
176 }
177 }
178
179 interp_ok(offset)
180}
181
182fn mutex_create<'tcx>(
184 ecx: &mut MiriInterpCx<'tcx>,
185 mutex_ptr: &OpTy<'tcx>,
186 kind: MutexKind,
187) -> InterpResult<'tcx> {
188 let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
189 let data = PthreadMutex { mutex_ref: MutexRef::new(), kind };
190 ecx.init_immovable_sync(&mutex, mutex_init_offset(ecx)?, PTHREAD_INIT, data)?;
191 interp_ok(())
192}
193
194fn mutex_get_data<'tcx, 'a>(
197 ecx: &'a mut MiriInterpCx<'tcx>,
198 mutex_ptr: &OpTy<'tcx>,
199) -> InterpResult<'tcx, &'a PthreadMutex>
200where
201 'tcx: 'a,
202{
203 let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
204 ecx.get_immovable_sync_with_static_init(
205 &mutex,
206 mutex_init_offset(ecx)?,
207 PTHREAD_UNINIT,
208 PTHREAD_INIT,
209 |ecx| {
210 let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
211 interp_ok(PthreadMutex { mutex_ref: MutexRef::new(), kind })
212 },
213 )
214}
215
216fn mutex_kind_from_static_initializer<'tcx>(
218 ecx: &MiriInterpCx<'tcx>,
219 mutex: &MPlaceTy<'tcx>,
220) -> InterpResult<'tcx, MutexKind> {
221 let prefix = match &ecx.tcx.sess.target.os {
222 Os::Android => Some(4),
225 _ => None,
226 };
227 let is_initializer = |name| bytewise_equal(ecx, mutex, &ecx.eval_path(&["libc", name]), prefix);
228
229 if is_initializer("PTHREAD_MUTEX_INITIALIZER")? {
233 return interp_ok(MutexKind::Default);
234 }
235 match &ecx.tcx.sess.target.os {
237 Os::Linux =>
238 if is_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP")? {
239 return interp_ok(MutexKind::Recursive);
240 } else if is_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP")? {
241 return interp_ok(MutexKind::ErrorCheck);
242 },
243 _ => {}
244 }
245 throw_ub_format!(
246 "`pthread_mutex_t` was not properly initialized at this location, or it got overwritten"
247 );
248}
249
250#[derive(Debug, Clone)]
255struct PthreadRwLock {
256 rwlock_ref: RwLockRef,
257}
258
259impl SyncObj for PthreadRwLock {
260 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
261 if !self.rwlock_ref.queue_is_empty() {
262 throw_ub_format!(
263 "{access_kind} of `pthread_rwlock_t` is forbidden while the queue is non-empty"
264 );
265 }
266 interp_ok(())
267 }
268
269 fn delete_on_write(&self) -> bool {
270 true
271 }
272}
273
274fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
275 let offset = match &ecx.tcx.sess.target.os {
276 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
277 Os::MacOs => 4,
279 os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"),
280 };
281 let offset = Size::from_bytes(offset);
282
283 if !ecx.machine.pthread_rwlock_sanity.replace(true) {
286 let static_initializer = ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]);
287 let init_field = static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
288 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
289 assert_eq!(
290 init, PTHREAD_UNINIT,
291 "PTHREAD_RWLOCK_INITIALIZER is incompatible with our initialization logic"
292 );
293 }
294
295 interp_ok(offset)
296}
297
298fn rwlock_get_data<'tcx, 'a>(
299 ecx: &'a mut MiriInterpCx<'tcx>,
300 rwlock_ptr: &OpTy<'tcx>,
301) -> InterpResult<'tcx, &'a PthreadRwLock>
302where
303 'tcx: 'a,
304{
305 let rwlock = ecx.deref_pointer_as(rwlock_ptr, ecx.libc_ty_layout("pthread_rwlock_t"))?;
306 ecx.get_immovable_sync_with_static_init(
307 &rwlock,
308 rwlock_init_offset(ecx)?,
309 PTHREAD_UNINIT,
310 PTHREAD_INIT,
311 |ecx| {
312 let prefix = match &ecx.tcx.sess.target.os {
313 Os::Android => Some(4),
316 _ => None,
317 };
318 if !bytewise_equal(
319 ecx,
320 &rwlock,
321 &ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]),
322 prefix,
323 )? {
324 throw_ub_format!(
325 "`pthread_rwlock_t` was not properly initialized at this location, or it got overwritten"
326 );
327 }
328 interp_ok(PthreadRwLock { rwlock_ref: RwLockRef::new() })
329 },
330 )
331}
332
333#[inline]
338fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
339 interp_ok(match &ecx.tcx.sess.target.os {
340 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
341 os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"),
343 })
344}
345
346fn condattr_get_clock_id<'tcx>(
347 ecx: &MiriInterpCx<'tcx>,
348 attr_ptr: &OpTy<'tcx>,
349) -> InterpResult<'tcx, Scalar> {
350 ecx.deref_pointer_and_read(
351 attr_ptr,
352 condattr_clock_offset(ecx)?,
353 ecx.libc_ty_layout("pthread_condattr_t"),
354 ecx.machine.layouts.i32,
355 )
356}
357
358fn condattr_set_clock_id<'tcx>(
359 ecx: &mut MiriInterpCx<'tcx>,
360 attr_ptr: &OpTy<'tcx>,
361 clock_id: i32,
362) -> InterpResult<'tcx, ()> {
363 ecx.deref_pointer_and_write(
364 attr_ptr,
365 condattr_clock_offset(ecx)?,
366 Scalar::from_i32(clock_id),
367 ecx.libc_ty_layout("pthread_condattr_t"),
368 ecx.machine.layouts.i32,
369 )
370}
371
372fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
377 let offset = match &ecx.tcx.sess.target.os {
378 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
379 Os::MacOs => 4,
381 os => throw_unsup_format!("`pthread_cond` is not supported on {os}"),
382 };
383 let offset = Size::from_bytes(offset);
384
385 if !ecx.machine.pthread_condvar_sanity.replace(true) {
388 let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]);
389 let init_field = static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
390 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
391 assert_eq!(
392 init, PTHREAD_UNINIT,
393 "PTHREAD_COND_INITIALIZER is incompatible with our initialization logic"
394 );
395 }
396
397 interp_ok(offset)
398}
399
400#[derive(Debug, Clone)]
401struct PthreadCondvar {
402 condvar_ref: CondvarRef,
403 clock: TimeoutClock,
404}
405
406impl SyncObj for PthreadCondvar {
407 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
408 if !self.condvar_ref.queue_is_empty() {
409 throw_ub_format!(
410 "{access_kind} of `pthread_cond_t` is forbidden while the queue is non-empty"
411 );
412 }
413 interp_ok(())
414 }
415
416 fn delete_on_write(&self) -> bool {
417 true
418 }
419}
420
421fn cond_create<'tcx>(
422 ecx: &mut MiriInterpCx<'tcx>,
423 cond_ptr: &OpTy<'tcx>,
424 clock: TimeoutClock,
425) -> InterpResult<'tcx> {
426 let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
427 let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
428 ecx.init_immovable_sync(&cond, cond_init_offset(ecx)?, PTHREAD_INIT, data)?;
429 interp_ok(())
430}
431
432fn cond_get_data<'tcx, 'a>(
433 ecx: &'a mut MiriInterpCx<'tcx>,
434 cond_ptr: &OpTy<'tcx>,
435) -> InterpResult<'tcx, &'a PthreadCondvar>
436where
437 'tcx: 'a,
438{
439 let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
440 ecx.get_immovable_sync_with_static_init(
441 &cond,
442 cond_init_offset(ecx)?,
443 PTHREAD_UNINIT,
444 PTHREAD_INIT,
445 |ecx| {
446 let prefix = match &ecx.tcx.sess.target.os {
447 Os::Android => Some(4),
450 _ => None,
451 };
452 if !bytewise_equal(
453 ecx,
454 &cond,
455 &ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]),
456 prefix,
457 )? {
458 throw_ub_format!(
459 "`pthread_cond_t` was not properly initialized at this location, or it got overwritten"
460 );
461 }
462 interp_ok(PthreadCondvar {
464 condvar_ref: CondvarRef::new(),
465 clock: TimeoutClock::RealTime,
466 })
467 },
468 )
469}
470
471fn sem_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
476 let offset = match &ecx.tcx.sess.target.os {
477 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
478 os => throw_unsup_format!("`sem` is not supported on {os}"),
480 };
481 interp_ok(Size::from_bytes(offset))
482}
483
484#[derive(Debug, Clone)]
485struct PthreadSem {
486 sem_ref: SemaphoreRef,
487}
488
489impl SyncObj for PthreadSem {
490 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
491 if !self.sem_ref.queue_is_empty() {
492 throw_ub_format!("{access_kind} of `sem_t` is forbidden while the queue is non-empty");
493 }
494 interp_ok(())
495 }
496
497 fn delete_on_write(&self) -> bool {
498 true
499 }
500}
501
502fn sem_create<'tcx>(
503 ecx: &mut MiriInterpCx<'tcx>,
504 sem_ptr: &OpTy<'tcx>,
505 value: u32,
506) -> InterpResult<'tcx> {
507 let sem = ecx.deref_pointer_as(sem_ptr, ecx.libc_ty_layout("sem_t"))?;
508 let data = PthreadSem { sem_ref: SemaphoreRef::new(value.into()) };
509 ecx.init_immovable_sync(&sem, sem_init_offset(ecx)?, PTHREAD_INIT, data)?;
510 interp_ok(())
511}
512
513fn sem_get_data<'tcx, 'a>(
514 ecx: &'a mut MiriInterpCx<'tcx>,
515 sem_ptr: &OpTy<'tcx>,
516) -> InterpResult<'tcx, &'a PthreadSem>
517where
518 'tcx: 'a,
519{
520 let sem = ecx.deref_pointer_as(sem_ptr, ecx.libc_ty_layout("sem_t"))?;
521 ecx.get_immovable_sync_with_static_init(
522 &sem,
523 sem_init_offset(ecx)?,
524 PTHREAD_UNINIT,
525 PTHREAD_INIT,
526 |_ecx| throw_ub_format!("`sem_t` value has not been initialized with `sem_init`"),
527 )
528}
529
530impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
531pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
532 fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
533 let this = self.eval_context_mut();
534
535 mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?;
536
537 interp_ok(())
538 }
539
540 fn pthread_mutexattr_settype(
541 &mut self,
542 attr_op: &OpTy<'tcx>,
543 kind_op: &OpTy<'tcx>,
544 ) -> InterpResult<'tcx, Scalar> {
545 let this = self.eval_context_mut();
546
547 let kind = this.read_scalar(kind_op)?.to_i32()?;
548 if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL")
549 || kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
550 || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
551 || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
552 {
553 assert_ne!(kind, PTHREAD_MUTEX_KIND_UNCHANGED);
555 mutexattr_set_kind(this, attr_op, kind)?;
556 } else {
557 let einval = this.eval_libc_i32("EINVAL");
558 return interp_ok(Scalar::from_i32(einval));
559 }
560
561 interp_ok(Scalar::from_i32(0))
562 }
563
564 fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
565 let this = self.eval_context_mut();
566
567 mutexattr_get_kind(this, attr_op)?;
569
570 this.write_uninit(
583 &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?,
584 )?;
585
586 interp_ok(())
587 }
588
589 fn pthread_mutex_init(
590 &mut self,
591 mutex_op: &OpTy<'tcx>,
592 attr_op: &OpTy<'tcx>,
593 ) -> InterpResult<'tcx, ()> {
594 let this = self.eval_context_mut();
595
596 let attr = this.read_pointer(attr_op)?;
597 let kind = if this.ptr_is_null(attr)? {
598 MutexKind::Default
599 } else {
600 mutexattr_translate_kind(this, mutexattr_get_kind(this, attr_op)?)?
601 };
602
603 mutex_create(this, mutex_op, kind)?;
604
605 interp_ok(())
606 }
607
608 fn pthread_mutex_lock(
609 &mut self,
610 mutex_op: &OpTy<'tcx>,
611 dest: &MPlaceTy<'tcx>,
612 ) -> InterpResult<'tcx> {
613 let this = self.eval_context_mut();
614
615 let mutex = mutex_get_data(this, mutex_op)?.clone();
616
617 let ret = if let Some(owner_thread) = mutex.mutex_ref.owner() {
618 if owner_thread != this.active_thread() {
619 this.mutex_enqueue_and_block(
620 mutex.mutex_ref,
621 Some((Scalar::from_i32(0), dest.clone())),
622 );
623 return interp_ok(());
624 } else {
625 match mutex.kind {
627 MutexKind::Default =>
628 throw_ub_format!(
629 "trying to acquire default mutex already locked by the current thread"
630 ),
631 MutexKind::Normal => throw_machine_stop!(TerminationInfo::LocalDeadlock),
632 MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
633 MutexKind::Recursive => {
634 this.mutex_lock(&mutex.mutex_ref)?;
635 0
636 }
637 }
638 }
639 } else {
640 this.mutex_lock(&mutex.mutex_ref)?;
642 0
643 };
644 this.write_scalar(Scalar::from_i32(ret), dest)?;
645 interp_ok(())
646 }
647
648 fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
649 let this = self.eval_context_mut();
650
651 let mutex = mutex_get_data(this, mutex_op)?.clone();
652
653 interp_ok(Scalar::from_i32(if let Some(owner_thread) = mutex.mutex_ref.owner() {
654 if owner_thread != this.active_thread() {
655 this.eval_libc_i32("EBUSY")
656 } else {
657 match mutex.kind {
658 MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
659 this.eval_libc_i32("EBUSY"),
660 MutexKind::Recursive => {
661 this.mutex_lock(&mutex.mutex_ref)?;
662 0
663 }
664 }
665 }
666 } else {
667 this.mutex_lock(&mutex.mutex_ref)?;
669 0
670 }))
671 }
672
673 fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
674 let this = self.eval_context_mut();
675
676 let mutex = mutex_get_data(this, mutex_op)?.clone();
677
678 if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
679 interp_ok(Scalar::from_i32(0))
681 } else {
682 match mutex.kind {
686 MutexKind::Default =>
687 throw_ub_format!(
688 "unlocked a default mutex that was not locked by the current thread"
689 ),
690 MutexKind::Normal =>
691 throw_ub_format!(
692 "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
693 ),
694 MutexKind::ErrorCheck | MutexKind::Recursive =>
695 interp_ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
696 }
697 }
698 }
699
700 fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
701 let this = self.eval_context_mut();
702
703 let mutex = mutex_get_data(this, mutex_op)?.clone();
706
707 if mutex.mutex_ref.owner().is_some() {
708 throw_ub_format!("destroyed a locked mutex");
709 }
710
711 let mutex_place =
714 this.deref_pointer_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?;
715 this.write_uninit(&mutex_place)?;
716
717 interp_ok(())
718 }
719
720 fn pthread_rwlock_rdlock(
721 &mut self,
722 rwlock_op: &OpTy<'tcx>,
723 dest: &MPlaceTy<'tcx>,
724 ) -> InterpResult<'tcx> {
725 let this = self.eval_context_mut();
726
727 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
728
729 if rwlock.rwlock_ref.is_write_locked() {
730 this.rwlock_enqueue_and_block_reader(
731 rwlock.rwlock_ref,
732 Scalar::from_i32(0),
733 dest.clone(),
734 );
735 } else {
736 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
737 this.write_null(dest)?;
738 }
739
740 interp_ok(())
741 }
742
743 fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
744 let this = self.eval_context_mut();
745
746 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
747
748 if rwlock.rwlock_ref.is_write_locked() {
749 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
750 } else {
751 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
752 interp_ok(Scalar::from_i32(0))
753 }
754 }
755
756 fn pthread_rwlock_wrlock(
757 &mut self,
758 rwlock_op: &OpTy<'tcx>,
759 dest: &MPlaceTy<'tcx>,
760 ) -> InterpResult<'tcx> {
761 let this = self.eval_context_mut();
762
763 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
764
765 if rwlock.rwlock_ref.is_locked() {
766 this.rwlock_enqueue_and_block_writer(
779 rwlock.rwlock_ref,
780 Scalar::from_i32(0),
781 dest.clone(),
782 );
783 } else {
784 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
785 this.write_null(dest)?;
786 }
787
788 interp_ok(())
789 }
790
791 fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
792 let this = self.eval_context_mut();
793
794 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
795
796 if rwlock.rwlock_ref.is_locked() {
797 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
798 } else {
799 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
800 interp_ok(Scalar::from_i32(0))
801 }
802 }
803
804 fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
805 let this = self.eval_context_mut();
806
807 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
808
809 if this.rwlock_reader_unlock(&rwlock.rwlock_ref)?
810 || this.rwlock_writer_unlock(&rwlock.rwlock_ref)?
811 {
812 interp_ok(())
813 } else {
814 throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
815 }
816 }
817
818 fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
819 let this = self.eval_context_mut();
820
821 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
824
825 if rwlock.rwlock_ref.is_locked() {
826 throw_ub_format!("destroyed a locked rwlock");
827 }
828
829 let rwlock_place =
832 this.deref_pointer_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?;
833 this.write_uninit(&rwlock_place)?;
834
835 interp_ok(())
836 }
837
838 fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
839 let this = self.eval_context_mut();
840
841 if this.tcx.sess.target.os != Os::MacOs {
843 let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME");
847 condattr_set_clock_id(this, attr_op, default_clock_id)?;
848 }
849
850 interp_ok(())
851 }
852
853 fn pthread_condattr_setclock(
854 &mut self,
855 attr_op: &OpTy<'tcx>,
856 clock_id_op: &OpTy<'tcx>,
857 ) -> InterpResult<'tcx, Scalar> {
858 let this = self.eval_context_mut();
859
860 let clock_id = this.read_scalar(clock_id_op)?;
861 if this.parse_clockid(clock_id).is_some() {
862 condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?;
863 } else {
864 let einval = this.eval_libc_i32("EINVAL");
865 return interp_ok(Scalar::from_i32(einval));
866 }
867
868 interp_ok(Scalar::from_i32(0))
869 }
870
871 fn pthread_condattr_getclock(
872 &mut self,
873 attr_op: &OpTy<'tcx>,
874 clk_id_op: &OpTy<'tcx>,
875 ) -> InterpResult<'tcx, ()> {
876 let this = self.eval_context_mut();
877
878 let clock_id = condattr_get_clock_id(this, attr_op)?;
879 this.write_scalar(
880 clock_id,
881 &this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?,
882 )?;
883
884 interp_ok(())
885 }
886
887 fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
888 let this = self.eval_context_mut();
889
890 if this.tcx.sess.target.os != Os::MacOs {
893 condattr_get_clock_id(this, attr_op)?;
894 }
895
896 this.write_uninit(
899 &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?,
900 )?;
901
902 interp_ok(())
903 }
904
905 fn pthread_cond_init(
906 &mut self,
907 cond_op: &OpTy<'tcx>,
908 attr_op: &OpTy<'tcx>,
909 ) -> InterpResult<'tcx, ()> {
910 let this = self.eval_context_mut();
911
912 let attr = this.read_pointer(attr_op)?;
913 let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == Os::MacOs {
915 this.eval_libc("CLOCK_REALTIME")
916 } else {
917 condattr_get_clock_id(this, attr_op)?
918 };
919 let Some(clock) = this.parse_clockid(clock_id) else {
920 throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)")
922 };
923
924 cond_create(this, cond_op, clock)?;
925
926 interp_ok(())
927 }
928
929 fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
930 let this = self.eval_context_mut();
931 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
932 this.condvar_signal(&condvar)?;
933 interp_ok(())
934 }
935
936 fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
937 let this = self.eval_context_mut();
938 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
939 while this.condvar_signal(&condvar)? {}
940 interp_ok(())
941 }
942
943 fn pthread_cond_wait(
944 &mut self,
945 cond_op: &OpTy<'tcx>,
946 mutex_op: &OpTy<'tcx>,
947 dest: &MPlaceTy<'tcx>,
948 ) -> InterpResult<'tcx> {
949 let this = self.eval_context_mut();
950
951 let data = cond_get_data(this, cond_op)?.clone();
952 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
953
954 this.condvar_wait(
955 data.condvar_ref,
956 mutex_ref,
957 None, Scalar::from_i32(0),
959 Scalar::from_i32(0), dest.clone(),
961 )?;
962
963 interp_ok(())
964 }
965
966 fn pthread_cond_timedwait(
967 &mut self,
968 cond_op: &OpTy<'tcx>,
969 mutex_op: &OpTy<'tcx>,
970 timeout_op: &OpTy<'tcx>,
971 dest: &MPlaceTy<'tcx>,
972 macos_relative_np: bool,
973 ) -> InterpResult<'tcx> {
974 let this = self.eval_context_mut();
975
976 let data = cond_get_data(this, cond_op)?.clone();
977 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
978
979 let Some(duration) = this
981 .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)?
982 else {
983 let einval = this.eval_libc("EINVAL");
984 this.write_scalar(einval, dest)?;
985 return interp_ok(());
986 };
987
988 let (clock, style) = if macos_relative_np {
989 (TimeoutClock::Monotonic, TimeoutStyle::Relative)
992 } else {
993 if data.clock == TimeoutClock::RealTime {
994 this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
995 }
996
997 (data.clock, TimeoutStyle::Absolute)
998 };
999
1000 this.condvar_wait(
1001 data.condvar_ref,
1002 mutex_ref,
1003 Some(this.machine.timeout(clock, style, duration)),
1004 Scalar::from_i32(0),
1005 this.eval_libc("ETIMEDOUT"), dest.clone(),
1007 )?;
1008
1009 interp_ok(())
1010 }
1011
1012 fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
1013 let this = self.eval_context_mut();
1014
1015 let condvar = &cond_get_data(this, cond_op)?.condvar_ref;
1018 if !condvar.queue_is_empty() {
1019 throw_ub_format!("destroying an awaited conditional variable");
1020 }
1021
1022 let cond_place = this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?;
1025 this.write_uninit(&cond_place)?;
1026
1027 interp_ok(())
1028 }
1029
1030 fn pthread_sem_init(
1031 &mut self,
1032 sem_op: &OpTy<'tcx>,
1033 pshared_op: &OpTy<'tcx>,
1034 value_op: &OpTy<'tcx>,
1035 dest: &MPlaceTy<'tcx>,
1036 ) -> InterpResult<'tcx> {
1037 let this = self.eval_context_mut();
1038
1039 let pshared = this.read_scalar(pshared_op)?.to_i32()?;
1040 let value = this.read_scalar(value_op)?.to_u32()?;
1041
1042 let _ignore = pshared;
1044
1045 sem_create(this, sem_op, value)?;
1046
1047 this.write_null(dest)?; interp_ok(())
1049 }
1050
1051 fn pthread_sem_wait(
1052 &mut self,
1053 sem_op: &OpTy<'tcx>,
1054 dest: &MPlaceTy<'tcx>,
1055 ) -> InterpResult<'tcx> {
1056 let this = self.eval_context_mut();
1057
1058 let data = sem_get_data(this, sem_op)?.clone();
1059 let dest = dest.clone();
1060 this.semaphore_decrement(
1061 data.sem_ref,
1062 None,
1063 callback!(
1064 @capture<'tcx> {
1065 dest: MPlaceTy<'tcx>,
1066 }
1067 |this, unblock: UnblockKind| {
1068 assert_eq!(unblock, UnblockKind::Ready);
1069 this.write_null(&dest)?;
1070 interp_ok(())
1071 }
1072 ),
1073 )?;
1074
1075 interp_ok(())
1076 }
1077
1078 fn pthread_sem_timedwait(
1079 &mut self,
1080 sem_op: &OpTy<'tcx>,
1081 abs_timeout_op: &OpTy<'tcx>,
1082 dest: &MPlaceTy<'tcx>,
1083 ) -> InterpResult<'tcx> {
1084 let this = self.eval_context_mut();
1085 this.check_no_isolation("`pthread_sem_timedwait`")?;
1086
1087 let abs_timeout = this.deref_pointer_as(abs_timeout_op, this.libc_ty_layout("timespec"))?;
1088
1089 let Some(duration) = this.read_timespec(&abs_timeout)? else {
1090 return this.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
1091 };
1092 let deadline =
1093 this.machine.timeout(TimeoutClock::RealTime, TimeoutStyle::Absolute, duration);
1094 let data = sem_get_data(this, sem_op)?.clone();
1095 let dest = dest.clone();
1096 this.semaphore_decrement(
1097 data.sem_ref,
1098 Some(deadline),
1099 callback!(
1100 @capture<'tcx> {
1101 dest: MPlaceTy<'tcx>,
1102 }
1103 |this, unblock: UnblockKind| {
1104 match unblock {
1105 UnblockKind::Ready =>
1106 this.write_null(&dest),
1107 UnblockKind::TimedOut =>
1108 this.set_errno_and_return_neg1(LibcError("ETIMEDOUT"), &dest),
1109 }
1110 }
1111 ),
1112 )?;
1113
1114 interp_ok(())
1115 }
1116
1117 fn pthread_sem_post(
1118 &mut self,
1119 sem_op: &OpTy<'tcx>,
1120 dest: &MPlaceTy<'tcx>,
1121 ) -> InterpResult<'tcx> {
1122 let this = self.eval_context_mut();
1123
1124 let data = sem_get_data(this, sem_op)?.clone();
1125 this.semaphore_increment(&data.sem_ref)?;
1126
1127 this.write_null(dest)?; interp_ok(())
1129 }
1130
1131 fn pthread_sem_destroy(
1132 &mut self,
1133 sem_op: &OpTy<'tcx>,
1134 dest: &MPlaceTy<'tcx>,
1135 ) -> InterpResult<'tcx> {
1136 let this = self.eval_context_mut();
1137
1138 let data = sem_get_data(this, sem_op)?.clone();
1139 if !data.sem_ref.queue_is_empty() {
1140 throw_ub_format!("destroying an awaited semaphore");
1141 }
1142
1143 let sem_place = this.deref_pointer_as(sem_op, this.libc_ty_layout("sem_t"))?;
1146 this.write_uninit(&sem_place)?;
1147
1148 this.write_null(dest)?; interp_ok(())
1150 }
1151}