diff --git a/tls/.gitignore b/tls/.gitignore new file mode 100644 index 0000000..6cc73e7 --- /dev/null +++ b/tls/.gitignore @@ -0,0 +1,4 @@ +/__thread +/__thread-static +/tsd +/tsd-static diff --git a/tls/Makefile b/tls/Makefile new file mode 100644 index 0000000..ab69ae6 --- /dev/null +++ b/tls/Makefile @@ -0,0 +1,31 @@ +CFLAGS = -Wall +LDLIBS = -lpthread + +.PHONY: all clean + +all: __thread __thread-static tsd tsd-static indirect_modify + +__thread-static: __thread.o + $(CC) $(LDLFAGS) -static -o $@ $^ $(LDLIBS) + +__thread: __thread.o + +__thread.o: __thread.c + +tsd-static: tsd.o + $(CC) $(LDLFAGS) -static -o $@ $^ $(LDLIBS) + +tsd: tsd.o + +tsd.o: tsd.c + +indirect_modify: indirect_modify.o + +indirect_modify.o: indirect_modify.c + $(CC) $(CFLAGS) -fstack-protector-strong -c -o $@ $< + +clean: + -rm -f __thread.o __thread __thread-static + -rm -f tsd.o tsd tsd-static + -rm -f indirect_modify.o indirect_modify + -rm -f *~ diff --git a/tls/README.md b/tls/README.md new file mode 100644 index 0000000..243ddb0 --- /dev/null +++ b/tls/README.md @@ -0,0 +1,11 @@ +# Thread-Local Storage + +This is a tutorial / presentation on thread-local storage (TLS). +TLS is a per-thread memory area used to store thread-specific data. +Global data that are required to be specific per thread are usually stored as part of the TLS. + +https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter8-1/index.html + +https://chao-tic.github.io/blog/2018/12/25/tls + +https://wiki.osdev.org/Thread_Local_Storage diff --git a/tls/__thread.c b/tls/__thread.c new file mode 100644 index 0000000..6958154 --- /dev/null +++ b/tls/__thread.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +static __thread size_t me; +static __thread size_t you; +static __thread size_t them = 543; + +static void *tf(void *arg) +{ + unsigned long id = (unsigned long) arg; + static __thread size_t us = 13; + + me = 200 + id; + us += id*id; + printf("me in thread (%p): %zu\n", &me, me); + printf("you in thread (%p): %zu\n", &you, you); + printf("you in thread (%p): %zu\n", &them, them); + printf("us in thread (%p): %zu\n", &us, us); + printf("errno in thread (%p): %d\n", &errno, errno); + + return NULL; +} + +int main(void) +{ + pthread_t new; + int err; + + me = 100; + you = 999; + them = 3; + printf("me before create (%p): %zu\n", &me, me); + printf("you before create (%p): %zu\n", &you, you); + printf("them before create (%p): %zu\n", &them, them); + printf("errno before create (%p): %d\n", &errno, errno); + + err = pthread_create(&new, NULL, tf, (void *) 1); + if (err < 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + err = pthread_create(&new, NULL, tf, (void *) 2); + if (err < 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + + printf("me after create before join (%p): %zu\n", &me, me); + printf("you after create before join (%p): %zu\n", &you, you); + printf("them after create before join (%p): %zu\n", &them, them); + printf("errno after create before join (%p): %d\n", &errno, errno); + pthread_join(new, NULL); + printf("me after join (%p): %zu\n", &me, me); + printf("you after join (%p): %zu\n", &you, you); + printf("them after join (%p): %zu\n", &them, them); + printf("errno after join (%p): %d\n", &errno, errno); + + return 0; +} diff --git a/tls/indirect_modify b/tls/indirect_modify new file mode 100755 index 0000000..f6458c3 Binary files /dev/null and b/tls/indirect_modify differ diff --git a/tls/indirect_modify.c b/tls/indirect_modify.c new file mode 100644 index 0000000..18bfcf0 --- /dev/null +++ b/tls/indirect_modify.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +/* + * Offset is determined by running the program and computing + * (&age - &placeholder) / sizeof(unsigned int). + */ +#define OFFSET_FROM_LOCAL_TO_TLS 522 + +/* + * &age is at fs:0xfffffffffffffffc, i.e. tcb-0x4 + * stack guard is at fs:0x28, i.e. tcb+x028 + * offset is (0x28-(-0x04))/4 = 11 + */ +#define OFFSET_FROM_TLS_TO_STACK_GUARD 11 + +static __thread unsigned int age; + +static void *tf(void *arg) +{ + unsigned int placeholder; + unsigned int *ptr; + unsigned long *stack_guard_ptr; + + ptr = &placeholder; + printf("placeholder address: %p\n", &placeholder); + + age = 22; + printf("age (address: %p): %u\n", &age, age); + + * (ptr + OFFSET_FROM_LOCAL_TO_TLS) = 99; + printf("age (address: %p): %u\n", &age, age); + + /* Print then overwrite stack guard with some gibberish. */ + stack_guard_ptr = (unsigned long *) (ptr + OFFSET_FROM_LOCAL_TO_TLS + OFFSET_FROM_TLS_TO_STACK_GUARD); + printf("stack guard / canary (address: %p): 0x%016lx\n", stack_guard_ptr, *stack_guard_ptr); + * (stack_guard_ptr) = 0xaabbccddeeff0011; + + return NULL; +} + +int main(void) +{ + pthread_t new; + int err; + + err = pthread_create(&new, NULL, tf, (void *) 1); + if (err < 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + pthread_join(new, NULL); + + return 0; +} diff --git a/tls/tsd.c b/tls/tsd.c new file mode 100644 index 0000000..5fe9a49 --- /dev/null +++ b/tls/tsd.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +static pthread_key_t me_key; +static pthread_key_t you_key; +static pthread_key_t them_key; + +static void *tf(void *arg) +{ + pthread_setspecific(me_key, (const void *) 200); + printf("me_key in thread (%p): %u, me value in thread: %zu\n", &me_key, me_key, (size_t) pthread_getspecific(me_key)); + printf("you_key in thread (%p): %u, you value in thread: %zu\n", &you_key, you_key, (size_t) pthread_getspecific(you_key)); + printf("them_key in thread (%p): %u, them value in thread: %zu\n", &them_key, them_key, (size_t) pthread_getspecific(them_key)); + + return NULL; +} + +int main(void) +{ + pthread_t new; + int err; + + pthread_key_create(&me_key, NULL); + pthread_key_create(&you_key, NULL); + pthread_key_create(&them_key, NULL); + + pthread_setspecific(me_key, (const void *) 100); + pthread_setspecific(you_key, (const void *) 999); + pthread_setspecific(them_key, (const void *) 3); + printf("me_key before create (%p): %u, me value before create: %zu\n", &me_key, me_key, (size_t) pthread_getspecific(me_key)); + printf("you_key before create (%p): %u, me value before create: %zu\n", &you_key, you_key, (size_t) pthread_getspecific(you_key)); + printf("them_key before create (%p): %u, me value before create: %zu\n", &them_key, them_key, (size_t) pthread_getspecific(them_key)); + + err = pthread_create(&new, NULL, tf, NULL); + if (err < 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + + printf("me_key after create before join (%p): %u, me value after create before join: %zu\n", &me_key, me_key, (size_t) pthread_getspecific(me_key)); + printf("you_key after create before join (%p): %u, you value after create before join: %zu\n", &you_key, you_key, (size_t) pthread_getspecific(you_key)); + printf("them_key after create before join (%p): %u, them value beafter create before join: %zu\n", &them_key, them_key, (size_t) pthread_getspecific(them_key)); + pthread_join(new, NULL); + printf("me_key after join (%p): %u, me value after join: %zu\n", &me_key, me_key, (size_t) pthread_getspecific(me_key)); + printf("you_key after join (%p): %u, you value after join: %zu\n", &you_key, you_key, (size_t) pthread_getspecific(you_key)); + printf("them_key after join (%p): %u, them value after join: %zu\n", &them_key, them_key, (size_t) pthread_getspecific(them_key)); + + /* This is really not required, but we call it for API completeness. */ + pthread_key_delete(me_key); + pthread_key_delete(you_key); + pthread_key_delete(them_key); + + return 0; +}