Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TSAN changes forked process exit code returned by waitpid #1751

Open
JamesShaker opened this issue Apr 26, 2024 · 0 comments
Open

TSAN changes forked process exit code returned by waitpid #1751

JamesShaker opened this issue Apr 26, 2024 · 0 comments

Comments

@JamesShaker
Copy link

Noticed something that seems like a bug, apologies if this is actually known/intended behavior!

TSAN Potential Bug Description

When a program:

  1. Triggers a thread sanitizer race warning
  2. Forks into a child process that exits
  3. Runs waitpid to retrieve the child process's exit code

The retrieved exit code is incorrect.

Version

On the system on which I observe the behavior clang --version returns:

Ubuntu clang version 17.0.6 (++20231209124227+6009708b4367-1~exp1~20231209124336.77)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

I have not tried on any other systems.

Example

Shell Interaction

$ ls
tsan_fork_demo.c
$ clang tsan_fork_demo.c -fsanitize=thread -o tsan_fork_demo
$ ./tsan_fork_demo n
Now trying forking!
(child process) Exiting with 42
SUCCESS: When *not* racing, waitpid correctly returned 42 as expected
$ ./tsan_fork_demo y
...
==================
WARNING: ThreadSanitizer: data race (pid=28705)
...
==================
Ran a race: child thread won the race!
Now trying forking!
(child process) Exiting with 42
ThreadSanitizer: reported 1 warnings
SUCCESS: When racing, waitpid *incorrectly* returned 66 (not 42) as expected
ThreadSanitizer: reported 1 warnings

Code for tsan_fork_demo.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>

#define EXIT_VAL 42

void* race_thread_code (void* arg) {
    pthread_detach(pthread_self());
    *(int*) arg = 1;
    return NULL;
}

int main (int argc, char* argv[]) {
    if (argc != 2 ||
        (argv[1][0] != 'y' && argv[1][0] != 'n') ||
        argv[1][1] != 0) {
            printf("usage: %s <y|n>\n", argv[0]);
            return -1;
    }

    int racing = argv[1][0] == 'y';

    if (racing) {
        int race_var = 0;
        pthread_t race_thread;
        pthread_create(&race_thread, NULL, race_thread_code, &race_var);
        if (race_var == 0) {
            fflush(stdout);
            fflush(stderr);
            printf("Ran a race: main thread won the race!\n");
        } else {
            fflush(stdout);
            fflush(stderr);
            printf("Ran a race: child thread won the race!\n");
        }
    }

    printf("Now trying forking!\n");
    pid_t pid = fork();

    if (pid == 0) {
        printf("(child process) Exiting with %d\n", EXIT_VAL);
        exit(EXIT_VAL);
    } else if (pid != -1) {
        int status;
        if (waitpid(pid, &status, 0) == pid) {
            if (WIFEXITED(status)) {
                if (racing && WEXITSTATUS(status) != EXIT_VAL) {
                    printf("SUCCESS: When racing, waitpid *incorrectly* returned %d (not %d) as expected\n",
                            WEXITSTATUS(status), EXIT_VAL);
                    return 0;
                } else if (!racing && WEXITSTATUS(status) == EXIT_VAL) {
                    printf("SUCCESS: When *not* racing, waitpid correctly returned %d as expected\n",
                            WEXITSTATUS(status));
                    return 0;
                }
            }
        }
    }

    printf("There was a failure in the demonstration.\n");
    return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant