Talk is cheap

Chega de blá blá blá, vamos falar de coisas úteis.
Uma das coisas que eu mais gosto com o movimento open source é que não apenas lhe permitem ver e modificar e estudar o código, mas sempre é incentivado(não da maneira mais simpática, certamente) a que o código seja limpo, legível e seguro. Isto é uma das coisas que admiro nos BSDs, existem regras e padrões para que patchs sejam aceitos. Um código sujo e um código limpo podem fazer a mesma coisa, mas certamente um código limpo é muito melhor de dar manutenção e integração.
Um código mais seguro é sempre desejável, e eu passei a testar algumas ferramentas a respeito. Uma delas, o FlawFinder, é uma ferramenta interessante, embora gere muito falso positivo. Mas é um começo, seguindo as indicações ali descobri muita coisa interessante.
Eu me deparei com uma discussão datada de 2000, e achei que só eu não sabia, mas aparentemente é desconhecida, pois vários programadores muito experientes não a conheciam(experientes assim, tipo, décadas…). A discussão começa com a proposta de Todd C. Miller e Theo de Raadt a respeito de novas funções para copiar e concatenar strings. Esta é um dos principais pontos de falha nos softwares.
Geralmente usa-se as funções strcat e strcpy para concatenar ou copiar uma string para outra. Porém estas funções não controlam quantos caracteres serão copiados, e podem ser copiados muitos mais do que deveriam e sobrescreverem áreas de memória. Por exemplo, digamos que você tem isto no seu programa:

char palavra[20],frase[100],usuario[10];

Você tem uma palavra de 20 caracteres, uma frase de 100 e nome de usuário de 10. Suponha que este nome de usuário serve para que você gerencie se o usuário pode realmente armazenar esta frase em determinado arquivo.
Então você vai lendo tranquilamente as entradas e concatenando as palavras em frase. Só que o usuário “esperto” digita uma palavra de mais de 20 caracteres. Não precisa ser muito, bastam 21 caracteres. Como o strcat não tem controle, ele irá escrever os 21 caracteres, invadindo assim a área da variável usuario. Com o valor de usuario alterado, o usuário não vai mais conseguir gravar no arquivo, pois este valor não corresponde mais ao valor correto. Agora nosso usuário vai ter que sair do programa e entrar novamente… soa familiar? 😀
Isto numa hipótese simples. Numa hipótese não tão simples, não haveria outra variável para o strcat invadir e ele acabaria sobrescrevendo uma área que resultaria em erro geral do programa, bum, segmentation fault na cara. Numa hipótese um pouco mais sofisticada, um atacante experiente manipularia a área onde está escrevendo para chamar um código arbitrário e executaria ações não previstas e maliciosas. Numa hipótese triste mais real, um script kiddie pegaria um exploit feito por um programador experiente e executaria ações não previstas e maliciosas.
Isto pode ser melhorado com o uso das funções strn[cat|cpy]. Porém este uso também é um dos grandes pontos de falha dos softwares, pois estas funções são mal interpretadas. A sintaxe é

strn[cat|cpy](destino,fonte,tamanho)

só que tamanho não quer dizer o tamanho que destino suporta, tamanho quer dizer o quanto de caracteres vão ser copiados para destino. Ou seja, o uso correto destas funções é

strn[cat|cpy](destino,fonte,tamanho-strlen(destino)-1)

Sim, menos o espaço já usado e menos 1, pois ele vai gravar o resultado disto e DEPOIS vai encerrar a string com um ” . Quer dizer, o strncpy não vai fazer isto, se não houver um ” na string de fonte E entre os caracteres copiados, a string não vai ter fim. Como muitas funções lêem/manipulam strings até encontrarem um ”, os resultados podem ser catastróficos. Isto somado a má performace, pois o strncpy preenche com zeros o espaço resultante. Então se você declarar no seu programa:

int tam_arq=1024;

porque você é uma pessoa esperta e quer facilitar futuras mudanças, e

string arquivo[tam_arq];

e escrever

strncpy(arquivo,”/etc/named.conf”,tam_arq);

o strncpy vai escrever /etc/named.conf e preencher 1009 zeros.
A proposta das funções strl[cat|cpy] é de ser mais intuitiva, ou seja, interpretar o tamanho como o tamanho máximo que o destino comporta, garantir o encerramento da string e copiar apenas o que é necessário. Estas funções já são padrão nos BSDs, e também são utilizadas dentro do kernel 2.6. A equipe da Glibc se recusa a aceitar esta função , com base em argumentos de que:

  • não é padrão do C
  • pode ser feito com funções como snprintf, ou o correto uso do strn[cat|cpy].
  • o programador deve saber o que está fazendo
  • Embora eu ache que eles tem razão, ainda acho que vale a pena utilizar métodos mais eficazes. Se de qualquer forma eu vou ter que declarar uma função para sempre calcular a quantidade a ser copiada, copiar e garantir o termino da string(afinal não vou ficar repetindo código), acho que vale a pena olhar as implementações do OpenBSD(strlcpy) e do kernel 2.6 no arquivo lib/string.c.

    One thought on “Talk is cheap

    1. realmente funções com string sao um saco… mas eu ainda prefiro verificar c o dest * comporta o source * … alem d padronizar o tamanho os char *[]… legal o blog! dificl encontrar post sobre C por ai

      falow!

    Leave a comment