Pular para o conteúdo
Engenharia de Software

Integer Overflow Explicado: O Bug que Derruba

Integer overflow ja derrubou foguete, quebrou jogo e crashou aviao. Nesse artigo voce vai entender o mecanismo por tras, ver como cada linguagem trata e aprender a prevenir

Em 1996, um foguete de US$ 370 milhoes explodiu porque um numero nao coube dentro de uma variavel. Parece mentira, mas e exatamente o que aconteceu com o Ariane 5. O culpado: integer overflow.

Integer overflow e um daqueles bugs que todo dev ja ouviu falar, poucos entendem de verdade, e quase ninguem testa direito. E o pior: ele aparece em toda linguagem, em todo tipo de sistema, e continua causando estrago ate hoje.

Nesse artigo vou destrinchar tudo: o que e, como funciona em cada linguagem, casos reais de producao e como voce previne no seu codigo.

O que e integer overflow (visualizacao)

Imagina um velocimetro analogico que vai de 0 a 999. Se voce ta em 999 e soma 1, o que acontece? Os digitos voltam pra 000. Nao da erro, nao para, nao avisa — simplesmente reseta.

Integer overflow e exatamente isso, so que com bits. Um inteiro de 8 bits sem sinal vai de 0 a 255. Se voce tenta guardar 256, ele volta pra 0. Um inteiro de 16 bits com sinal vai de -32.768 a 32.767. Se voce tenta guardar 32.768, ele vira -32.768.

Nao e bug do hardware. E matematica binaria funcionando como projetado. O problema e quando o software nao espera esse comportamento.

Limites dos tipos inteiros

Integer Overflow Explicado: O Bug que Derruba. Integer overflow ja derrubou foguete, quebrou jogo e crashou aviao. Nesse artigo voce vai entender o mecanismo por tras, ver como cada linguagem trata e aprender a prevenir no seu codigo.

Signed vs Unsigned: a diferenca que importa

Computadores representam numeros negativos usando complemento de dois. O bit mais a esquerda (MSB) indica o sinal: 0 pra positivo, 1 pra negativo. Isso significa que metade dos valores possiveis e reservada pra negativos.

No int8, o numero 127 em binario e 01111111. Se voce somar 1, fica 10000000. Esse padrao de bits representa -128 em complemento de dois. E por isso que overflow de inteiro com sinal sempre 'pula' do maior positivo pro menor negativo.

Inteiros sem sinal (unsigned) sao mais previsíveis: vao de 0 ate (2^n - 1) e o overflow simplesmente volta pro 0. Tipo um hodometro zerando.

Em C e C++, overflow de inteiro com sinal e comportamento indefinido. O compilador pode fazer o que quiser — incluindo remover codigo inteiro baseado na suposicao de que overflow 'nunca acontece'. Ja overflow de unsigned e comportamento definido: sempre faz wrap-around.

Overflow em C: comportamento indefinido na veia

C e a linguagem onde overflow e mais perigoso porque o compilador tem liberdade total pra otimizar assumindo que overflow nao ocorre. Isso gera bugs que parecem impossíveis.

c
#include <stdio.h>
#include <limits.h>

int main() {
    // Overflow de signed: comportamento INDEFINIDO
    int x = INT_MAX;  // 2147483647
    printf("x = %d\n", x);
    printf("x + 1 = %d\n", x + 1);  // Provavelmente -2147483648
    // Mas o compilador pode fazer QUALQUER COISA aqui

    // Overflow de unsigned: comportamento DEFINIDO (wrap-around)
    unsigned int y = UINT_MAX;  // 4294967295
    printf("y + 1 = %u\n", y + 1);  // Sempre 0

    // Armadilha classica: comparar signed com unsigned
    int a = -1;
    unsigned int b = 1;
    if (a < b) {
        printf("Correto\n");
    } else {
        printf("Surpresa! -1 > 1 em comparacao signed/unsigned\n");
        // -1 vira 4294967295 quando convertido pra unsigned
    }
    return 0;
}

O lance mais sinistro do C: compiladores como GCC e Clang otimizam assumindo que signed overflow nunca acontece. Se voce escrever if (x + 1 > x), o compilador pode remover o if inteiro porque 'x + 1 sempre e maior que x' (ignorando overflow). Use -fwrapv se quiser wrap-around garantido, ou -ftrapv pra crash no overflow.

Overflow em Java: wrap-around silencioso

Java e mais previsivel que C: overflow de inteiro sempre faz wrap-around, sem comportamento indefinido. Mas isso tambem e perigoso porque o programa continua rodando com o valor errado sem dar nenhum sinal.

java
public class OverflowDemo {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;  // 2147483647
        System.out.println(max + 1);  // -2147483648 (sem excecao!)

        // Armadilha: calcular media de dois inteiros grandes
        int a = 2_000_000_000;
        int b = 2_000_000_000;
        int mediaErrada = (a + b) / 2;  // a + b estoura!
        int mediaCerta = a + (b - a) / 2;  // Correto
        System.out.println("Errada: " + mediaErrada);  // negativo
        System.out.println("Certa: " + mediaCerta);     // 2000000000

        // Desde Java 8: Math.addExact lanca ArithmeticException
        try {
            Math.addExact(max, 1);  // ArithmeticException!
        } catch (ArithmeticException e) {
            System.out.println("Overflow detectado: " + e.getMessage());
        }
    }
}

Desde o Java 8, voce pode usar Math.addExact(), Math.multiplyExact() e similares pra ter deteccao de overflow com excecao. Mas ninguem usa isso no dia a dia. A maioria dos devs Java nem sabe que existe.

Overflow em Python: a linguagem que 'nao tem'

Python 3 e a excecao honrosa: inteiros nativos tem precisao arbitraria. O numero cresce o quanto precisar. 2 elevado a 1000? Sem problema. Nao tem overflow.

python
# Python 3: inteiros nativos nao tem overflow
x = 2 ** 1000
print(len(str(x)))  # 302 digitos. Sem problema.

# POREM: NumPy usa inteiros de tamanho fixo
import numpy as np
a = np.int32(2_147_483_647)
print(a + np.int32(1))  # -2147483648 (overflow!)

# E ctypes tambem
import ctypes
b = ctypes.c_int32(2_147_483_647)
b.value += 1
print(b.value)  # -2147483648

# Moral: Python puro e seguro, mas bibliotecas C por baixo nao sao

O ponto cego do Python: quando voce usa NumPy, pandas, ctypes ou qualquer binding C, os inteiros voltam a ter tamanho fixo e overflow vira problema real. Muita gente acha que 'Python nao tem overflow' e esquece que metade do ecossistema cientifico roda C por baixo.

Overflow em JavaScript: float64 disfarçado

JavaScript nao tem tipo inteiro nativo. Todos os numeros sao float64 (IEEE 754 double). Inteiros sao representados exatamente ate 2^53 (Number.MAX_SAFE_INTEGER = 9.007.199.254.740.991). Acima disso, perde precisao.

javascript
// JavaScript: numeros sao float64
console.log(Number.MAX_SAFE_INTEGER);      // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1);  // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2);  // 9007199254740992 (igual!)
// Perdeu precisao. Nao deu erro.

// BigInt resolve pra inteiros grandes
const grande = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
console.log(grande);  // 9007199254740993n (correto)

// Typed Arrays tem overflow classico
const arr = new Int8Array(1);
arr[0] = 127;
arr[0] += 1;
console.log(arr[0]);  // -128 (overflow!)

// Bitwise operators truncam pra int32
console.log(0xFFFFFFFF | 0);  // -1 (int32 signed)
console.log(0xFFFFFFFF >>> 0);  // 4294967295 (uint32)

O perigo em JS e diferente: nao e overflow classico, e perda de precisao silenciosa. Quando voce trabalha com IDs longos (tipo Snowflake IDs do Twitter/Discord com 18+ digitos), precisa usar BigInt ou string. Guardar como number perde digitos.

Overflow em Go e Rust: abordagens opostas

Go

Overflow silencioso com wrap-around em todos os casos. Sem checked arithmetic nativa.

+ Prós

  • • Comportamento previsivel e consistente
  • • Performance maxima — sem overhead de checagem
  • • math/big pra quando precisa de precisao arbitraria

− Contras

  • • Overflow silencioso e perigoso em producao
  • • Sem funcoes nativas de checked arithmetic
  • • Dev precisa validar manualmente
  • • Nenhum aviso do compilador

Rust

Panic em debug, wrap em release. 4 modos explicitos: checked, saturating, wrapping, overflowing.

+ Prós

  • • Debug mode detecta overflow automaticamente
  • • 4 metodos explicitos pra cada comportamento desejado
  • • Compilador forca voce a pensar sobre overflow
  • • clippy avisa sobre operacoes suspeitas

− Contras

  • • Release mode faz wrap silencioso por padrao
  • • Precisa configurar overflow-checks = true em release
  • • Curva de aprendizado pra entender os 4 modos
go
package main

import (
    "fmt"
    "math"
)

func main() {
    var x int32 = math.MaxInt32  // 2147483647
    x++
    fmt.Println(x)  // -2147483648 (silencioso!)

    // Precisa checar manualmente
    if x > 0 && x > math.MaxInt32 - 1 {
        fmt.Println("overflow detectado")
    }
}
rust
fn main() {
    let x: i32 = i32::MAX;  // 2147483647

    // Em debug: panic! Em release: wrap silencioso
    // let y = x + 1;  // panic em debug

    // 4 modos explicitos:
    println!("{:?}", x.checked_add(1));     // None
    println!("{}", x.saturating_add(1));    // 2147483647 (fica no max)
    println!("{}", x.wrapping_add(1));      // -2147483648
    println!("{:?}", x.overflowing_add(1)); // (-2147483648, true)
}

Casos reais de overflow em producao

Overflow nao e so teoria. Aparece em producao em contextos que voce nem imagina.

Pac-Man: level 256

O numero do level era guardado em uint8. Maximo: 255. No level 256, o byte volta pra 0, e o codigo que renderiza o mapa usa esse valor direto pra calcular posicoes. Metade da tela vira lixo de memoria. O famoso 'kill screen'.

Gangnam Style quebrou o YouTube

Em 2014, o contador de views do YouTube era int32. Limite: 2.147.483.647. Gangnam Style estava quebrando esse numero em tempo real. O Google correu pra atualizar pra int64.

Boeing 787: reboot a cada 248 dias

Os geradores de energia do 787 Dreamliner usavam um contador int32 de centesimos de segundo. Em 248 dias, o contador estourava e os geradores perdiam potencia. A FAA mandou rebotar o aviao antes de 248 dias como 'fix temporario'.

Ariane 5: o caso classico

Float64 convertido pra int16. Valor 32.768 nao cabe em int16 (max 32.767). Overflow, excecao nao tratada, sistema de navegacao cai, backup cai igual, foguete explode. US$ 370 milhoes em 37 segundos. A historia completa ta em A Historia Nao Contada do Desastre do Ariane 5.

Overflow vs Underflow vs Truncation

Esses tres termos sao confundidos o tempo todo. Vamos separar direito:

  1. Overflow: o valor excede o limite superior do tipo. int8: 127 + 1 = -128. O numero 'transborda'.
  2. Underflow (inteiro): o valor excede o limite inferior. int8: -128 - 1 = 127. O numero 'transborda' por baixo.
  3. Underflow (ponto flutuante): o resultado e tao proximo de zero que perde precisao e vira 0. Diferente do conceito inteiro.
  4. Truncation: quando voce converte de um tipo maior pra menor e os bits excedentes sao cortados. float64 -> int16 no Ariane 5 e truncation que causou overflow.

Na pratica, quando alguem diz 'overflow' no contexto de inteiros, pode estar falando de qualquer um dos tres. O conceito central e o mesmo: o valor nao cabe no recipiente.

Como prevenir overflow no seu codigo

Da pra se proteger de overflow sem complicar seu codigo. Aqui vao as praticas que funcionam:

Checklist anti-overflow

  • Use o tipo inteiro adequado ao dominio. Se o valor pode chegar a bilhoes, use int64
  • Valide inputs antes de operar. Conheca o range maximo de cada entrada
  • Use checked arithmetic quando a linguagem oferece (Rust checked_add, Java Math.addExact)
  • Teste com valores de borda: 0, 1, -1, MAX, MIN, MAX-1, MIN+1
  • Cuidado com conversoes de tipo: float->int, int64->int32, signed->unsigned
  • Em C/C++: compile com -ftrapv ou use __builtin_add_overflow
  • Em producao critica: habilite overflow checks mesmo em release (Rust: overflow-checks = true)
  • Use linters e sanitizers: UBSan (C/C++), clippy (Rust), SpotBugs (Java)

O mais importante: pense no overflow como risco real em qualquer calculo numerico. Nao e um bug exotico. E um dos tipos de falha mais comuns e mais faceis de prevenir na historia do software.

Perguntas frequentes

Integer overflow e a mesma coisa que buffer overflow?

Nao. Integer overflow e quando um valor numerico excede o limite do tipo. Buffer overflow e quando voce escreve dados alem do limite de um buffer de memoria. Sao bugs diferentes, mas integer overflow pode causar buffer overflow quando o valor estourado e usado como indice de array ou tamanho de alocacao.

TypeScript protege contra overflow?

Nao. TypeScript tem o tipo number (que e float64 por baixo, igual JS) e bigint. O sistema de tipos nao impede overflow em runtime. Se voce usa Typed Arrays (Int32Array, etc.), overflow acontece normalmente. TypeScript te ajuda com tipos em tempo de compilacao, mas nao muda o comportamento numerico do JavaScript.

Em qual linguagem overflow e mais perigoso?

C e C++, sem duvida. Overflow de inteiro com sinal e comportamento indefinido nessas linguagens. O compilador pode otimizar de formas inesperadas assumindo que overflow nunca ocorre, gerando bugs que sao praticamente impossiveis de debugar olhando so pro codigo fonte.

Perguntas frequentes

O que e integer overflow?

Integer overflow acontece quando uma operacao matematica produz um resultado maior (ou menor) do que o tipo de dado consegue armazenar. O numero 'transborda' e vira um valor inesperado — geralmente negativo ou zero — sem avisar. E um dos bugs mais antigos e recorrentes da computacao.

Integer overflow e a mesma coisa que buffer overflow?

Nao. Integer overflow e quando um valor numerico excede o limite do tipo inteiro. Buffer overflow e quando dados sao escritos alem do limite de um bloco de memoria alocado. Sao bugs diferentes, embora integer overflow possa causar buffer overflow quando o valor transbordado e usado como indice de array ou tamanho de alocacao.

Python tem integer overflow?

Python 3 nao tem integer overflow nos inteiros nativos porque usa aritmetica de precisao arbitraria (bigint). O numero cresce o quanto precisar. Porem, bibliotecas como NumPy usam inteiros de tamanho fixo (int32, int64) e nesses casos overflow acontece normalmente.

Como prevenir integer overflow no dia a dia?

Use tipos adequados ao problema (int64 pra valores grandes), valide inputs antes de operar, use checked arithmetic quando a linguagem oferece (Rust, C# checked), e teste com valores nos limites do tipo. Em linguagens como C/C++, habilite warnings de overflow do compilador e use sanitizers como UBSan.

O que e integer overflow (visualizacao)

Imagina um velocimetro analogico que vai de 0 a 999. Se voce ta em 999 e soma 1, o que acontece? Os digitos voltam pra 000. Nao da erro, nao para, nao avisa — simplesmente reseta. Integer overflow e exatamente isso, so que com bits. Um inteiro de 8 bits sem sinal vai de 0 a 255. Se voce tenta guardar 256, ele volta pra 0. Um inteiro de 16 bits com sinal vai de -32.768 a 32.767. Se voce tenta guardar 32.768, ele vira -32.768. Nao e bug do hardware. E matematica binaria funcionando como projetado. O problema e quando o software nao espera esse comportamento.

Como prevenir overflow no seu codigo

Da pra se proteger de overflow sem complicar seu codigo. Aqui vao as praticas que funcionam: O mais importante: pense no overflow como risco real em qualquer calculo numerico. Nao e um bug exotico. E um dos tipos de falha mais comuns e mais faceis de prevenir na historia do software.