.dtors parece gravável, mas tenta escrever segfault

9

Este é o Ubuntu 9.04, 2.6.28-11-server, 32 bits x86


$ cat test.c
main() { int *dt = (int *)0x08049f18; *dt = 1; }
$ readelf -S ./test
...
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
...
$ ./test
Segmentation fault
$

Para os não iniciados: o gcc cria um segmento de destruição, .dtors , no executável elf, que é chamado após main() sair. Esta tabela tem sido gravável, e parece que deve estar no meu caso (veja readelf output). Mas tentar escrever na tabela causa um segfault.

Eu percebo que tem havido um movimento em direção a readonly .dtors, plt, tenho ultimamente, mas o que eu não entendo é a incompatibilidade entre readelf e o segfault.

    
por Fixee 25.02.2011 / 03:59

2 respostas

5

Essas seções são marcadas como GNU_RELRO (readonly relocations), o que significa que, assim que o carregador dinâmico tiver consertado todas as realocações (no tempo de carregamento, não haverá relocação lenta), marcará essas seções como somente leitura. Note que a maior parte de .got.plt está em outra página, então não recebe o tratamento.

Você pode ver o script de vinculação com ld --verbose , se você pesquisar por RELRO, encontrará algo semelhante a:

.got            : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt        : { *(.got.plt) }

, o que significa que as seções RELRO terminam com 12 bytes em .got.plt (os ponteiros para as funções de vinculação dinâmica já foram resolvidos, portanto podem ser marcados como somente leitura).

O projeto do Gentoo tem alguma documentação sobre o RELRO no link .

    
por 25.02.2011 / 21:28
5

Eu posso dizer por que está falhando, embora eu não saiba realmente qual parte do sistema é responsável. Enquanto .dtors é marcado como gravável no binário, parece que (junto com .ctors , o GOT e algumas outras coisas) estão sendo mapeados em uma página separada e não gravável na memória. No meu sistema, .dtors está sendo colocado em 0x8049f14 :

$ readelf -S test
  [17] .ctors            PROGBITS        08049f0c 000f0c 000008 00  WA  0   0  4
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .jcr              PROGBITS        08049f1c 000f1c 000004 00  WA  0   0  4
  [20] .dynamic          DYNAMIC         08049f20 000f20 0000d0 08  WA  6   0  4
  [21] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [22] .got.plt          PROGBITS        08049ff4 000ff4 00001c 04  WA  0   0  4
  [23] .data             PROGBITS        0804a010 001010 000008 00  WA  0   0  4
  [24] .bss              NOBITS          0804a018 001018 000008 00  WA  0   0  4

Se eu executar o executável e verificar /proc/PID/maps , vejo:

08048000-08049000 r-xp 00000000 08:02 163678     /tmp/test
08049000-0804a000 r--p 00000000 08:02 163678     /tmp/test
0804a000-0804b000 rw-p 00001000 08:02 163678     /tmp/test

.data / .bss ainda podem ser gravados em sua própria página, mas os outros em 0x8049000-0x804a000 não são. Eu suponho que este é um recurso de segurança no kernel (como você disse, "houve um movimento em direção a readonly .dtors, plt, got ultimamente"), mas eu não sei especificamente o que é chamado (OpenBSD tem algo muito semelhante chamado W ^ X ; o Linux tem PaX , mas não embutido na maioria dos kernels)

Você pode contornar isso com mprotect , o que permite alterar os atributos na memória de uma página:

mprotect((void*)0x8049000, 4096, PROT_WRITE);

Com isso, meu programa de teste não trava, mas se eu tentar sobrescrever o sentinela final de .dtors ( 0x8049f18 ) com o endereço de outra função, essa função ainda não será executada; essa parte eu não consigo descobrir.

Espero que alguém saiba o que é responsável por tornar a página readonly, e por que modificar .dtors não parece fazer nada no meu sistema

    
por 25.02.2011 / 07:12