在pthreads start_例程函数中声明数组导致分段错误 #包括 #包括 void*函数(void*arg){ int图片[4096][4096]; } int main(){ int N=10,S=10; pthread_t pids[10]; pthread_create(&pids[0],NULL,function,NULL); pthread_join(pids[0],NULL); 返回0; }

在pthreads start_例程函数中声明数组导致分段错误 #包括 #包括 void*函数(void*arg){ int图片[4096][4096]; } int main(){ int N=10,S=10; pthread_t pids[10]; pthread_create(&pids[0],NULL,function,NULL); pthread_join(pids[0],NULL); 返回0; },c,segmentation-fault,pthreads,stack-overflow,C,Segmentation Fault,Pthreads,Stack Overflow,我使用:gcc test.c-pthread编译了上述代码 在运行可执行文件时,它崩溃,显示:分段错误 但是,如果我删除int图片[4096][4096]定义,它不会崩溃 这可能是什么原因?我生成了核心转储文件。我运行了核心转储文件。它给了我以下信息: #include <stdio.h> #include <pthread.h> void* function(void* arg){ int picture[4096][4096]; } int main(){

我使用:
gcc test.c-pthread
编译了上述代码

在运行可执行文件时,它崩溃,显示:
分段错误

但是,如果我删除
int图片[4096][4096]定义,它不会崩溃


这可能是什么原因?

我生成了核心转储文件。我运行了核心转储文件。它给了我以下信息:

#include <stdio.h>
#include <pthread.h>

void* function(void* arg){
  int picture[4096][4096];
}

int main(){
  int N=10, S=10;
  pthread_t pids[10];
  pthread_create(&pids[0], NULL, function, NULL);
  pthread_join(pids[0], NULL);
  return 0;
}
Pthred_kk.c:5处函数(arg=)中的0 0x00005643352ba745 图片= #在pthread_create的start_线程(arg=0x7fe80f055700)中有1 0x00007fe80f6526db。c:463 pd=0x7fe80f055700 现在= 放松{buf={cancel{jmp{buf={140634661148416、8554578219241222147、140634661146560、0、1407249340640、, -8545604918547140605,-8545605192128745469},mask___saved=0},priv={pad={0x0,0x0,0x0,0x0},data={prev=0x0, cleanup=0x0,canceltype=0} 非首次呼叫= #位于../sysdeps/unix/sysv/linux/x86_64/clone.S:95的clone()中的2 0x00007fe80f37b88f

picture=崩溃程序是:

#包括
#包括
void*函数(void*arg)
{
int picture[4096][4096];//4096*4096*sizeof(int)=67108864字节=64 MB
}
int main()
{
pthread_t pids[10];
pthread_create(&pids[0],NULL,function,NULL);
pthread_join(pids[0],NULL);
返回0;
}
程序在执行时崩溃:

$gcc p.c-lpthread
美元/年
分段故障(堆芯转储)
线程堆栈布局 GLIBC/pthread中线程的默认堆栈大小为8MB。在线程创建时,线程描述符(也称为任务控制块(TCB))存储在堆栈底部,并在堆栈顶部设置一个红色区域(没有读/写权限的4KB保护页)。堆栈从高地址增长到低地址

strace
控制下的程序结果:

#0  0x00005643352ba745 in function (arg=<error reading variable: Cannot access memory at address 0x7fe80b054ed8>) at Pthred_kk.c:5
        picture = <error reading variable picture (value requires 67108864 bytes, which is more than max-value-size)>
#1  0x00007fe80f6526db in start_thread (arg=0x7fe80f055700) at pthread_create.c:463
        pd = 0x7fe80f055700
        now = <optimized out>
        unwind_buf = {cancel_jmp_buf = {{jmp_buf = {140634661148416, 8554578219241222147, 140634661146560, 0, 0, 140724934020640, 
                -8545604918547140605, -8545605192128745469}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, 
              cleanup = 0x0, canceltype = 0}}}
        not_first_call = <optimized out>
#2  0x00007fe80f37b88f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
为什么你的程序崩溃了 线程入口点定义了一个
4096x4096x4
字节的表,该表等于64 MB。这对于8MB长的堆栈区域来说太多了。但是,由于函数定义了一个巨大的本地表,但没有对它的读/写访问权限,因此我们可以预期不会发生崩溃。因此,不应发生崩溃

strace
日志显示,在访问地址0x7fee8d4dcef0时发生崩溃,地址0x7fee8d4dcef0位于已分配内存区域的堆栈区域上方:
[pid 3338]--SIGSEGV{si_signo=SIGSEGV,si_code=SEGV_ACCERR,si_addr=0x7fee8d4dcef0}--

它实际上在保护页中

++-------------+0x7fee8d4dc000
|      |                    |
4 KB | |红色区域0x00005594eb9461a0:orq$0x0,(%rsp)
0x00005594eb9461a5:cmp%r11,%rsp
0x00005594eb9461a8:jne 0x5594eb946199
0x00005594eb9461aa:低于$0x20,%rsp
0x00005594eb9461ae:mov%rdi,-0x4000018(%rbp)
0x00005594eb9461b5:mov%fs:0x28%rax
0x00005594eb9461be:mov%rax,-0x8(%rbp)
0x00005594eb9461c2:xor%eax,%eax
57 int图片[4096][4096];
58  }
上面线程入口点的反汇编代码显示,
gcc
每4kb(内存页大小)生成一次堆栈访问。它首先用本地表开头的地址设置寄存器(0x4000000为4096x4096xsizeof(int)=67108864字节):

0x00005594eb946191:lea-0x4000000(%rsp),%r11
然后,它以每4096字节(0x1000)0循环“oring”堆栈内容:

0x00005594eb946199:sub$0x1000,%rsp
=>0x00005594eb9461a0:orq$0x0,(%rsp)
0x00005594eb9461a5:cmp%r11,%rsp
0x00005594eb9461a8:jne 0x5594eb946199
因此,崩溃是因为在某个时候,
orq
指令发生在堆栈的保护页中

注意事项

  • 生成“显然无用”的代码的原因是针对堆栈冲突类漏洞的保护,如本文所述
  • 当然,使用优化选项编译相同的代码不会触发任何崩溃,因为函数()不包含任何代码:
$gcc p.c-lpthread-O2
美元/年
函数()的优化反汇编代码是一个简单的“返回”:

$objdump-S a.out
[...]
00000000000011f0:
11f0:f3 0f 1e fa endbr64
11f4:c3 retq
11f5:66 2e 0f 1f 84 00 nopw%cs:0x0(%rax,%rax,1)
11fc:00
11ff:90无
如何为线程设置更大的堆栈 如上所述,默认情况下,GLIBC/pthread库分配一个8MB的默认堆栈。但它还提供了设置由用户分配的堆栈或通过以下步骤定义堆栈大小的功能:

  • 定义线程属性
  • 使用设置属性中的堆栈大小
  • 将属性作为第二个参数传递给
  • 调用以释放属性
下面是该程序的增强版,它为线程定义了65 MB的堆栈:

#包括
#包括
void*函数(void*arg)
{
int picture[4096][4096];//4096*4096*sizeof(int)=67108864字节=64 MB
}
内部主(空)
{
pthread_t pids[10];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr,65*1024*1024);
pthread_create(&pids[0],&attr,function,NULL);
pthread_join(pids[0],NULL);
pthread_attr_dest
$ strace -f ./a.out
[...]
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fee8d4dc000
mprotect(0x7fee8d4dd000, 8388608, PROT_READ|PROT_WRITE) = 0
brk(NULL)                               = 0x556cf1b72000
brk(0x556cf1b93000)                     = 0x556cf1b93000
clone(child_stack=0x7fee8dcdbfb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTIDstrace: Process 3338 attached
, parent_tid=[3338], tls=0x7fee8dcdc700, child_tidptr=0x7fee8dcdc9d0) = 3338
[pid  3338] set_robust_list(0x7fee8dcdc9e0, 24 <unfinished ...>
[pid  3337] futex(0x7fee8dcdc9d0, FUTEX_WAIT, 3338, NULL <unfinished ...>
[pid  3338] <... set_robust_list resumed>) = 0
[pid  3338] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7fee8d4dcef0} ---
[pid  3337] <... futex resumed>)        = ?
[pid  3338] +++ killed by SIGSEGV (core dumped) +++
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)