#include #include #include #include #include #include #include #include #include /* * TLS functionality testing utility. */ #define __NR_set_thread_area 243 _syscall1(int, set_thread_area, struct modify_ldt_ldt_s *, info) #define __NR_get_thread_area 244 _syscall1(int, get_thread_area, struct modify_ldt_ldt_s *, info) static inline void initseg (int seg) { asm ("mov %w0,%%fs" : : "r" (seg)); } static inline unsigned char __readseg (unsigned offset) { unsigned char res; asm ("fs; movb (%1),%%al" : "=a" (res) : "r" (offset)); return res; } static inline void __writeseg (unsigned offset, unsigned char b) { asm ("fs; movb %b1,(%0)" : : "r" (offset), "r" (b)); } static void readseg (void *dst, const void *src) { *(char *)dst = __readseg((unsigned int)src); } static void writeseg (void *dst, unsigned char value) { __writeseg((unsigned int)dst, value); } unsigned char pre_data [4096] = { [ 0 ... 4095 ] = 33 }; unsigned char data [4096] = { [ 0 ... 4095 ] = 44 }; unsigned char post_data [4096] = { [ 0 ... 4095 ] = 55 }; static void print_info (struct modify_ldt_ldt_s *info) { printf("info %p:\n", info); #define P(f) printf("..."#f": %d.\n", info->##f) P(entry_number); P(base_addr); P(limit); P(seg_32bit); P(contents); P(read_exec_only); P(limit_in_pages); P(seg_not_present); P(useable); } int main (void) { int i, idx, seg, ret; unsigned int base; unsigned char result; struct modify_ldt_ldt_s info, info2; memset(&info, 0, sizeof(info)); memset(&info2, 0, sizeof(info2)); info.entry_number = -1; info.base_addr = 0; info.limit = 0xfffff; info.seg_32bit = 1; info.contents = MODIFY_LDT_CONTENTS_DATA; info.read_exec_only = 0; info.limit_in_pages = 1; info.seg_not_present = 0; data[0] = 123; data[4096] = 210; base = 0; info.base_addr = base; printf("\ndoing set_thread_area(%08x):\n", base); ret = set_thread_area(&info); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } idx = info.entry_number; seg = idx * 8 + 3; printf("got idx: %d (sel: %02x)\n", idx, seg); initseg(seg); printf("\nreading %p byte of [0x%08x] TLS:\n", &data, base); readseg (&result, &data); if (result == 123) printf("====> %d --- TEST PASSED.\n\n", result); else printf("====> %d --- TEST FAILURE!\n\n", result); info.entry_number = -1; base = (unsigned int)&data; info.base_addr = base; printf("\ndoing set_thread_area(%08x):\n", base); ret = set_thread_area(&info); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } idx = info.entry_number; seg = idx * 8 + 3; printf("got idx: %d (sel: %02x)\n", idx, seg); initseg(seg); printf("\nreading %p byte of [0x%08x] TLS:\n", &data, base); readseg (&result, 0); if (result == 123) printf("====> %d --- TEST PASSED.\n\n", result); else printf("====> %d --- TEST FAILURE!\n\n", result); printf("\nreading TLS idx %d's descriptor.\n", idx); info2.entry_number = idx; ret = get_thread_area(&info2); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } if (memcmp(&info, &info2, sizeof(info))) { printf("huh, info != info2? (%d)\n", memcmp(&info, &info2, sizeof(info))); print_info(&info); print_info(&info2); } else printf("info == info2 - TEST PASSED.\n"); printf("\nclearing TLS idx %d's descriptor.\n", idx); info.entry_number = idx; info.base_addr = 0; info.limit = 0; info.seg_32bit = 0; info.contents = 0; info.read_exec_only = 1; info.limit_in_pages = 0; info.seg_not_present = 1; ret = set_thread_area(&info); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } printf("TEST PASSED.\n"); base = (unsigned int) &data; printf("\nre-allocating TLS idx %d's descriptor.\n", idx); for (i = 0; i < 2; i++) { info.entry_number = -1; info.base_addr = base; info.limit = 0xfffff; info.seg_32bit = 1; info.contents = MODIFY_LDT_CONTENTS_DATA; info.read_exec_only = 0; info.limit_in_pages = 1; info.seg_not_present = 0; ret = set_thread_area(&info); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } if (!i && (idx != info.entry_number)) { printf("idx %d != entry_number %d! TEST FAILED!\n", idx, info.entry_number); exit(1); } idx = info.entry_number; seg = idx * 8 + 3; printf("got idx: %d (sel: %02x)\n", idx, seg); sleep(1); initseg(seg); } printf("TEST PASSED.\n\n"); printf("writing last byte of 4097 byte [0x%08x] TLS:\n", base); writeseg ((void *)4096, 234); readseg (&result, (void *)4096); if (result == 234) printf("====> %d --- TEST PASSED.\n", result); else printf("====> %d --- TEST FAILURE!.\n", result); printf("writing read-only segment [0x%08x] (should coredump):\n", base); info.entry_number = -1; info.read_exec_only = 1; base = (unsigned int)&data; info.base_addr = base; ret = set_thread_area(&info); if (ret < 0) { printf("ret: %d, TEST FAILED!\n", ret); exit(1); } idx = info.entry_number; seg = idx * 8 + 3; printf("got idx: %d (sel: %02x)\n", idx, seg); initseg(seg); writeseg ((void *)4096, 234); printf("====> %d --- TEST FAILURE!.\n", result); return 0; }