mailing list of musl libc
 help / color / mirror / code / Atom feed
From: Joakim Sindholt <opensource@zhasha.com>
To: musl@lists.openwall.com
Subject: Re: [musl] Suggestion for thread safety
Date: Mon, 28 Feb 2022 16:50:51 +0100	[thread overview]
Message-ID: <20220228165051.dd53c432ccd85517cb9513cd@zhasha.com> (raw)
In-Reply-To: <CAOZ3c1pvia0_BabcKoJjP5GbZH7k8xHSa_CzLBvSJOskYfeabQ@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1881 bytes --]

On Mon, 28 Feb 2022 14:43:36 +0000, Lee Shallis <gb2985@gmail.com> wrote:
> Seems the wait just wasn't long enough, at about 4 yields onwards the
> results become consistent success, I've attached the file I did the
> experiments in, I even tried it under -O3 and no exits were
> encountered, so yes my method works, just needs a bit more wait time
> for extreme cases

Between the lines
> if ( !(shared->tid) )
and
> shared->tid = tid;
the kernel might suspend the running thread and allow the other to run,
or you might simply get unlucky and have the two threads do the checks
close enough to simultaneously that the memory hasn't been synchronized
yet. Either way you end up with both threads seeing that shared->tid is
zero and both of them writing their tids to it, and thus both enter the
critical section at the same time. And so the lock fails at the very
first hurdle: mutual exclusion. No amount of sleeping will make the bug
go away, only slightly more difficult to trigger.
The point of the clock_nanosleep call was to force a reschedule while
holding the lock. This also increases the runtime inside the lock which
in this case increases the likelihood that the thread trying to take the
lock will be waiting for it and end up racing with the thread that
currently has it when it unlocks and tries to relock it.

Now that you've inserted lots of sched_yield()s your lock is not only
still broken (in more ways than the one we've been trying to get you to
understand) but also extremely slow.

As a hint for your future education: the first (and far from only) thing
you'll need is compare-and-swap, aka. CAS.
You can read up on this class of bugs if you'd like. It's called "Time
Of Check to Time Of Use" or "TOCTOU" for short.

I didn't even need to poke at the code this time as the code you sent
breaks just the same on my machine.

I hope you'll learn from this.

[-- Attachment #2: not-on-my-machine.txt --]
[-- Type: text/plain, Size: 2849 bytes --]

zhasha@wirbelwind /home/zhasha ; cat lock.c
#define _GNU_SOURCE
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

typedef unsigned int uint;
typedef struct _LOCK { uint num; pid_t tid; } LOCK;

void LockSiData( LOCK *shared )
{
        pid_t tid = gettid();
        uint const yield = 4;
        uint i;
        /*
        struct timespec ts = {0};
        ts.tv_nsec = 1;
        */

        while ( shared->tid != tid )
        {
                if ( !(shared->tid) )
                        shared->tid = tid;
                //clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0);
                for ( i = 0; i < yield; ++i )
                        sched_yield();
        }

        shared->num++;
}

void FreeSiData( LOCK *shared )
{
        pid_t tid = gettid();
        if ( shared->tid != tid )
                return;
        shared->num--;
        if ( shared->num )
                return;
        shared->tid = (pid_t)0;
        //sched_yield();
}

volatile uint quit = 0;
volatile uint data = 0;
LOCK lock = {0};

void* thread( void *ud )
{
        uint i;
        struct timespec ts = {0};
        ts.tv_nsec = 1;
        (void)ud;

        while ( quit < CLOCKS_PER_SEC )
        {
                LockSiData( &lock );

                i = data++;
                if (i != 0)
                {
                        flockfile( stdout );
                        printf("var = %u, expected 0\n", i);
                        funlockfile( stdout );
                        FreeSiData( &lock );
                        exit(1);
                }

                /* Why is needed for the test? */
                clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0);

                i = data--;
                if (i != 1)
                {
                        flockfile( stdout );
                        printf("var = %u, expected 1\n", i);
                        funlockfile( stdout );
                        FreeSiData( &lock );
                        exit(1);
                }

                ++quit;

                FreeSiData( &lock );
        }
        return ud;
}

int main()
{
        pthread_t pt;
        int i;

        for (i = 0; i < 2; i++)
        {
                if ((errno = pthread_create(&pt, 0, thread, 0)) != 0 )
                {
                        flockfile( stdout );
                        printf("pthread_create failed: %m\n");
                        funlockfile( stdout );
                        return 1;
                }
        }
        pthread_exit(0);
}
zhasha@wirbelwind /home/zhasha ; gcc -lpthread -O3 lock.c
zhasha@wirbelwind /home/zhasha ; ./a.out
var = 1, expected 0
zhasha@wirbelwind /home/zhasha ; ./a.out
var = 1, expected 0
var = 2, expected 1
zhasha@wirbelwind /home/zhasha ;

  parent reply	other threads:[~2022-02-28 15:51 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-21 11:36 Lee Shallis
2022-02-21 17:42 ` Markus Wichmann
2022-02-23  0:30   ` Lee Shallis
2022-02-23 18:57     ` Markus Wichmann
2022-02-23 20:06       ` Rich Felker
2022-02-26  9:56       ` Lee Shallis
2022-02-26 11:38         ` Joakim Sindholt
2022-02-27 23:32           ` Lee Shallis
2022-02-28  0:15             ` Rich Felker
2022-02-28  8:48             ` Joakim Sindholt
2022-02-28 14:43               ` Lee Shallis
2022-02-28 15:19                 ` Rich Felker
2022-02-28 15:50                 ` Joakim Sindholt [this message]
2022-02-28 16:07                   ` Lee Shallis
2022-03-02  1:44                     ` Lee Shallis
2022-02-23  1:19 ` Rich Felker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220228165051.dd53c432ccd85517cb9513cd@zhasha.com \
    --to=opensource@zhasha.com \
    --cc=musl@lists.openwall.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).