1use rustc_abi::Size;
2use rustc_target::spec::Os;
3
4use crate::*;
5
6fn is_initializer<'tcx>(
10 ecx: &MiriInterpCx<'tcx>,
11 place: &MPlaceTy<'tcx>,
12 name: &str,
13 prefix: Option<u64>,
14) -> InterpResult<'tcx, bool> {
15 let size = place.layout.size;
16 let cmp_size = prefix.map(Size::from_bytes).unwrap_or(size);
17
18 let init = &ecx.eval_path(&["libc", name]);
19 assert_eq!(size, init.layout.size);
20
21 let place_bytes = ecx.read_bytes_ptr_strip_provenance(place.ptr(), cmp_size)?;
22 let init_bytes = ecx.read_bytes_ptr_strip_provenance(init.ptr(), cmp_size)?;
23
24 if place_bytes == init_bytes {
25 assert!(init_bytes.iter().any(|b| *b != crate::meta_obj::OPAQUE_OBJ_COOKIE));
28 interp_ok(true)
29 } else {
30 interp_ok(false)
31 }
32}
33
34struct PthreadMutexAttr {
37 kind: Option<MutexKind>,
39}
40
41#[derive(Debug, Clone, Copy)]
43enum MutexKind {
44 Normal,
45 Default,
46 Recursive,
47 ErrorCheck,
48}
49
50impl MetaObj for PthreadMutexAttr {
51 fn opaque(&self) -> bool {
52 true
53 }
54}
55
56fn mutexattr_create<'tcx>(
57 ecx: &mut MiriInterpCx<'tcx>,
58 mutexattr_ptr: &OpTy<'tcx>,
59) -> InterpResult<'tcx> {
60 let mutexattr = ecx.deref_pointer(mutexattr_ptr)?;
61 let data = PthreadMutexAttr { kind: None };
62 ecx.insert_meta(&mutexattr, Box::new(data))?;
63 interp_ok(())
64}
65
66fn mutexattr_destroy<'tcx>(
67 ecx: &mut MiriInterpCx<'tcx>,
68 mutexattr_ptr: &OpTy<'tcx>,
69) -> InterpResult<'tcx> {
70 let mutexattr = ecx.deref_pointer(mutexattr_ptr)?;
71 ecx.remove_meta::<PthreadMutexAttr>(&mutexattr)?.ok_or_else(|| {
72 err_ub_format!("`pthread_mutexattr_t` has not been initialized or has been tampered with")
73 })?;
74 interp_ok(())
75}
76
77fn mutexattr_get_data<'tcx, 'a>(
80 ecx: &'a mut MiriInterpCx<'tcx>,
81 mutexattr_ptr: &OpTy<'tcx>,
82) -> InterpResult<'tcx, &'a mut PthreadMutexAttr>
83where
84 'tcx: 'a,
85{
86 let mutexattr = ecx.deref_pointer(mutexattr_ptr)?;
87 ecx.get_or_insert_meta(&mutexattr, |_ecx| {
88 throw_ub_format!("`pthread_mutexattr_t` has not been initialized or has been tampered with")
89 })
90}
91
92fn mutexattr_translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> Option<MutexKind> {
94 Some(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) {
95 MutexKind::Normal
96 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
97 MutexKind::ErrorCheck
98 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
99 MutexKind::Recursive
100 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") {
101 MutexKind::Default
104 } else {
105 return None;
106 })
107}
108
109#[derive(Debug, Clone)]
112struct PthreadMutex {
113 mutex_ref: MutexRef,
114 kind: MutexKind,
115}
116
117impl MetaObj for PthreadMutex {
118 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
119 if !self.mutex_ref.queue_is_empty() {
120 throw_ub_format!(
121 "accessing the memory backing a `pthread_mutex_t` is forbidden while the queue is non-empty"
122 );
123 }
124 interp_ok(())
125 }
126
127 fn opaque(&self) -> bool {
128 true
129 }
130}
131
132fn mutex_create<'tcx>(
134 ecx: &mut MiriInterpCx<'tcx>,
135 mutex_ptr: &OpTy<'tcx>,
136 kind: MutexKind,
137) -> InterpResult<'tcx> {
138 let mutex = ecx.deref_pointer(mutex_ptr)?;
139 let data = PthreadMutex { mutex_ref: MutexRef::new(), kind };
140 ecx.insert_meta(&mutex, Box::new(data))?;
141 interp_ok(())
142}
143
144fn mutex_get_data<'tcx, 'a>(
147 ecx: &'a mut MiriInterpCx<'tcx>,
148 mutex_ptr: &OpTy<'tcx>,
149) -> InterpResult<'tcx, &'a mut PthreadMutex>
150where
151 'tcx: 'a,
152{
153 let mutex = ecx.deref_pointer(mutex_ptr)?;
154 ecx.get_or_insert_meta(&mutex, |ecx| {
155 let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
156 interp_ok(PthreadMutex { mutex_ref: MutexRef::new(), kind })
157 })
158}
159
160fn mutex_kind_from_static_initializer<'tcx>(
162 ecx: &MiriInterpCx<'tcx>,
163 mutex: &MPlaceTy<'tcx>,
164) -> InterpResult<'tcx, MutexKind> {
165 let prefix = match &ecx.tcx.sess.target.os {
166 Os::Android => Some(4),
169 _ => None,
170 };
171 let is_initializer = |name| is_initializer(ecx, mutex, name, prefix);
172
173 if is_initializer("PTHREAD_MUTEX_INITIALIZER")? {
177 return interp_ok(MutexKind::Default);
178 }
179 match &ecx.tcx.sess.target.os {
181 Os::Linux =>
182 if is_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP")? {
183 return interp_ok(MutexKind::Recursive);
184 } else if is_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP")? {
185 return interp_ok(MutexKind::ErrorCheck);
186 },
187 _ => {}
188 }
189 throw_ub_format!(
190 "`pthread_mutex_t` was not properly initialized at this location, or it got overwritten"
191 );
192}
193
194#[derive(Debug, Clone)]
197struct PthreadRwLock {
198 rwlock_ref: RwLockRef,
199}
200
201impl MetaObj for PthreadRwLock {
202 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
203 if !self.rwlock_ref.queue_is_empty() {
204 throw_ub_format!(
205 "accessing the memory backing a `pthread_rwlock_t` is forbidden while the queue is non-empty"
206 );
207 }
208 interp_ok(())
209 }
210
211 fn opaque(&self) -> bool {
212 true
213 }
214}
215
216fn rwlock_get_data<'tcx, 'a>(
217 ecx: &'a mut MiriInterpCx<'tcx>,
218 rwlock_ptr: &OpTy<'tcx>,
219) -> InterpResult<'tcx, &'a mut PthreadRwLock>
220where
221 'tcx: 'a,
222{
223 let rwlock = ecx.deref_pointer(rwlock_ptr)?;
224 ecx.get_or_insert_meta(
225 &rwlock,
226 |ecx| {
227 let prefix = match &ecx.tcx.sess.target.os {
228 Os::Android => Some(4),
231 _ => None,
232 };
233 if !is_initializer(
234 ecx,
235 &rwlock,
236 "PTHREAD_RWLOCK_INITIALIZER",
237 prefix,
238 )? {
239 throw_ub_format!(
240 "`pthread_rwlock_t` was not properly initialized at this location, or it got overwritten"
241 );
242 }
243 interp_ok(PthreadRwLock { rwlock_ref: RwLockRef::new() })
244 },
245 )
246}
247
248struct PthreadCondAttr {
251 clock: TimeoutClock,
252}
253
254impl MetaObj for PthreadCondAttr {
255 fn opaque(&self) -> bool {
256 true
257 }
258}
259
260fn condattr_create<'tcx>(
261 ecx: &mut MiriInterpCx<'tcx>,
262 condattr_ptr: &OpTy<'tcx>,
263) -> InterpResult<'tcx> {
264 let condattr = ecx.deref_pointer(condattr_ptr)?;
265 let data = PthreadCondAttr { clock: TimeoutClock::RealTime };
266 ecx.insert_meta(&condattr, Box::new(data))?;
267 interp_ok(())
268}
269
270fn condattr_destroy<'tcx>(
271 ecx: &mut MiriInterpCx<'tcx>,
272 condattr_ptr: &OpTy<'tcx>,
273) -> InterpResult<'tcx> {
274 let condattr = ecx.deref_pointer(condattr_ptr)?;
275 ecx.remove_meta::<PthreadCondAttr>(&condattr)?.ok_or_else(|| {
276 err_ub_format!("`pthread_condattr_t` has not been initialized or has been tampered with")
277 })?;
278 interp_ok(())
279}
280
281fn condattr_get_data<'tcx, 'a>(
284 ecx: &'a mut MiriInterpCx<'tcx>,
285 condattr_ptr: &OpTy<'tcx>,
286) -> InterpResult<'tcx, &'a mut PthreadCondAttr>
287where
288 'tcx: 'a,
289{
290 let condattr = ecx.deref_pointer(condattr_ptr)?;
291 ecx.get_or_insert_meta(&condattr, |_ecx| {
292 throw_ub_format!("`pthread_condattr_t` has not been initialized or has been tampered with")
293 })
294}
295
296#[derive(Debug, Clone)]
299struct PthreadCondvar {
300 condvar_ref: CondvarRef,
301 clock: TimeoutClock,
302}
303
304impl MetaObj for PthreadCondvar {
305 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
306 if !self.condvar_ref.queue_is_empty() {
307 throw_ub_format!(
308 "accessing the memory backing a `pthread_cond_t` is forbidden while the queue is non-empty"
309 );
310 }
311 interp_ok(())
312 }
313
314 fn opaque(&self) -> bool {
315 true
316 }
317}
318
319fn cond_create<'tcx>(
320 ecx: &mut MiriInterpCx<'tcx>,
321 cond_ptr: &OpTy<'tcx>,
322 clock: TimeoutClock,
323) -> InterpResult<'tcx> {
324 let cond = ecx.deref_pointer(cond_ptr)?;
325 let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
326 ecx.insert_meta(&cond, Box::new(data))?;
327 interp_ok(())
328}
329
330fn cond_get_data<'tcx, 'a>(
331 ecx: &'a mut MiriInterpCx<'tcx>,
332 cond_ptr: &OpTy<'tcx>,
333) -> InterpResult<'tcx, &'a mut PthreadCondvar>
334where
335 'tcx: 'a,
336{
337 let cond = ecx.deref_pointer(cond_ptr)?;
338 ecx.get_or_insert_meta(
339 &cond,
340 |ecx| {
341 let prefix = match &ecx.tcx.sess.target.os {
342 Os::Android => Some(4),
345 _ => None,
346 };
347 if !is_initializer(
348 ecx,
349 &cond,
350 "PTHREAD_COND_INITIALIZER",
351 prefix,
352 )? {
353 throw_ub_format!(
354 "`pthread_cond_t` was not properly initialized at this location, or it got overwritten"
355 );
356 }
357 interp_ok(PthreadCondvar {
359 condvar_ref: CondvarRef::new(),
360 clock: TimeoutClock::RealTime,
361 })
362 },
363 )
364}
365
366#[derive(Debug, Clone)]
369struct PthreadSem {
370 sem_ref: SemaphoreRef,
371}
372
373impl MetaObj for PthreadSem {
374 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
375 if !self.sem_ref.queue_is_empty() {
376 throw_ub_format!(
377 "accessing the memory backing a `sem_t` is forbidden while the queue is non-empty"
378 );
379 }
380 interp_ok(())
381 }
382
383 fn opaque(&self) -> bool {
384 true
385 }
386}
387
388fn sem_create<'tcx>(
389 ecx: &mut MiriInterpCx<'tcx>,
390 sem_ptr: &OpTy<'tcx>,
391 value: u32,
392) -> InterpResult<'tcx> {
393 let sem = ecx.deref_pointer(sem_ptr)?;
394 let data = PthreadSem { sem_ref: SemaphoreRef::new(value.into()) };
395 ecx.insert_meta(&sem, Box::new(data))?;
396 interp_ok(())
397}
398
399fn sem_get_data<'tcx, 'a>(
400 ecx: &'a mut MiriInterpCx<'tcx>,
401 sem_ptr: &OpTy<'tcx>,
402) -> InterpResult<'tcx, &'a mut PthreadSem>
403where
404 'tcx: 'a,
405{
406 let sem = ecx.deref_pointer(sem_ptr)?;
407 ecx.get_or_insert_meta(&sem, |_ecx| {
408 throw_ub_format!("`sem_t` value has not been initialized with `sem_init`")
409 })
410}
411
412impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
413pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
414 fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
415 let this = self.eval_context_mut();
416 mutexattr_create(this, attr_op)
417 }
418
419 fn pthread_mutexattr_settype(
420 &mut self,
421 attr_op: &OpTy<'tcx>,
422 kind_op: &OpTy<'tcx>,
423 ) -> InterpResult<'tcx, Scalar> {
424 let this = self.eval_context_mut();
425
426 let kind = this.read_scalar(kind_op)?.to_i32()?;
427 let Some(kind) = mutexattr_translate_kind(this, kind) else {
428 let einval = this.eval_libc_i32("EINVAL");
429 return interp_ok(Scalar::from_i32(einval));
430 };
431
432 let mutexattr = mutexattr_get_data(this, attr_op)?;
433 mutexattr.kind = Some(kind);
434
435 interp_ok(Scalar::from_i32(0))
436 }
437
438 fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
439 let this = self.eval_context_mut();
440 mutexattr_destroy(this, attr_op)
441 }
442
443 fn pthread_mutex_init(
444 &mut self,
445 mutex_op: &OpTy<'tcx>,
446 attr_op: &OpTy<'tcx>,
447 ) -> InterpResult<'tcx, ()> {
448 let this = self.eval_context_mut();
449
450 let attr = this.read_pointer(attr_op)?;
451 let kind = if this.ptr_is_null(attr)? {
452 MutexKind::Default
453 } else {
454 let mutexattr = mutexattr_get_data(this, attr_op)?;
455 mutexattr.kind.unwrap_or(MutexKind::Default)
456 };
457
458 mutex_create(this, mutex_op, kind)?;
459
460 interp_ok(())
461 }
462
463 fn pthread_mutex_lock(
464 &mut self,
465 mutex_op: &OpTy<'tcx>,
466 dest: &MPlaceTy<'tcx>,
467 ) -> InterpResult<'tcx> {
468 let this = self.eval_context_mut();
469
470 let mutex = mutex_get_data(this, mutex_op)?.clone();
471
472 let ret = if let Some(owner_thread) = mutex.mutex_ref.owner() {
473 if owner_thread != this.active_thread() {
474 this.mutex_enqueue_and_block(
475 mutex.mutex_ref,
476 Some((Scalar::from_i32(0), dest.clone())),
477 );
478 return interp_ok(());
479 } else {
480 match mutex.kind {
482 MutexKind::Default =>
483 throw_ub_format!(
484 "trying to acquire default mutex already locked by the current thread"
485 ),
486 MutexKind::Normal => throw_machine_stop!(TerminationInfo::LocalDeadlock),
487 MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
488 MutexKind::Recursive => {
489 this.mutex_lock(&mutex.mutex_ref)?;
490 0
491 }
492 }
493 }
494 } else {
495 this.mutex_lock(&mutex.mutex_ref)?;
497 0
498 };
499 this.write_scalar(Scalar::from_i32(ret), dest)?;
500 interp_ok(())
501 }
502
503 fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
504 let this = self.eval_context_mut();
505
506 let mutex = mutex_get_data(this, mutex_op)?.clone();
507
508 interp_ok(Scalar::from_i32(if let Some(owner_thread) = mutex.mutex_ref.owner() {
509 if owner_thread != this.active_thread() {
510 this.eval_libc_i32("EBUSY")
511 } else {
512 match mutex.kind {
513 MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
514 this.eval_libc_i32("EBUSY"),
515 MutexKind::Recursive => {
516 this.mutex_lock(&mutex.mutex_ref)?;
517 0
518 }
519 }
520 }
521 } else {
522 this.mutex_lock(&mutex.mutex_ref)?;
524 0
525 }))
526 }
527
528 fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
529 let this = self.eval_context_mut();
530
531 let mutex = mutex_get_data(this, mutex_op)?.clone();
532
533 if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
534 interp_ok(Scalar::from_i32(0))
536 } else {
537 match mutex.kind {
541 MutexKind::Default =>
542 throw_ub_format!(
543 "unlocked a default mutex that was not locked by the current thread"
544 ),
545 MutexKind::Normal =>
546 throw_ub_format!(
547 "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
548 ),
549 MutexKind::ErrorCheck | MutexKind::Recursive =>
550 interp_ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
551 }
552 }
553 }
554
555 fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
556 let this = self.eval_context_mut();
557
558 let mutex = mutex_get_data(this, mutex_op)?.clone();
561
562 if mutex.mutex_ref.owner().is_some() {
563 throw_ub_format!("destroyed a locked mutex");
564 }
565
566 let mutex_place = this.deref_pointer(mutex_op)?;
569 this.write_uninit(&mutex_place)?;
570
571 interp_ok(())
572 }
573
574 fn pthread_rwlock_rdlock(
575 &mut self,
576 rwlock_op: &OpTy<'tcx>,
577 dest: &MPlaceTy<'tcx>,
578 ) -> InterpResult<'tcx> {
579 let this = self.eval_context_mut();
580
581 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
582
583 if rwlock.rwlock_ref.is_write_locked() {
584 this.rwlock_enqueue_and_block_reader(
585 rwlock.rwlock_ref,
586 Scalar::from_i32(0),
587 dest.clone(),
588 );
589 } else {
590 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
591 this.write_int(0, dest)?;
592 }
593
594 interp_ok(())
595 }
596
597 fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
598 let this = self.eval_context_mut();
599
600 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
601
602 if rwlock.rwlock_ref.is_write_locked() {
603 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
604 } else {
605 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
606 interp_ok(Scalar::from_i32(0))
607 }
608 }
609
610 fn pthread_rwlock_wrlock(
611 &mut self,
612 rwlock_op: &OpTy<'tcx>,
613 dest: &MPlaceTy<'tcx>,
614 ) -> InterpResult<'tcx> {
615 let this = self.eval_context_mut();
616
617 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
618
619 if rwlock.rwlock_ref.is_locked() {
620 this.rwlock_enqueue_and_block_writer(
633 rwlock.rwlock_ref,
634 Scalar::from_i32(0),
635 dest.clone(),
636 );
637 } else {
638 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
639 this.write_int(0, dest)?;
640 }
641
642 interp_ok(())
643 }
644
645 fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
646 let this = self.eval_context_mut();
647
648 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
649
650 if rwlock.rwlock_ref.is_locked() {
651 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
652 } else {
653 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
654 interp_ok(Scalar::from_i32(0))
655 }
656 }
657
658 fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
659 let this = self.eval_context_mut();
660
661 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
662
663 if this.rwlock_reader_unlock(&rwlock.rwlock_ref)?
664 || this.rwlock_writer_unlock(&rwlock.rwlock_ref)?
665 {
666 interp_ok(())
667 } else {
668 throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
669 }
670 }
671
672 fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
673 let this = self.eval_context_mut();
674
675 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
678
679 if rwlock.rwlock_ref.is_locked() {
680 throw_ub_format!("destroyed a locked rwlock");
681 }
682
683 let rwlock_place = this.deref_pointer(rwlock_op)?;
686 this.write_uninit(&rwlock_place)?;
687
688 interp_ok(())
689 }
690
691 fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
692 let this = self.eval_context_mut();
693 condattr_create(this, attr_op)
694 }
695
696 fn pthread_condattr_setclock(
697 &mut self,
698 attr_op: &OpTy<'tcx>,
699 clock_id_op: &OpTy<'tcx>,
700 ) -> InterpResult<'tcx, Scalar> {
701 let this = self.eval_context_mut();
702
703 let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
704 let Some(clock) = this.parse_clockid(clock_id) else {
705 let einval = this.eval_libc_i32("EINVAL");
706 return interp_ok(Scalar::from_i32(einval));
707 };
708
709 let condattr = condattr_get_data(this, attr_op)?;
710 condattr.clock = clock;
711
712 interp_ok(Scalar::from_i32(0))
713 }
714
715 fn pthread_condattr_getclock(
716 &mut self,
717 attr_op: &OpTy<'tcx>,
718 clk_id_op: &OpTy<'tcx>,
719 ) -> InterpResult<'tcx, ()> {
720 let this = self.eval_context_mut();
721
722 let clock_id_dest = this.deref_pointer(clk_id_op)?;
723 let condattr = condattr_get_data(this, attr_op)?;
724
725 let clock_id = match condattr.clock {
726 TimeoutClock::Monotonic => this.eval_libc_i32("CLOCK_MONOTONIC"),
727 TimeoutClock::RealTime => this.eval_libc_i32("CLOCK_REALTIME"),
728 };
729 this.write_int(clock_id, &clock_id_dest)?;
730
731 interp_ok(())
732 }
733
734 fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
735 let this = self.eval_context_mut();
736 condattr_destroy(this, attr_op)
737 }
738
739 fn pthread_cond_init(
740 &mut self,
741 cond_op: &OpTy<'tcx>,
742 attr_op: &OpTy<'tcx>,
743 ) -> InterpResult<'tcx, ()> {
744 let this = self.eval_context_mut();
745
746 let attr = this.read_pointer(attr_op)?;
747 let clock = if this.ptr_is_null(attr)? {
749 TimeoutClock::RealTime
750 } else {
751 let condattr = condattr_get_data(this, attr_op)?;
752 condattr.clock
753 };
754
755 cond_create(this, cond_op, clock)?;
756
757 interp_ok(())
758 }
759
760 fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
761 let this = self.eval_context_mut();
762 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
763 this.condvar_signal(&condvar)?;
764 interp_ok(())
765 }
766
767 fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
768 let this = self.eval_context_mut();
769 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
770 while this.condvar_signal(&condvar)? {}
771 interp_ok(())
772 }
773
774 fn pthread_cond_wait(
775 &mut self,
776 cond_op: &OpTy<'tcx>,
777 mutex_op: &OpTy<'tcx>,
778 dest: &MPlaceTy<'tcx>,
779 ) -> InterpResult<'tcx> {
780 let this = self.eval_context_mut();
781
782 let data = cond_get_data(this, cond_op)?.clone();
783 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
784
785 this.condvar_wait(
786 data.condvar_ref,
787 mutex_ref,
788 None, Scalar::from_i32(0),
790 Scalar::from_i32(0), dest.clone(),
792 )?;
793
794 interp_ok(())
795 }
796
797 fn pthread_cond_timedwait(
798 &mut self,
799 cond_op: &OpTy<'tcx>,
800 mutex_op: &OpTy<'tcx>,
801 timeout_op: &OpTy<'tcx>,
802 dest: &MPlaceTy<'tcx>,
803 macos_relative_np: bool,
804 ) -> InterpResult<'tcx> {
805 let this = self.eval_context_mut();
806
807 let data = cond_get_data(this, cond_op)?.clone();
808 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
809
810 let Some(duration) = this.read_timespec(&this.deref_pointer(timeout_op)?)? else {
812 let einval = this.eval_libc("EINVAL");
813 this.write_scalar(einval, dest)?;
814 return interp_ok(());
815 };
816
817 let (clock, style) = if macos_relative_np {
818 (TimeoutClock::Monotonic, TimeoutStyle::Relative)
821 } else {
822 if data.clock == TimeoutClock::RealTime {
823 this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
824 }
825
826 (data.clock, TimeoutStyle::Absolute)
827 };
828
829 this.condvar_wait(
830 data.condvar_ref,
831 mutex_ref,
832 Some(this.machine.timeout(clock, style, duration)),
833 Scalar::from_i32(0),
834 this.eval_libc("ETIMEDOUT"), dest.clone(),
836 )?;
837
838 interp_ok(())
839 }
840
841 fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
842 let this = self.eval_context_mut();
843
844 let condvar = &cond_get_data(this, cond_op)?.condvar_ref;
847 if !condvar.queue_is_empty() {
848 throw_ub_format!("destroying an awaited conditional variable");
849 }
850
851 let cond_place = this.deref_pointer(cond_op)?;
854 this.write_uninit(&cond_place)?;
855
856 interp_ok(())
857 }
858
859 fn pthread_sem_init(
860 &mut self,
861 sem_op: &OpTy<'tcx>,
862 pshared_op: &OpTy<'tcx>,
863 value_op: &OpTy<'tcx>,
864 dest: &MPlaceTy<'tcx>,
865 ) -> InterpResult<'tcx> {
866 let this = self.eval_context_mut();
867
868 let pshared = this.read_scalar(pshared_op)?.to_i32()?;
869 let value = this.read_scalar(value_op)?.to_u32()?;
870
871 let _ignore = pshared;
873
874 sem_create(this, sem_op, value)?;
875
876 this.write_int(0, dest)?; interp_ok(())
878 }
879
880 fn pthread_sem_wait(
881 &mut self,
882 sem_op: &OpTy<'tcx>,
883 dest: &MPlaceTy<'tcx>,
884 ) -> InterpResult<'tcx> {
885 let this = self.eval_context_mut();
886
887 let data = sem_get_data(this, sem_op)?.clone();
888 let dest = dest.clone();
889 this.semaphore_decrement(
890 data.sem_ref,
891 None,
892 callback!(
893 @capture<'tcx> {
894 dest: MPlaceTy<'tcx>,
895 }
896 |ecx, unblock: UnblockKind| {
897 assert_eq!(unblock, UnblockKind::Ready);
898 ecx.write_int(0,&dest)?;
899 interp_ok(())
900 }
901 ),
902 )?;
903
904 interp_ok(())
905 }
906
907 fn pthread_sem_timedwait(
908 &mut self,
909 sem_op: &OpTy<'tcx>,
910 abs_timeout_op: &OpTy<'tcx>,
911 dest: &MPlaceTy<'tcx>,
912 ) -> InterpResult<'tcx> {
913 let this = self.eval_context_mut();
914 this.check_no_isolation("`pthread_sem_timedwait`")?;
915
916 let abs_timeout = this.deref_pointer(abs_timeout_op)?;
917
918 let Some(duration) = this.read_timespec(&abs_timeout)? else {
919 return this.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
920 };
921 let deadline =
922 this.machine.timeout(TimeoutClock::RealTime, TimeoutStyle::Absolute, duration);
923 let data = sem_get_data(this, sem_op)?.clone();
924 let dest = dest.clone();
925 this.semaphore_decrement(
926 data.sem_ref,
927 Some(deadline),
928 callback!(
929 @capture<'tcx> {
930 dest: MPlaceTy<'tcx>,
931 }
932 |ecx, unblock: UnblockKind| {
933 match unblock {
934 UnblockKind::Ready =>
935 ecx.write_int(0,&dest),
936 UnblockKind::TimedOut =>
937 ecx.set_errno_and_return_neg1(LibcError("ETIMEDOUT"), &dest),
938 }
939 }
940 ),
941 )?;
942
943 interp_ok(())
944 }
945
946 fn pthread_sem_post(
947 &mut self,
948 sem_op: &OpTy<'tcx>,
949 dest: &MPlaceTy<'tcx>,
950 ) -> InterpResult<'tcx> {
951 let this = self.eval_context_mut();
952
953 let data = sem_get_data(this, sem_op)?.clone();
954 this.semaphore_increment(&data.sem_ref)?;
955
956 this.write_int(0, dest)?; interp_ok(())
958 }
959
960 fn pthread_sem_destroy(
961 &mut self,
962 sem_op: &OpTy<'tcx>,
963 dest: &MPlaceTy<'tcx>,
964 ) -> InterpResult<'tcx> {
965 let this = self.eval_context_mut();
966
967 let data = sem_get_data(this, sem_op)?.clone();
968 if !data.sem_ref.queue_is_empty() {
969 throw_ub_format!("destroying an awaited semaphore");
970 }
971
972 let sem_place = this.deref_pointer(sem_op)?;
975 this.write_uninit(&sem_place)?;
976
977 this.write_int(0, dest)?; interp_ok(())
979 }
980}