7

Ho riscontrato un problema con un arresto anomalo dovuto alla restituzione di un valore di un set di bit quando il set di bit è ampio. È un bug del compilatore o ho fatto qualcosa per errore che ha causato un comportamento indefinito?Errore di segmento su GCC4.6 quando si tenta di spostare un grosso set di bit, si tratta di un bug del compilatore?

Il codice seguente si arresta in modo anomalo su GCC 4.6.3 con il flag impostato -std=c++0x.

#include <bitset> 

// typedef std::bitset<0xffff> uut; 
typedef std::bitset<0xffffff> uut; 

struct foo { 
    foo(uut b) 
    : b_(std::move(b)) 
    { 
    } 

    uut b_; 
}; 

uut make_bits(int) 
{ 
    uut bits; 

    // Only works for 0xffff: 
    return std::move(bits); 
    // Works for both 0xffff and 0xffffff: 
    //return bits; 
} 

int main() 
{ 
    foo(make_bits(0)); 
} 

Stranamente se rimuovere il parametro int è ok, forse questo fa sì che la funzione da inline?

Come @unwind suggerito, ecco l'output eseguito in valgrind ./a.out:

==24780== Memcheck, a memory error detector 
==24780== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==24780== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==24780== Command: ./a.out 
==24780== 
==24780== Warning: client switching stacks? SP change: 0x7ff000068 --> 0x7fea00058 
==24780==   to suppress, use: --max-stackframe=6291472 or greater 
==24780== Invalid write of size 8 
==24780== at 0x4005E5: main (in /home/sam/scratch/a.out) 
==24780== Address 0x7fea00058 is on thread 1's stack 
==24780== 
==24780== Warning: client switching stacks? SP change: 0x7fea00050 --> 0x7fe800040 
==24780==   to suppress, use: --max-stackframe=2097168 or greater 
==24780== Invalid write of size 8 
==24780== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24780== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24780== Address 0x7fe800048 is on thread 1's stack 
==24780== 
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV) 
==24780== Access not within mapped region at address 0x7FE800048 
==24780== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24780== If you believe this happened as a result of a stack 
==24780== overflow in your program's main thread (unlikely but 
==24780== possible), you can try to increase the size of the 
==24780== main thread stack using the --main-stacksize= flag. 
==24780== The main thread stack size used in this run was 8388608. 
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV) 
==24780== Access not within mapped region at address 0x7FE800039 
==24780== at 0x4A255A0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so) 
==24780== If you believe this happened as a result of a stack 
==24780== overflow in your program's main thread (unlikely but 
==24780== possible), you can try to increase the size of the 
==24780== main thread stack using the --main-stacksize= flag. 
==24780== The main thread stack size used in this run was 8388608. 
==24780== 
==24780== HEAP SUMMARY: 
==24780==  in use at exit: 0 bytes in 0 blocks 
==24780== total heap usage: 0 allocs, 0 frees, 0 bytes allocated 
==24780== 
==24780== All heap blocks were freed -- no leaks are possible 
==24780== 
==24780== For counts of detected and suppressed errors, rerun with: -v 
==24780== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2) 

E con valgrind --max-stacksize=99999999 ./a.out, come valgrind mi ha spinto a:

==24790== Memcheck, a memory error detector 
==24790== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==24790== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==24790== Command: ./a.out 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7ff000068 --> 0x7fea00058 
==24790==   to suppress, use: --max-stackframe=6291472 or greater 
==24790== Invalid write of size 8 
==24790== at 0x4005E5: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fea00058 is on thread 1's stack 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7fea00050 --> 0x7fe800040 
==24790==   to suppress, use: --max-stackframe=2097168 or greater 
==24790== Invalid write of size 8 
==24790== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x400576: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800044 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x400590: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800038 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x4C2E0E0: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400594: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800050 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x4C2E0EB: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400594: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2E10E: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800038 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4005A7: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D10D: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fee00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D11A: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe9fffc8 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D108: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe9fffc0 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4005C1: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7fe800040 --> 0x7fea00050 
==24790==   to suppress, use: --max-stackframe=2097168 or greater 
==24790==   further instances of this message will not be shown. 
==24790== Invalid read of size 8 
==24790== at 0x4005C9: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4E5376C: (below main) (libc-start.c:226) 
==24790== Address 0x7fea00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D000: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00060 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D004: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fea00060 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D00F: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00070 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D108: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D11A: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D10D: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7feffffe0 is on thread 1's stack 
==24790== 
==24790== 
==24790== HEAP SUMMARY: 
==24790==  in use at exit: 0 bytes in 0 blocks 
==24790== total heap usage: 0 allocs, 0 frees, 0 bytes allocated 
==24790== 
==24790== All heap blocks were freed -- no leaks are possible 
==24790== 
==24790== For counts of detected and suppressed errors, rerun with: -v 
==24790== ERROR SUMMARY: 2097097 errors from 19 contexts (suppressed: 2 from 2) 
+1

Quel bitset è _huge_! Sei consapevole del fatto che contiene più di 2 megabyte di bit? –

+4

Direi che è un overflow dello stack. – Flexo

+0

@Lightness Races in Orbit: sì! :) –

risposta

2

Possiamo vedere esattamente ciò che GCC sta facendo sotto il cappuccio compilando entrambi i casi con -S:

g++-4.6 -std=c++0x test.cc -S -fverbose-asm 

E t gallina utilizzando diff per confrontare le uscite:

diff -rNu move.s ret.s |c++filt  
--- move.s 2015-05-21 14:00:49.097524035 +0100 
+++ ret.s 2015-05-21 14:00:40.021510019 +0100 
@@ -79,23 +79,13 @@ 
    .cfi_offset 5, -8 
    movl %esp, %ebp #, 
    .cfi_def_cfa_register 5 
- subl $2097176, %esp #, 
- leal -2097160(%ebp), %eax #, tmp60 
+ subl $24, %esp #, 
+ movl 8(%ebp), %eax # .result_ptr, tmp59 
    movl $2097152, %edx #, tmp61 
    movl %edx, 8(%esp) # tmp61, 
    movl $0, 4(%esp) #, 
    movl %eax, (%esp) # tmp60, 
    call memset # 
- leal -2097160(%ebp), %eax #, tmp64 
- movl %eax, (%esp) # tmp64, 
- call std::remove_reference<std::bitset<16777215u>&>::type&& std::move<std::bitset<16777215u>&>(std::bitset<16777215u>&) # 
- movl %eax, %edx #, D.21547 
- movl 8(%ebp), %eax # .result_ptr, tmp65 
- movl $2097152, %ecx #, tmp68 
- movl %ecx, 8(%esp) # tmp68, 
- movl %edx, 4(%esp) # tmp67, 
- movl %eax, (%esp) # tmp66, 
- call memcpy # 
    movl 8(%ebp), %eax # .result_ptr, 
    leave 
    .cfi_restore 5 

(+ esistono righe segnate solo nel ritorno per caso valore, le linee con - esistono solo nel caso move).

C'è molto più manipolazione del puntatore dello stack in corso nel caso di spostamento (e alcuni numeri molto grandi in quel caso). È cruciale che poi finisce con una chiamata memcpy che copia i risultati nuovamente nello stack.

La mia analisi di ciò è che per il ritorno in caso di valore c'è in realtà un altro evento di ottimizzazione, il che significa che il temporaneo non utilizzato all'interno principale viene omesso interamente per il ritorno per caso valore, ma non per il caso di spostamento.

Possiamo confermare che ulteriormente eseguendo la stessa analisi sul ritorno per caso il valore con -O0 disabilitando tutti optimsisations e di vedere ciò che accade:

diff -Nru noopt.s ret.s 
--- noopt.s 2015-05-21 14:06:14.798028762 +0100 
+++ ret.s 2015-05-21 14:00:40.021510019 +0100 
@@ -3,7 +3,7 @@ 
# compiled by GNU C version 4.6.4, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1 
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 
# options passed: -imultilib . -imultiarch i386-linux-gnu -D_GNU_SOURCE 
-# test.cc -mtune=generic -march=i686 -O0 -std=c++0x -fverbose-asm 
+# test.cc -mtune=generic -march=i686 -std=c++0x -fverbose-asm 
# -fstack-protector 
# options enabled: -fasynchronous-unwind-tables -fauto-inc-dec 
# -fbranch-count-reg -fcommon -fdelete-null-pointer-checks -fdwarf2-cfi-asm 
@@ -79,23 +79,13 @@ 
    .cfi_offset 5, -8 
    movl %esp, %ebp #, 
    .cfi_def_cfa_register 5 
- subl $2097176, %esp #, 
- leal -2097160(%ebp), %eax #, tmp60 
+ subl $24, %esp #, 
+ movl 8(%ebp), %eax # .result_ptr, tmp59 
    movl $2097152, %edx #, tmp61 
    movl %edx, 8(%esp) # tmp61, 
    movl $0, 4(%esp) #, 
    movl %eax, (%esp) # tmp60, 
    call memset # 
- leal -2097160(%ebp), %eax #, tmp64 
- movl %eax, (%esp) # tmp64, 
- call _ZSt4moveIRSt6bitsetILj16777215EEEONSt16remove_referenceIT_E4typeEOS4_ # 
- movl %eax, %edx #, D.21547 
- movl 8(%ebp), %eax # .result_ptr, tmp65 
- movl $2097152, %ecx #, tmp68 
- movl %ecx, 8(%esp) # tmp68, 
- movl %edx, 4(%esp) # tmp67, 
- movl %eax, (%esp) # tmp66, 
- call memcpy # 
    movl 8(%ebp), %eax # .result_ptr, 
    leave 
    .cfi_restore 5 

Anche in questo caso c'è lo stesso stack pointer manipluation e copia accadendo con ottimizzazioni disabilitato nel ritorno per caso di valore. Quindi sembra che tu abbia un overflow dello stack in entrambi i casi, ma nel caso del ritorno per valore il tuo caso di test non è sufficiente per osservarlo effettivamente a causa di altre ottimizzazioni.

Soluzione: allocare nell'heap o ottenere uno stack più grande utilizzando pthread_attr_setstacksize o clone su Linux.

Problemi correlati