Array Out-of-Bounds

por Fabio A. Mazzarino

Out-of-bounds pode ser traduzido com fora dos limites, mas a expressão array out-of-bounds significa acessar um array além do seu limite inferior ou superior, outro nome muito popular é array overflow, ou string overflow.

Nas linguagens C/C++ não há checagem de limites para arrays simples, o compilador deixa o programador acessar a memória do jeito que bem entender, e a responsabilidade de não estourar os limites é do próprio programador.

Quando isso acontece o compilador simplesmente começa a acessar a memória contígua à memória alocada para o array, provavelmente outras variáveis.

Estourando o Limite Superior

O código a seguir ilustra bem esse caso:

char s1[] = "0123456789";
char s2[] = "ABCDEFGHIJ";
char s3[] = "abcdefghij";
char c = 'a';
int ct = 0;
printf("Step 0\ns1: [%s]\ns2: [%s]\ns3: [%s]\n\n", s1, s2, s3);
for (ct = 0; ct < 20; ct++, c++)
    s2[ct] = c; 
s2[ct] = '\x0';
printf("Step 1\ns1: [%s]\ns2: [%s]\ns3: [%s]\n\n", s1, s2, s3);

Aonde teremos a seguinte saída:

Step 0
s1: [0123456789]
s2: [ABCDEFGHIJ]
s3: [abcdefghij]

Step 1
s1: [qrst]
s2: [abcdefghijklmnopqrst]
s3: [abcdefghij]

Note que s2 ficou conforme a alteração no código com 20 caracteres, porém s1 foi alterado também. O compilador reservou 11 caracteres para s1 e 11 para s2, ao ultrapassar os limites superiores de s2, após alguns bytes o programa passou a escrever no espaço de memória de s1, essa diferença de 5 caracteres é devido ao alinhamento da alocação, e pode variar de compilador para compilador.

Estourando o Limite Inferior

Vamos testar os limites inferiores de s2:

for (ct = 0, c = '9'; ct > -10; ct--, c--)
    s2[ct] = c;
printf("Step 2\ns1: [%s]\ns2: [%s]\ns3: [%s]\n\n", s1, s2, s3);

E a saída:

Step 2
s1: [qrst]
s2: [9bcdefghijklmnopqrst]
s3: [abcdefg0123456789bcdefghijklmnopqrst]

Note que s2 teve o primeiro caracter alterado, mas s3 também foi alterado, resultado do estouro dos limites de s2. Novamente é possível perceber o alinhamento da alocação feita pelo compilador.

A partir dos exemplos é possível deduzir o potencial de problemas que um código que estoura os limites de um array pode causar. É possível alterar as variáveis contíguas e até mesmo alterar o fluxo de retorno.

Que Problema Pode Causar?

O problema maior ocorre quando ocorre o overflow de um array sobre outra variável, e o programa continua a funcionar. Valores podem ser reescritos e os resultados de uma função alterados. E para piorar esse tipo de falha pode facilmente passar sem ser detectado em testes unitários ou testes integrados, sendo detectados somente com dados em produção.

Um outro problema ainda mais grave é o overflow de variáveis alocadas dinamicamente. Normalmente, quando ocorre, é de difícil detecção e/ou reprodução, e muitas horas de desenvolvimento serão necessárias para corrigir a falha.

Especial cuidado com o tamanho dos arrays e strings. Um descuido pode custar muito tempo de desenvolvimento para corrigir.

O código do post de hoje pode ser facilmente encontrado no github.