Dois Bugs no Parser DateTime
do AWS SDK for C++
Depois do
decodificador Base64,
apontamos o ESBMC para os parsers de timestamp do
AWS SDK for C++
e reportamos mais dois defeitos: acumulação de dígitos sem limite, que estoura os
acumuladores int dos campos, e uma conversão sem
verificação de faixa, que reportava datas fora do intervalo como parsing bem-sucedido. A
AWS confirmou os dois, incorporou o patch que enviamos e publicou a correção no
PR #3896,
liberado no SDK 1.11.877.
2
bugs de parsing de timestamp que reportamos, confirmados e corrigidos pela AWS no PR #3896
3
parsers de data endurecidos: RFC822, ISO-8601 e ISO-8601 Basic
1.11.877
versão do AWS SDK for C++ que traz a correção, publicada em 2026-08-24
12
testes de regressão adicionados upstream junto com a correção
Contexto
Aws::Utils::DateTime
é o tipo de timestamp do AWS SDK for C++, e quase nada que uma aplicação faça com a AWS
escapa dele. Prazos de validade de credenciais, timestamps de assinatura de requisição,
cabeçalhos Last-Modified
do S3 e todo campo com data em toda resposta de serviço passam por um dos seus três
parsers: RFC822, ISO-8601 e ISO-8601 Basic. Os três operam sobre bytes que chegaram
pela rede.
Cada parser é uma máquina de estados escrita à mão, que percorre a string um caractere
por vez. Os campos são acumulados dígito a dígito,
valor = valor * 10 + (c - '0'),
e a máquina muda de estado ao encontrar um delimitador. Esse formato é compacto e
rápido, e tem uma fraqueza característica: o laço é guiado por onde está o
delimitador, não pela largura permitida do campo.
Abordagem
Analisamos os parsers com o ESBMC, nosso verificador de modelos de software, usando a mesma técnica que produziu os achados do Base64: em vez de alimentar os parsers com uma lista de strings de timestamp, nós os conduzimos com entrada simbólica e perguntamos ao solver se alguma string até um dado limite poderia alcançar um estouro aritmético ou um resultado silenciosamente errado.
O parsing de timestamps é um bom alvo para isso. As entradas são curtas, o espaço de estados é pequeno, e as propriedades de interesse são exatamente as que um verificador de modelos limitado decide diretamente: um acumulador sai da faixa do seu tipo, e uma conversão produz um valor que quem chama vai ler como válido sem que ele seja.
Resultados
Acumulação de dígitos sem limite, estourando os acumuladores int.
Nada limitava quantos dígitos um campo guiado por delimitador podia absorver, então
um timestamp como
Wed, 99999999999999999999 Oct 2002 08:00:00 GMT
leva o acumulador do dia para além de
INT_MAX.
Estouro de inteiro com sinal é comportamento indefinido em C++ ([expr.pre]/4), de
modo que o que o parser faz a partir daí não é definido pela linguagem. O mesmo
padrão valia para os campos de ano, mês, hora, minuto e segundo, nos três parsers.
Enviamos o patch que limita cada acumulador à largura do seu campo: dois dígitos
para dia, mês, hora, minuto e segundo, quatro para o ano.
Datas fora da faixa reportadas como parsing bem-sucedido.
Separadamente,
ConvertTimestampStringToTimePoint
entregava o valor obtido a
std::chrono::system_clock::from_time_t
sem verificação de faixa. Essa conversão escala os segundos até o período de tique
do relógio, o que, num relógio de nanossegundos, deixa a representação
int64_t
cobrindo apenas de cerca de 1677 a 2262. Uma data fora dessa janela estourava, e o
valor errado resultante era devolvido a quem chamou como parsing bem-sucedido. A
correção rejeita timestamps fora da faixa antes de converter e deriva a janela do
relógio da plataforma, em vez de fixá-la no código.
O sentinela de "nunca expira" é o caso que dói. Fri, 31 Dec 9999 23:59:59 GMT
é um marcador convencional de "nunca expira" em código próximo do HTTP, e está bem
fora da faixa de um relógio de nanossegundos. Antes da correção ele era "parseado
com sucesso" para um valor sem relação alguma com o ano 9999, que é exatamente o
modo de falha errado para um timestamp que quem chama vai comparar com a hora atual
para decidir se algo expirou. Depois da correção,
WasParseSuccessful()
retorna falso e quem chama consegue ver que o valor não é utilizável.
Confirmados e corrigidos upstream no PR #3896, integrado em 2026-08-24 e publicado no AWS SDK for C++ 1.11.877 no mesmo dia. O PR traz 12 novos testes de regressão fixando o comportamento, incluindo ida e volta nas fronteiras 2262-04-11 e 1677-09-22 e a rejeição de campos de dia com 9, 11 e 20 dígitos. A AWS credita o relato e o patch de largura de campo na mensagem de commit.
Se você usa o SDK
Atualize para o AWS SDK for C++ 1.11.877 ou posterior. Vale notar que a segunda
correção muda um comportamento observável, e não apenas endurece um caminho interno: um
timestamp fora da faixa do relógio do sistema agora resulta em parsing
inválido, em vez de produzir um valor errado. Se o seu código faz parsing de
sentinelas muito distantes no futuro e não verifica
WasParseSuccessful(),
a atualização vai expor isso, o que é o objetivo, mas convém saber antes da troca de
versão, e não depois.
O que isso significa
Os dois defeitos são de naturezas diferentes, e só um deles é do tipo que uma ferramenta de segurança de memória apontaria. A acumulação de dígitos é comportamento indefinido, e um sanitizador poderia capturá-la dada a entrada certa. O bug de conversão não produz travamento nem diagnóstico: a função retorna, a verificação de sucesso de quem chamou passa, e o número errado se propaga. A verificação de modelos limitada trata os dois do mesmo jeito, porque ambos são apenas propriedades a decidir sobre o espaço de entradas, e nenhum depende de alguém ter adivinhado a entrada ao escrever um teste.
É também o segundo trabalho sobre a mesma base de código, e essa é a parte que vale generalizar. Uma vez modelado um decodificador ou um parser, o próximo da mesma biblioteca custa bem menos, e os achados se acumulam. Rotinas pequenas e autocontidas que ficam sobre bytes não confiáveis são onde essa técnica se paga mais rápido.
Referências
- aws/aws-sdk-cpp PR #3896, "Validate DateTime range and bound parser field widths (defense in depth)" (integrado em 2026-08-24). github.com/aws/aws-sdk-cpp/pull/3896
- aws/aws-sdk-cpp versão 1.11.877 (2026-08-24), a primeira a trazer a correção. github.com/aws/aws-sdk-cpp/releases/tag/1.11.877
- Nosso trabalho anterior no mesmo SDK: dois CVEs de segurança de memória no Base64, boletim de segurança da AWS 2026-080. Leia o estudo de caso
- ESBMC, o verificador de modelos limitado de código aberto usado na análise. github.com/esbmc/esbmc
Nota. Os problemas foram reportados por Lucas Carvalho Cordeiro e Rafael Sá Menezes (University of Manchester), creditados nominalmente na mensagem de commit upstream. A CRC Ltd não mantém parceria comercial com a AWS e a análise não foi encomendada pela AWS; os achados passaram pelo processo de divulgação coordenada de vulnerabilidades da AWS. A AWS classificou a correção como defesa em profundidade e não atribuiu CVE a nenhum dos dois problemas.