Pular para o conteúdo

Ler entrada hostil

Todo tamanho que vem do arquivo é suspeito: as cinco conferências antes de interpretar um byte.

Um leitor de formato binário é a porta mais atacada de um sistema: o arquivo vem de fora, e cada número dentro dele — tamanho, contagem, deslocamento — decide quanto o programa vai ler e onde. Em C, confiar num desses números é o estouro de buffer clássico. Aqui a memória não corre risco, mas o programa ainda pode travar, alocar gigabytes ou ler o lugar errado.

ConfiraContraSenão
a assinaturaa constante do formatointerpreta um arquivo de outro formato
o tamanho declaradoo que realmente chegoufatia além do fim
a contagemum teto razoávelaloca um cluster de um bilhão de itens
o deslocamentoo intervalo do próprio arquivolê fora dele
a conferência (CRC)os dadosinterpreta lixo como dado válido
dataforge
adopt Arcane.Estrutura as Est
adopt Arcane.Bytes as Bytes

steady MAX_ITENS := 10000
Cab := Est.definir("Cab", [["magica", "char", 4], ["quantos", "u32"]], "rede", yes)
Item := Est.definir("Item", [["id", "u32"]], "rede", yes)

action ler_lista(dados):
    given len(dados) smaller Cab.tamanho:
        trigger "arquivo menor que o cabeçalho"
    cab := Cab.ler(dados)
    given cab["magica"] is not "LIST":
        trigger $"assinatura '{cab["magica"]}' não é de uma lista"
    given cab["quantos"] bigger MAX_ITENS:
        trigger $"{cab["quantos"]} itens passa do teto de {MAX_ITENS}"
    precisa := Cab.tamanho + cab["quantos"] * Item.tamanho
    given precisa bigger len(dados):
        trigger $"declara {cab["quantos"]} itens ({precisa} bytes) e o arquivo tem {len(dados)}"
    yield [j["id"] cycle j in Est.janelas(dados, Item, cab["quantos"], Cab.tamanho)]

bom := Bytes.concatenar(Cab({"magica": "LIST", "quantos": 2}).bytes(),
    Item({"id": 7}).bytes(), Item({"id": 9}).bytes())
assert ler_lista(bom) is [7, 9]

mentiroso := Cab({"magica": "LIST", "quantos": 4000000000}).bytes()
recusado := void
monitor:
    ler_lista(mentiroso)
handle Error as e:
    recusado := e.message
assert recusado.contains("teto")