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.
| Confira | Contra | Senão |
|---|---|---|
| a assinatura | a constante do formato | interpreta um arquivo de outro formato |
| o tamanho declarado | o que realmente chegou | fatia além do fim |
| a contagem | um teto razoável | aloca um cluster de um bilhão de itens |
| o deslocamento | o intervalo do próprio arquivo | lê fora dele |
| a conferência (CRC) | os dados | interpreta 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")