quarta-feira, 4 de setembro de 2013

Anatomia de um bug killer: Como apenas 4 personagens podem matar iPhone, apps Mac


Ganhar uma HDTV LED de 40 polegadas Samsung com o Reg e HP!


Análise Houve muita coisa sniggering em mangas depois abana descobriram que poderiam perturbar iOS 6 iPhones e iPads e Macs rodando o OS X 10.8, através do envio de uma mensagem de texto simples desonestos ou e-mail.


Um bug é acionado quando o componente CoreText nos sistemas operacionais vulneráveis ​​da Apple tenta processar na tela uma determinada seqüência de caracteres Unicode: o kernel reage matando o programa em execução, seja o seu navegador web, cliente de mensagem, Twitter ou qualquer outro aplicativo tentou usar CoreText para exibir a seqüência impertinente.


Muita alegria seguiu-se como as pessoas twittou os caracteres especiais, postou no artigo comenta web ou mandei uma mensagem para eles, e se alegraram nas gritos de frustração fanbois. (Facebook teve que bloquear a cadeia de serem apresentados como uma atualização de status.)


Mas como isso funciona bug? Depois de alguns exames, ele parece ser uma programação bastante bonito cock-up que é bastante fácil de explicar. O código vulnerável foi provavelmente na natureza para yonks, algumas pessoas notaram que há seis meses e que apareceu em alguns slides [ PDF ] em abril para um corte na apresentação conferência Box. Quase ninguém prestou atenção naquela época - mas começou a se espalhar pela web no fim de semana depois de uma série de gatilho apareceu em um site russo.



Muito tempo, não leu: Um resumo


Sistema de renderização CoreText da Apple usa inteiros assinados para passar ao redor dos índices e comprimentos de cordas. A duração negativa, -1, é passado desmarcada para uma função de biblioteca que usa-lo como um inteiro longo não assinado para definir os limites de uma matriz. Isso faz com que a biblioteca para tentar ler além do final de um array e na memória não alocado, desencadeando uma exceção fatal.


Se você é au fait com software desmontagem de depurá-lo, o que se segue vai ser óbvio para você. Se você está interessado no que se passa debaixo do seu Mac ou capuz de iThing, então continue a ler.



Primeiro de tudo, vamos olhar para o acidente. Todos estes passos ocorreu em um Mac 64-bit rodando Mac OS X 10.8.4. Ao fazer o aplicativo Terminal exibir uma seqüência particular de quatro caracteres Unicode de 16 bits, o programa está rapidamente mortos pelo kernel com este relatório culpa OS gerado:


Grokking esses logs de acidente pode ser um pouco de dor de cabeça, mas a primeira coisa a se identificar é que o processador estava rodando dentro de uma biblioteca chamada libvDSP.dylib, especificamente a instrução 117.462 bytes nessa biblioteca, quando ocorreu a falha. Isso é bem no topo da pilha de retrocesso, na linha 30, no relatório, que tenta descrever a seqüência das funções do código chamado antes de bater o bug.


Se abrirmos libvDSP (localizado profundamente dentro da hierarquia de seu computador / System / Library / filesystem) na ferramenta de engenharia reversa bastante útil Hopper , podemos olhar para o código de máquina compilado que explodiu. Veja a imagem abaixo: a instrução falha 117,462 bytes dentro, ou 1cad6 em hexadecimal, é destaque (clique para ampliar).


Captura de tela da libvdsp aberto em Hopper


Essa instrução, addsd XMM1, QWORD [ds: RDI + rsi], tenta carregar um valor de 64-bit para o registro XMM1 a partir do endereço de memória calculado somando o IDI e rsi registra juntos. Os estados de registro de acidente que foram mortos por uma falha EXC_BAD_ACCESS no endereço 0x00007fa95cc00008, em outras palavras, que a instrução tentou ler dados de que o endereço de memória, mas foi marcado como inacessível. Nós não deveríamos estar tocando essa parte do mapa de memória, então o nosso programa é morto pelo kernel antes de qualquer dano pode ser feito.


Na verdade, o log com folga nos diz que, pouco antes do acidente, havia 2048 KB de memória alocada para o Terminal até o endereço 0x00007fa95cc00000. Parece provável que o programa deu um passo além desse limite e provocou a exceção fatal. Se rolar pelo registro de acidente para o estado do segmento, podemos ver os valores que estavam nos registros de CPU no momento do acidente:



Tópico 0 colidiu com o estado da linha X86 (64-bit):
rax: 0x0000000000000030 rbx: 0x00007fa95bcc5010
rcx: 0xfffffffffffc3e0a RDX: 0x00007fff5c862d60
RDI: 0x00007fa95cbffff8 rsi: 0x0000000000000010
ead: 0x00007fff5c862d70 rsp: 0x00007fff5c862d58
r8: 0xffffffffffffffff R9: 0x00007fa95c83e1e8
[...]
r15: 0x0000000000000002

Hopper destaca que há um loop de execução entre 0x1cad2 e 0x1cae7, lendo dados e adicioná-lo a três totais em execução em XMM0, XMM1 e XMM2, e subtraindo-se o valor no registro rcx cada vez até que ela cruza o ponto zero em que ponto nós saltar para fora do Loop - efetivamente usando rcx como uma contagem regressiva loop. Em cada iteração, RDI aumenta e rsi permanece constante. Como mencionado acima, estes dois são somados para calcular o endereço lemos, você pode ver que a adição de IDI para rsi produz o 0x7fa95cc00008 endereço, que (como visto acima) desencadeia a falha.


Então, IDI está crescendo muito grande, obrigando-nos a ler a memória não atribuída a nós. O endereço dos dados que tentam buscar só é limitado pela rcx, então isso sugere rcx é muito grande - não chegar a zero antes de IDI nos empurra para a memória inválido - e, sim, que é claramente o caso: a 0xfffffffffffc3e0a pelo tempo do acidente, rcx foi a contagem regressiva de um valor unfeasibly grande, muito maior do que a memória alocada para o programa inteiro. Deve ser bastante pequena. O que está errado?


Ao analisar as informações de depuração no binário libvDSP, Hopper nos diz que estamos na função vDSP_sveD () da biblioteca no momento da falha. Apple tem documentado que a função aqui :



anular vDSP_sveD (double * __vDSP_A, vDSP_Stride __ vDSP_I,
double * __vDSP_C, vDSP_Length __ vDSP_N);

Ele é usado para resumir uma série de dupla precisão números de ponto flutuante. Vamos mapear os parâmetros de entrada para a função para os registros utilizados neste código compilado, seguindo o System V AMD64 ABI que o Mac OS X usa:



IDI = double * __vDSP_A Ponteiro para a matriz de valores de entrada
rsi = vDSP_Stride __ vDSP_I O passo, não se preocupe com isso
RDX = double * Pointer __vDSP_C para onde armazenar o resultado
rcx = vDSP_Length __ vDSP_N O número de elementos do array para somar

O tipo vDSP_Length variável é definida como se segue:



typedef vDSP_Length longo não assinado;

Então rcx deve ser definido um número inteiro positivo: o número de elementos do array para processar, daí porque ele conta a zero no circuito.


Algo dentro do componente CoreText apela, pois, vDSP_sveD () com uma estupidamente grande valor vDSP_N __ ea função de biblioteca não faz nada para verificar a sanidade desse número porque ele assume o interlocutor sabe o que está fazendo. Este valor do contador de loop grande causa IDI exceder os limites do array input __ vDSP_A e explodir o nosso app.


Então, o que está chamando essa função biblioteca? Olhando para baixo ao lado do rastreamento de pilha, vemos o processador estava em trun :: function trun do componente CoreText, especificamente 850 bytes dentro Lá, encontramos uma instrução que chama outra função CoreText rotulado TStorageRange :: SetStorageSubRange (), que é desmontado abaixo (clique para ampliar):


Código para SetStorageSubRange ()


Esta função SetStorageSubRange () é interno ao CoreText e não está documentado publicamente. Mas podemos ver que chama vDSP_sveD () no 0x274f9, levando a nossa queda, como descrito acima.


p> Um pouco antes que chamada de função condenado, rcx (que contém o valor comprimento de matriz desonesto passou a libvDSP) leva o seu valor a partir do registo r8 em 0x274f6. A função vDSP_sveD () não modifica r8, que é muito útil para nós bisbilhotando. Do nosso despejo referido, podemos ver o conteúdo do R8, que é usado para inicializar o loop de contagem regressiva registo rcx pouco antes vDSP_sveD () é chamado.


E sim, o seu valor é ridiculamente grande. Na verdade, é o maior valor possível para um registo de 64 bits sem sinal, por isso não admira que exceder limites e acidentes do array:



r8: 0xffffffffffffffff

Isso inteiro sem sinal, quando expresso como um inteiro decimal assinado, é -1, de acordo com as regras de complemento de dois .


Então vDSP_sveD () é chamado por SetStorageSubRange de CoreText () com um comprimento de matriz negativo, o que não é o que a função da biblioteca espera: é definido como tendo um valor positivo apenas SetStorageSubRange () não é chamado a função soma de libvDSP corretamente..


Onde é que este número negativo vem? De volta para dentro SetStorageSubRange (), vemos que o registo r8 (usado para inicializado rcx para vDSP_sveD ()) é dado o valor de RDX perto do início da função no 0x2744e. Ao seguir todos os caminhos de código possíveis em SetStorageSubRange (), podemos ver que o valor do r8 não muda a partir desse valor inicial, se é negativo ou um bit sinalizador interno é clara. Por conseguinte, a -1 passado para vDSP_sveD () vem de RDX.


Isso RDX registo é um parâmetro de entrada do SetStorageSubRange (). A julgar pelo código, o primeiro parâmetro para a função, armazenada em IDI, é um ponteiro para uma variável de 64 bits que vDSP_sveD () 's cálculo soma deve ser armazenado dentro Isso folhas RDX e rsi.


De acordo com as informações de depuração, SetStorageSubRange () tem uma estrutura CFRange como uma de suas entradas. Esta estrutura é definida publicamente aqui :



struct CFRange {location CFIndex; comprimento CFIndex;};

Essa estrutura descreve "uma série de itens seqüenciais em um recipiente, como caracteres em um buffer". Ele contém dois CFIndex variáveis, um local marcado, que define o ponto de partida para uma série de coisas, eo outro comprimento marcado, que é o número de coisas em que a matriz que estamos interessados ​​polegadas


E CFIndex é definido como:



typedef assinado longo CFIndex;

Assim, parece que o segundo parâmetro para o SetStorageSubRange () é a função de uma estrutura CFRange, colocando o local em rsi e o valor do comprimento de RDX, que é um número inteiro negativo. Seja qual for chamado SetStorageSubRange (), portanto, aprovada em -1 como um comprimento de corda, que escorreu para o acidente descrito acima.


(Dado que o comprimento é um longo assinado, -1 pode ser um número válido. Sistema de renderização da Apple Core usa assinado CFIndex valores ", como um índice de array e para a contagem, tamanho e parâmetros de comprimento e valores de retorno" em todo o seu software.)


Recuando mais uma vez, chegamos a função Trun não-trivial :: Trun () em CoreText, que apelou SetStorageSubRange () no 0x25d57. A desmontagem está abaixo (clique para ampliar):


Trun :: Trun () código de despejo


As coisas começam a ficar interessantes na marca 0x25d25: o registo rbx é incrementado e seu valor copiado para RDX. Em seguida, o valor do registo de r15 é subtraído RDX. O valor resultante em RDX é então passada para SetStorageSubRange (), o que temos visto é -1. E nós sabemos o valor de r15, porque milagrosamente foi preservada a partir daquele momento todo o caminho para o acidente:



r15: 0x0000000000000002

Então, trabalhando para trás, rbx tem que ser zero apenas antes de ser incrementada a fim de nos pousar com um RDX de -1 que derruba o castelo de cartas mais tarde. Seguindo o código de montagem spaghetti através Trun :: Trun parece ser que rbx é um comprimento calculado anteriormente, muito provavelmente o número de caracteres ou glifos Unicode para processar.


Filippo Bigarella, que está tentando corrigir o bug para dispositivos com jailbreak, descobriu que o Unicode especial de seqüência Morte fez o publicamente documentado CTRunGetGlyphCount () em CoreText retornar um valor de -1. Essa função deve retornar o número de glifos Unicode em uma "corrida glifo, que é um conjunto de glifos consecutivos que compartilham os mesmos atributos e direção".


Isto sugere que o assassino Unicode picada - uma curta mistura sem sentido de cirílico e árabe - é uma sequência de caracteres que faz com que o sistema operacional para determinar que tem um comprimento zero ou negativo. Para os não iniciados, Unicode é uma maneira de armazenar e processar cartas para além do conjunto ASCII você vai sem dúvida estar familiarizado com, é capaz de representar caracteres de idiomas árabes e asiáticos para símbolos matemáticos.


Também pode definir a direção da esquerda para a direita e da direita para a esquerda, e combinar vários glifos em um personalizado. Um bug-provocando seqüência envolve um espaço simples (código ASCII 0x20), que pode estar relacionado ao código de manipulação de espaço em branco visto no rastreamento de pilha.


Este é o lugar onde a trilha fica frio, e se você vai perdoar as metáforas mistas, vamos deixar essa última peça do quebra-cabeça - a string de comprimento zero - como uma teoria. Se alguém tiver alguma idéia brilhante, entrar em contato . A falha, tal como está, não parece ser exploradas além falhando o programa do usuário: é poderoso difícil alavancar uma-de-array final ler culpa em algo mais sério.


Ele também pode ser desencadeada em 32 bits iPhones ARM-powered, iPods e iPads rodando a última versão disponível publicamente do iOS. Isto significa que o bug não é específico para uma arquitetura especial: o estouro de buffer irá funcionar da mesma forma, exceto dentro de 32 bits em vez de 64, como visto acima.


O app-assassinato erro de codificação está ausente no iOS 7 e Mac OS X 10.9 (codinome Mavericks), ambos devido a um lançamento público em breve. El Reg contactado a Apple para saber se deve ou não o seu software mais antigo será corrigido, mas ninguém foi disponível para comentar.


Assim como artigos têm erros de digitação, software tem bugs. E fóruns de suporte da Apple têm queixas sobre falhas Unicode acionados. ®







via Alimentar (Feed)

Nenhum comentário:

Postar um comentário