Skip to content

PTHREAD_MUTEX_INITIALIZER support#3

Open
gc00 wants to merge 2 commits into
mainfrom
pthread-mutex-initializer-support
Open

PTHREAD_MUTEX_INITIALIZER support#3
gc00 wants to merge 2 commits into
mainfrom
pthread-mutex-initializer-support

Conversation

@gc00

@gc00 gc00 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Porting support for PTHREAD_MUTEX_INITIALIZER to deep-debug

@gc00 gc00 requested a review from maxwellpirtle June 30, 2026 17:36
@gc00 gc00 added the enhancement New feature or request label Jun 30, 2026

@maxwellpirtle maxwellpirtle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We saw this fix worked in McMini before, but after considering it further, I think a better solution would be to perform the check for whether we think the mutex was statically initialized within libmcmini.so itself and not on the model size. We then inform the model of the result of the check instead.

Specifically, when we encounter pthread_mutex_lock and friends in libmcmini.so, we should check if the mutex appears to be PTHREAD_MUTEX_INITIALIZER using a simple memcmp. If the mutex appears to be statically initialized, we would record this information inside

volatile runner_mailbox& rmb

The model could then check this extra data inside to yield something like

if (rmb.cnts[0] == 1) {
 // Indicates that `memcmp(remote_mut, PTHREAD_MUTEX_INITIALIZER) == 0` so initialize the mutex in the model
} else {
// memcmp(remote_mut, PTHREAD_MUTEX_INITIALIZER) != 0: UB
throw undefined_behavior_exception(
          "Attempting to lock an uninitialized mutex");
}

This keeps the model-side code cleaner. Reasoning about multiple processes is challenging. Deep debug attempts to reduce the burden of needing to reason about multiple processes in any given function as much as possible. In general, any additional information that libmcmini.so needs to communicate with the model should be passed through the specific payloads in the mailboxes.

@maxwellpirtle

maxwellpirtle commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Specifically, look at wrappers.c

volatile runner_mailbox *thread_get_mailbox() {
  assert(!is_checkpoint_thread());
  assert(tid_self != RID_INVALID);
  assert(tid_self != RID_CHECKPOINT_THREAD);
  return &((volatile struct mcmini_shm_file *)(global_shm_start))
              ->mailboxes[tid_self];
}

You'll see examples such as this in mc_pthread_mutex_unlock

//...
    case TARGET_BRANCH:
    case TARGET_BRANCH_AFTER_RESTART: {
      volatile runner_mailbox *mb = thread_get_mailbox();
      mb->type = MUTEX_UNLOCK_TYPE;
      memcpy_v(mb->cnts, &mutex, sizeof(mutex));
      thread_wake_scheduler_and_wait();
      return libpthread_mutex_unlock(mutex);
    }

we could add

memcpy_v(mb->cnts, &mutex, sizeof(mutex));
static int static_mutex = PTHREAD_MUTEX_INITIALIZER;
int is_static_mutex = memcmp(&mutex, & static_mutex, sizeof(mutex)) == 0;
memcpy_v(mb->cnts[sizeof(mutex)], &is_static_mutex, sizeof(mutex)); // NEW CODE

and then the model code becomes

if (rmb.cnts[sizeof(pthread_mutex_t) == 1) {
  // Statically initialized mutex that we didn't see, initialize it now
  m.observe_object(remote_mut, new mutex(mutex::state::unlocked, remote_mut));
else {
  throw undefined_behavior_exception(
          "Attempting to lock an uninitialized mutex");
}

Corresponding changes would be needed in other locations in libmcmini.so

@gc00 gc00 changed the title Pthread mutex initializer support PTHREAD_MUTEX_INITIALIZER support Jul 1, 2026
@gc00

gc00 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Hi @maxwellpirtle , Thanks for reviewing this. I agree that if we can move this to the model side, this would be an improvement. And presumably, this would be easier to do in deep-debug, rather than classic McMini.

Since you are more familiar with the deep-debug code, is there any chance that you could do this? It would probably take me more time than it would you. Hopefully, with tools like Claude, it would be fairly fast for you to do this. (In my case, I simply asked Claude to port the classic McMini code to deep-debug.)

A mutex declared with PTHREAD_MUTEX_INITIALIZER never flows through the
pthread_mutex_init wrapper, so the model never observes it being
initialized. Previously mutex_lock_callback threw "uninitialized mutex"
in that case (the `// TODO: add code from Gene's PR here` placeholder).

Port the original McMini behavior: on a lock of a mutex the model has
not seen, read the target's memory via process_vm_readv and compare
against PTHREAD_MUTEX_INITIALIZER. On a match, lazily register the mutex
as initialized+unlocked; otherwise report undefined behavior. Unlock is
left reporting undefined behavior for an unknown mutex (faithful to the
original, which does not lazily register on unlock).

- model_to_system_map: expose get_target_pid() so callbacks can read the
  live target's memory.
- coordinator: implement get_target_pid() from the current process handle.
- mutex.cpp: add the process_vm_readv validation helper and port the lock
  callback; fix the mis-copied unlock message.

A FIXME documents that, as in the original McMini, this only detects an
uninitialized mutex for stack/garbage mutexes; a never-initialized
data/heap mutex is zero-filled (== PTHREAD_MUTEX_INITIALIZER on glibc)
and still slips through.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gc00 gc00 force-pushed the pthread-mutex-initializer-support branch from 61b13e8 to 5f1f26d Compare July 1, 2026 03:45
Exercises the reject path of the PTHREAD_MUTEX_INITIALIZER support: a
stack-allocated pthread_mutex_t filled with non-zero garbage (so it cannot
be mistaken for the all-zero PTHREAD_MUTEX_INITIALIZER) is locked without
initialization. McMini reports:

  UNDEFINED BEHAVIOR:
  Attempting to lock an uninitialized mutex

Note the data/heap case is intentionally not detected (zero-filled ==
PTHREAD_MUTEX_INITIALIZER); see the FIXME in
src/mcmini/model/transitions/mutex.cpp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants