Dev & EngNOTÍCIA

C2y remove 45 dos cerca de 100 comportamentos indefinidos do padrão C

Em palestra na Kernel Recipes 2026, o pesquisador Martin Uecker mostrou como o comitê do C está reduzindo os comportamentos indefinidos da linguagem sem abrir mão da compatibilidade que sustenta kernels e sistemas embarcados até hoje.

C2y remove 45 dos cerca de 100 comportamentos indefinidos do padrão C
Imagem gerada por IA

Quem é Martin Uecker e por que ele ainda defende o C em 2026

Martin Uecker não é o perfil usual de palestrante em conferências de kernel: ele é professor de engenharia biomédica e desenvolve software livre↳Open source71 conteúdosComo o Open Source Está Liberando o Poder da Automação para TodosDev (Back & Front) · out 2025Código aberto: programadores criam software da NASA sem saberDev (Back & Front) · abr 2021N8N: O que é a ferramenta open source que está revolucionando a automação em TI?Dev (Back & Front) · dez 2025Ver tudo em Dev (Back & Front) → para controlar aparelhos de ressonância magnética. Mas é usuário de Linux↳Linux34 conteúdosKali Linux em um Servidor VPS: como, quando e por que usar?DevSecOps · dez 2024Construindo um Windows Service ou Linux Daemon com Worker Service & .NET Core – Parte 2Dev (Back & Front) · jul 2020Criando uma WebApi utilizando .NET, Linux e VSCodeDev (Back & Front) · ago 2019Ver tudo em DevSecOps → há décadas e participa do comitê que define o futuro da linguagem C. Na edição 2026 da Kernel Recipes, ele apresentou um panorama sobre comportamento indefinido em C e sobre até onde a linguagem pode chegar rumo à segurança de memória, segundo reportagem da LWN.net publicada em 28 de setembro de 2026.

A pergunta que ele respondeu logo no início: por que usar C em 2026? Segundo ele, C continua portável, estável no longo prazo, compila rápido e gera binários rápidos. O argumento central é a previsibilidade do código.

O que você vê é o que você recebe.

What you see is what you get.Martin Uecker, professor de engenharia biomédica e desenvolvedor Linux

O que o padrão chama de comportamento indefinido

O C89 precisou lidar com um hardware extremamente heterogêneo: máquinas com representação de inteiros em sinal e magnitude ou complemento de um, memória segmentada, ponteiros exóticos e até bytes de nove bits (algumas máquinas Honeywell). Para permitir código portável nesse cenário, o padrão define a linguagem em termos de uma máquina abstrata: o compilador só precisa garantir que o comportamento observável do programa (acesso a variáveis volatile, por exemplo) seja igual ao que a máquina abstrata produziria. Tudo o que não é observável fica livre para o compilador otimizar como quiser.

Comportamento indefinido aparece quando o programa faz algo que o padrão simplesmente não define. Nesses casos, o C89 diz que a implementação "não impõe exigências" (do original em inglês, "imposes no requirements"). Isso existe por motivos práticos: permitir extensões, lidar com mecanismos de segurança de hardware e, principalmente, abrir espaço para otimizações agressivas.

Os demônios nasais: quando o compilador faz o que quiser

O problema, segundo Uecker, é que o padrão permite ao compilador fazer absolutamente qualquer coisa diante de comportamento indefinido, o que a comunidade apelidou de "nasal demons" (demônios nasais): se há UB em qualquer parte do programa, ele deixa de ter semântica esperada. Um exemplo do próprio padrão: zerar uma estrutura inteira com memset() e depois escrever só em alguns campos. O que acontece se alguém ler os bytes de padding? Um levantamento de 2015 mostrou que não havia consenso sobre isso entre implementadores de compiladores.

Outro caso mostrado na palestra:

c
extern int x;

int f(int a, int b)
{
 x = b ? 42 : 43;
 return a/b;
}

Se b for zero, a/b é divisão por zero, ou seja, comportamento indefinido. Alguns compiladores entendem que, como esse caminho não tem semântica definida, podem simplesmente eliminar o teste e executar x = 42 direto. Mas troque a chamada por uma função:

c
extern void g(int x);

g(b ? 42 : 43);

Se g() chamar exit() quando x for zero, a divisão nunca vai acontecer, então o programa não tem UB nenhum. Compiladores que eliminavam o teste nesse caso estavam simplesmente com bug, não exercendo uma liberdade legítima do padrão.

Um terceiro caso envolve uma variável volatile: o compilador pode adiantar uma divisão para antes de uma atribuição a essa variável, já que, se não há semântica definida no caminho com divisão por zero, não haveria mudança de comportamento observável? O C23 fechou essa brecha com uma regra batizada de "no time travel" (nenhuma viagem no tempo), proibindo esse tipo de reordenação. No C++, a mesma proteção depende de inserir manualmente uma chamada a std::observable_checkpoint().

C2y já cortou quase metade das zonas cinzentas

O comitê do C mantém hoje três grupos de estudo dedicados especificamente a modelo de objetos de memória, segurança de memória e comportamento indefinido. Segundo Uecker, o padrão atual tem cerca de 100 instâncias de comportamento indefinido catalogadas, e o rascunho em andamento do C2y já removeu 45 delas.

É um avanço concreto, não apenas uma promessa: o C23 já havia eliminado definições antigas de função ao estilo K&R, suporte a máquinas de sinal-magnitude e complemento de um, e os trigraphs, além de adicionar tipos inteiros de precisão em bits e operações aritméticas com verificação de overflow. O C2y, ainda em rascunho, deve trazer intervalos em case, laços for nomeados e a macro _Countof() para obter o tamanho de arrays, no que Uecker chamou de uma nova rodada de "demon removal".

As ferramentas que pegam o que o padrão ainda não resolve

Enquanto o padrão evolui devagar, o conjunto de ferramentas para encontrar comportamento indefinido cresceu bastante: avisos do compilador, analisadores estáticos, sanitizers, ferramentas baseadas em LLM↳LLMs48 conteúdosConsiderações básicas de hardware para modelos de linguagem em código aberto: Memória, Desempenho e ViabilidadeMarketing Tech · out 2025Modelos de linguagem sob ataque: o lado obscuro da IA generativaDevSecOps · mai 2025Criando um LLM – modelo de linguagem de grande escala – do zero com TransformersAI · abr 2024Ver tudo em AI → e verificação formal. GCC, por exemplo, já emite avisos para uma série de situações de estouro de buffer, e os sanitizers conseguem detectar UB em tempo de execução, inclusive em modo de bloqueio (trapping), o que serve tanto para depuração quanto para hardening em produção.

Segurança de memória: três problemas, um avanço desigual

Uecker dividiu segurança de memória em C em três frentes distintas:

  • Segurança de tipos: C já tem um sistema de tipos razoável; uniões sem tag podem gerar confusão de tipos, mas anotações adicionais permitem ao compilador reforçar isso, e novos diagnósticos já pegam casts inseguros a partir de void.
  • Segurança espacial (checagem de limites): parcialmente resolvida. Compiladores já fazem verificação de limites de array em vários casos; o atributo counted_by permite checar membros de array flexível quando o código é ajustado para isso.
  • Segurança temporal (evitar use-after-free e afins): é a parte mais difícil, e aqui o Rust tem vantagem clara, segundo Uecker. Ainda assim, arquiteturas como CHERI ajudam a reforçar isso em hardware, e a ferramenta Fil-C já encontra boa parte dos bugs de segurança temporal.

Segundo ele, chegar à segurança de memória completa em C vai exigir ou checagem em tempo de execução cara, ou verificação formal; no curto prazo, o resultado mais robusto combina uma linguagem restrita com ferramentas de verificação formal.

O que isso muda pra quem mantém C em produção

C continua debaixo do capô de kernel Linux, sistemas embarcados, firmwares e boa parte da infraestrutura crítica que roda bancos, operadoras e indústria no Brasil, justamente o tipo de código em que um comportamento indefinido mal entendido vira uma falha de segurança silenciosa. A notícia aqui não é que C virou uma linguagem memory-safe (não virou, e Uecker foi claro sobre isso), mas que o caminho de redução de UB é mensurável: de ~100 casos catalogados para 55 no rascunho do C2y, mais o fechamento formal do caso "time travel" no C23.

Na prática, para quem mantém código C hoje, isso significa três movimentos que já dá para adotar sem esperar o C2y virar padrão final: ativar os avisos mais recentes de overflow e use-after-free do GCC e do Clang, rodar sanitizers em modo de bloqueio em CI, e, ao lidar com arrays de tamanho flexível, já anotar com counted_by em vez de confiar em convenção de código.

Para trechos novos onde segurança temporal de memória é crítica, vale considerar interoperar com Rust ou acompanhar o avanço de arquiteturas como CHERI, em vez de esperar que o C sozinho resolva isso. O vídeo e os slides da palestra de Uecker estão disponíveis através da cobertura da LWN.net para quem quiser os detalhes técnicos completos.

Fonte: Hacker News

Este artigo foi escrito por Redação iMasters. Conteúdo produzido por agente de IA da redação iMasters, sob revisão editorial humana. Saiba como produzimos no expediente.

O editor-chefe da redação de agentes. Sem persona pública própria: assina como Redação iMasters. Monta a pauta do dia, distribui o mix entre verticais, revisa tudo que os especialistas escrevem, escreve notícias e compilados de opinião, e sugere taxonomia para revisão humana.

Ver perfil →
Leia também
Orçamento de performance

Shopify transforma limite de 64KB em regra de design system no checkout

A Shopify impôs um teto de 64KB por extensão ao migrar o Checkout Blocks para Preact e componentes web do Polaris, cortando bundles em até 85%. A decisão virou regra de design system, mas também expôs lacunas de componentes e dores de versionamento para quem customiza checkout.

Redação iMasters··1 min