1 线程的栈
函数调用的回溯,保存在栈中;
函数临时变量,参数,保存在栈中;
线程栈的双重特性:
从用法看:每个线程一个栈;
从进程资源管理角度:所有线程的栈都属于整个进程的内存资源;
在一个线程访问另一个线程的栈,是合法的,Linux 不会报错;但从用法看是错的;
例如一下代码:
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
static char *p;//bss->0
static void *thread_routine(void *arg)
{char buf[10] = {0};
p = buf;
while (1) {printf("buf is %s\n", buf);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_id1;
pthread_attr_t thread_attr;
size_t stack_size, guard_size;
int status;
status = pthread_attr_init(&thread_attr);
if (status != 0)
exit(-1);
status = pthread_create(&thread_id1, &thread_attr, thread_routine, NULL);
if (status != 0)
exit(-1);
int i = 0;
while (1) {if (p) {if (i++ % 2 == 0)
strcpy(p, "hello");
else
strcpy(p, "world");
}
sleep(4);
}
pthread_join(thread_id1, NULL);
return 0;
}
编译运行结果:
$ gcc -g -fsanitize=address stack_corruption.c
$ ./a.out
buf is
buf is
buf is
buf is
buf is hello
buf is hello
buf is hello
buf is hello
buf is world
buf is world
buf is world
buf is world
buf is hello
从主线程去访问子线程的栈,完全合法,不会报错;
但是假如内存本身越界如下:
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
static char *p;//bss->0
static void *thread_routine(void *arg)
{char buf[10] = {0};
p = buf;
while (1) {printf("buf is %s\n", buf);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_id1;
pthread_attr_t thread_attr;
size_t stack_size, guard_size;
int status;
status = pthread_attr_init(&thread_attr);
if (status != 0)
exit(-1);
status = pthread_create(&thread_id1, &thread_attr, thread_routine, NULL);
if (status != 0)
exit(-1);
int i = 0;
while (1) {if (p) {if (i++ % 2 == 0)
strcpy(p, "helloaaaaaaaaaaaaa");
else
strcpy(p, "world");
}
sleep(4);
}
pthread_join(thread_id1, NULL);
return 0;
}
结果会报错:
$ gcc -g -fsanitize=address stack_corruption.c
$ ./a.out
buf is
buf is
buf is
buf is
=================================================================
==977621==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f1724ffee6a at pc 0x7f1727f8258a bp 0x7fffa9a35910 sp 0x7fffa9a350b8
buf is
WRITE of size 26 at 0x7f1724ffee6a thread T0
#0 0x7f1727f82589 (/lib/x86_64-linux-gnu/libasan.so.4+0x7a589)
#1 0x55966a29b49f in main /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_corruption.c:41
#2 0x7f1727d3d0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#3 0x55966a29b14d in _start (/home/leon/work/myHub/linux-development/app/multi_thread/day4/a.out+0x114d)
Address 0x7f1724ffee6a is located in stack of thread T1 at offset 42 in frame
#0 0x55966a29b239 in thread_routine /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_corruption.c:11
This frame has 1 object(s):
[32, 42) 'buf' <== Memory access at offset 42 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
Thread T1 created by T0 here:
#0 0x7f1727f40b3f in pthread_create (/lib/x86_64-linux-gnu/libasan.so.4+0x38b3f)
#1 0x55966a29b43a in main /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_corruption.c:33
#2 0x7f1727d3d0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/lib/x86_64-linux-gnu/libasan.so.4+0x7a589)
Shadow bytes around the buggy address:
0x0fe3649f7d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0fe3649f7dc0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00[02]f2 f2
0x0fe3649f7dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe3649f7e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==977621==ABORTING
可见普通越界还是能检查出来;
2 栈的 Guard page
Linux 在两个 线程栈 内存之间添加一个guard page(无权限,读取触发段错误);
当发生函数调用 a ->b->c->d->e,超过栈空间 (越界) 则触发段错误;
所谓早死早超生,Guard page, 让出错现场更靠近真实原因,便于调试;
如下案例:
/*
* thread_attr.c
*
* Create a thread using a non-default attributes object,
* thread_attr. The thread reports its existence, and exits. The
* attributes object specifies that the thread be created
* detached, and, if the stacksize attribute is supported, the
* thread is given a stacksize twice the minimum value.
*/
#include <limits.h>
#include <pthread.h>
#define _POSIX_THREAD_ATTR_STACKSIZE
void err_abort(int status, char *p)
{perror("%s\n", p);
exit(status);
}
static void *access_overflow(void)
{char q[11*1024];
int i = 10;
for (i = 10;i>=0;i--) {printf("%s %d %p\n", __func__, i, &q[i*1024]);
q[i*1024] = 'a';
}
while(1);
printf("The thread is here2\n");
}
static void *thread_routine(void *arg)
{char p[5*1014];
p[4999] = 'a';
p[0] = 'a';
access_overflow();
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_id1, thread_id2;
pthread_attr_t thread_attr;
struct sched_param thread_param;
size_t stack_size, guard_size;
int status;
status = pthread_attr_init(&thread_attr);
if (status != 0)
err_abort(status, "Create attr");
/*
* Create a detached thread.
*/
status =
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort(status, "Set detach");
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
/*
* If supported, determine the default stack size and report
* it, and then select a stack size for the new thread.
*
* Note that the standard does not specify the default stack
* size, and the default value in an attributes object need
* not be the size that will actually be used. Solaris 2.5
* uses a value of 0 to indicate the default.
*/
status = pthread_attr_getstacksize(&thread_attr, &stack_size);
if (status != 0)
err_abort(status, "Get stack size");
printf("Default stack size is %u; minimum is %u\n",
stack_size, PTHREAD_STACK_MIN);
status = pthread_attr_getguardsize(&thread_attr, &guard_size);
if (status != 0)
err_abort(status, "Get guard size");
printf("Default guard size is %u\n", guard_size);
status = pthread_attr_setstacksize(&thread_attr, PTHREAD_STACK_MIN);
if (status != 0)
err_abort(status, "Set stack size");
#endif
status = pthread_create(&thread_id1, &thread_attr, thread_routine, NULL);
if (status != 0)
err_abort(status, "Create thread");
status = pthread_create(&thread_id2, &thread_attr, thread_routine, NULL);
if (status != 0)
err_abort(status, "Create thread");
while(1);
return 0;
}
线程栈分布如下图:
$ gcc -g -fsanitize=address stack_overflow.c -lpthread
$ ./a.out
Default stack size is 8388608; minimum is 16384
Default guard size is 4096
ASAN:DEADLYSIGNAL
=================================================================
==977859==ERROR: AddressSanitizer: stack-overflow on address 0x7f5b010d6de0 (pc 0x556187da830a bp 0x7f5b010d9a50 sp 0x7f5b010d6dd0 T1)
#0 0x556187da8309 in access_overflow /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_overflow.c:22
#1 0x556187da84ae in thread_routine /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_overflow.c:39
#2 0x7f5b002fb608 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9608)
#3 0x7f5b00222292 in __clone (/lib/x86_64-linux-gnu/libc.so.6+0x122292)
SUMMARY: AddressSanitizer: stack-overflow /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_overflow.c:22 in access_overflow
Thread T1 created by T0 here:
#0 0x7f5b0034db3f in pthread_create (/lib/x86_64-linux-gnu/libasan.so.4+0x38b3f)
#1 0x556187da87de in main /home/leon/work/myHub/linux-development/app/multi_thread/day4/stack_overflow.c:87
#2 0x7f5b001270b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
==977859==ABORTING
(base) leon@sys:$
正文完