Executando verificação de segurança...
20

Memory safety não é o que você pensa

Com a fama de Rust, surgiram muitas pessoas interessadas na linguagem, mas também surgiram muitas para criticar. Críticas essas principalmente focadas em um aspecto específico da linguagem: Rust diz ser memory safe.

E então surge o ceticismo e, com ele, "argumentos" como:

  • "Falhas de segurança são culpa do programador."
  • "Rust diz ser segura, mas não tem provas disso."
  • "Dá para explorar Spectre em Rust, ela não é memory safe. Viu só?"
  • "Olha, um código Rust com falha de segurança. Viu? Viu???"
  • etc.

Entre outros argumentos sem embasamento técnico que são totalmente errados desde a concepção, uma vez que ninguém nunca disse que software escrito em Rust não tem falhas de segurança. O conceito de safety de uma linguagem de programação não é sobre isso.

Esses erros são cometidos pela falta de estudos sobre Programming Language Theory (PLT), que é a ramificação da Ciência da Computação que estuda o design e implementação de linguagens de programação.

Nesta área, o conceito de "safe" (que é diferente da palavra "secure" em Inglês) não está relacionado a segurança da informação e/ou falhas de segurança (repare: é information security e não information safety!). O conceito é sobre comportamento, não é sobre cybersec.

Quase toda operação na especificação de uma linguagem tem um comportamento bem definido do que vai acontecer no programa quando aquela operação é executada, mas é possível que, propositalmente, quem projetou a linguagem não defina o comportamento do programa quando uma determinada operação é executada. Exemplo em C:

char *str = NULL;
*str = 'A';

A especificação da linguagem C (ISO/IEC 9899) diz que escrever dados em um ponteiro NULL é comportamento indefinido (undefined behavior). Isto significa que a spec não diz o que vai acontecer, então o que acontece na prática depende de vários fatores: do compilador, do sistema operacional, do estado atual do processo e por aí vai.

Nota: Repare que este é um comportamento indefinido relacionado ao sistema de memória (memory) da linguagem.

Como mencionei antes, o conceito de safe é sobre comportamento. Um código safe é um código que não tem comportamento indefinido, todo comportamento no código é perfeitamente bem definido na especificação da linguagem.

Quando a gente diz que uma linguagem é memory safe, basicamente estamos dizendo que a linguagem não tem comportamento indefinido nas operações relacionadas ao sistema de memória, como: uso de ponteiros/referências, alocações de memória e afins.

E sim, isso pode ser provado. Não é a opinião do seu João de que Rust é memory safe. Ela é, isso é um fato.

E O QUE ISSO TEM A VER COM SEGURANÇA?

Bom, apesar do conceito de safety não ser sobre segurança, mas sim sobre comportamento, existe sim uma relação entre memory safety e segurança:

O fato é que a GIGANTESCA maioria das falhas de segurança em programas, escritos em C ou C++, são causadas por comportamento indefinido.

Carregando publicação patrocinada...
6

Isso vai ajudar muita gente entender a questão, obrigado por postar.

Mas tem um subtexto aí é algo que muita gente está entendendo menos ainda.

Muitas pessoas estão aprendendo Rust, e isso é bom. Alguns estão indo para ela, o que pode ser bom em alguns casos.

Mas a questão é que a maioria dessas pessoas nunca pensaram em usar C ou C++. Então porque raios estão pensando em Rust?

Rust não concorre com Java ou C# ou Go, ou linguagens de script. E essas linguagens sempre foram memory safe.

Eu tenho visto cada vez mais códigos em Rust onde a pessoa claramente não sabe obter eficiência, coisa que ele já não sabia fazer em outras linguagens, e por isso o GC não atrapalhava, até porque alguns são tão bons que até podia ajudar esses casos. Até dá para fazer soft real time nessas linguagens, e em C/C++/Rust só dá para fazer hard real time com técnicas bem fora do que se costuma adotar, a pessoa precisa saber fazer isso, e tenho a impressão que Rust dificulta isso e te obriga usar código unsafe.

Para usar Rust, assim como C, C++, Zig e outras linguagens do tipo, você precisa de um requisito de controle de memória muito forte, ter o máximo de eficiência de processamento, acesso baixo nível e necessidade de hard real time. A maioria das aplicações não precisam disso.

Se a pessoa faz essa escolha simples de forma tão errada, ela certamente não tem habilidade de engenharia para usar Rust de forma correta. Para os demais Rust é uma boa pedida. Mas vamos ver o que acontece com C++ que resolveu evoluir para esse lado de forma bem rápida, eles sentiram a água batendo no pescoço, e terá Carbon que manterá a as características de C++ (Rust não faz isso) e em um futuro a intenção é dar mais segurança.

S2


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente (não vendo nada, é retribuição na minha aposentadoria) (links aqui no perfil também).

2

Complementado:

C tem, o que, 32 palavras-chave? Um livrinho de 150 páginas que cobre todos os detalhes da linguagem que você lê em duas cagadas. Em uma tarde você domino o básico!

Rust? Um zilhão de keywords, mais de 600 páginas no livro oficial que mal arranha as partes cabeludas da linguagem, e boa sorte aprendendo o básico em menos de uma semana!

Faz zero absoluto, -273 Kelvin, de sentido alguém pular pra Rust sem passar por C. É como querer pilotar uma moto sem nunca ter andado de bicleta.

1

Eu falo isso, muita gente finge que não ouviu. Por isso falo que C é boa para aprender a programar.

Rust é uma linguagem complexa, é um bom meio termo, mas não tem tudo que C++ tem que é mais complexa ainda.

3

Complementado:

Parece um detalhe técnico, mas esse "buraco negro" na lógica do programa é o que torna o comportamento indefinido extremamente perigoso, porque ele simplesmente torna seu código inválido. O compilador pode literamente fazer QUALQUER coisa!

  1. Funciona por sorte — até que não funcione mais.

Você pode pensar: "Que bom, deu certo!" Mas não deu. Isso é pura sorte. O programa é inválido, e esse "funcionamento" não tem garantia nenhuma. Troque o compilador, rode em outra máquina ou adicione uma instrução qualquer e o resultado pode ser um travamento, um valor absurdo ou algo pior. Confiar nisso é como jogar roleta-russa com seu software. Já peguei código em C que quebrava quando removia um comentário!!!

  1. compilador vira um inimigo imprevisível

O compilador não tem obrigação de "fazer sentido" e isso é aterrorizante. Um erro em uma linha pode derrubar o programa em outro ponto, tornando o bug um pesadelo para rastrear. O programa roda direitinho por semanas, mas do nada o erro pode bagunçar a memória, criando falhas misteriosas que só aparecem muito distante da instrução problemática no tempo e espaço.

O comportamento indefinido é perigoso porque destrói a confiabilidade do seu software.

Mas, por que tem tanto comportamento indefinido em C? Quando o comitê se reuniu para formalizar a linguagem C, havia muitas implementações concorrentes, cada uma com suas próprias peculiaridades e comportamentos. Definir um padrão único para todos os casos teria quebrado muitos compiladores e programas existentes, que já dependiam dessas diferenças. Assim, para manter a compatibilidade e a flexibilidade, o padrão optou por deixar certos comportamentos "indefinidos". Essa decisão permitiu que cada implementação de C continuasse a fazer o que já fazia, sem forçar uma mudança radical. Em resumo, o comportamento indefinido existe em C por uma questão de legado, um compromisso para não quebrar o que já estava estabelecido. Mais prático sempre ganha de técnicamente superior. Se C não fosse essa ZONA teria perdido para o ADA como padrão de assembly portátil, se na época não deu, achar que Rust vai substituir o C hoje é completa loucura!!!

2
3

Uso Rust a cerca de 2 anos, amo a linguagem e toparia fazer qualquer coisa com ela, tenho até pequenos projetos em produção e bom dando os meus centavos (sei que não é sobre isso o post, mas tem uma relação aproximada). Rust não é tão segura quanto falam, propositalmente ela tem formas de "burlar" as regras para que programadores preguiçosos (ou go horse) entreguem código funcionando rapidamente. Isso deixa o código frágil e que abre margem para a ataques do tipo negação de serviço, pois a linguagem foi criada com o objetivo de nunca entrar em estado de erro (panico no caso do rust), esse tipo de situação é possível quando o dev exagera no uso de unwrap e derivados (isso faz com que você force o programa a considerar que aquilo não dará erro) é praticamente um await sem try/catch do JS. Um ataque que tenta causar erro no sistema pode deixá-lo indisponível (dependendo da infraestrutura).

Mas como alguem famoso ai disse... "Rust é uma linguagem chata e isso é ótimo" isso se deve pq ela te força a tratar os erros... Isso é chato, mas no longo prazo trás uma confiança absurda, um exemplo disso é um bot de telegram que fiz, ele gera e envia imagens baseado em dados .csv. Ele ficou funcionando sem erros por mais de 1 ano, só reiniciei quando tive que trocar o token do telegram.

3

Bom artigo, memory save é muito sobre não acessar algo que foi removido da memória ou ainda deixar coisas da memória que vão virar uma bola de neve no futuro.
Rust ajuda a resolver esse problema, realmente não quer dizer que nunca vão ter problemas de segurança do código onde ele não podera ser explorado. Toda a linguagem tem isso, o memory save tras essa solução mas, ao mesmo tempo pode trazer mais complexidade ao desenvolvimento, tudo precisa ser pesado quando faz um sistema.
Eu fico surpreso pela quantidade de hate que tem com o Rust (isso sem mencionar as analise mal feitas) ultimamente, até fiz um vídeo falando sobre isso.

1

Boa! E o engraçado do hate sobre Rust é que não dão um argumento técnico sequer, são sempre reclamações bobinhas ou baseadas em falta de conhecimento.

E quando apontam um aspecto técnico válido, como o tempo de compilação, nunca é um aspecto da linguagem, mas sim da implementação (compilador, runtime e afins).

Vou ficar realmente impressionado no dia que alguém criticar Rust com um argumento técnico válido. Vai ser uma novidade para mim.

0

Uso Rust tem cerca de dois anos e tenho pequenos projetos em produção com ele, amo a linguagem, mas o gasto de tempo e recurso para builds em produção é um pé no saco kkkk

0

Uso C++ tem cerca de 3 anos em code base Android/iOS, amo a linguagem, mas o gasto de tempo e recursos com toolchain/build system é um "pé no saco" kkkk

1

Oi Silva, bom dia!
Que texto bem escrito cara! Obrigado por me explicar um pouco sobre memory safety, eu ando pesquisando sobre essas comparações ai de RUST / C/C++.
Estou estudando C, e pretendo ir para C++ no futuro e tenho visto esse alvoroço em torno de que RUST é melhor que C++ e quase sempre relacionado a isso de segurança de memoria e algo do tipo.