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.

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:
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:
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_bypermite 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.
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.












