1 pontos por GN⁺ 2025-12-14 | 1 comentários | Compartilhar no WhatsApp
  • Segurança de memória e sandboxing são conceitos de segurança independentes, e é preciso ter ambos para formar uma defesa robusta
  • O Fil-C é uma implementação com segurança de memória para C/C++, garantindo segurança até o nível de chamadas de sistema do Linux e podendo ser usado até em componentes do sistema como o OpenSSH
  • No processo de portar o sandbox baseado em seccomp-BPF do OpenSSH para o Fil-C, as principais questões foram a limitação da criação de threads e o ajuste dos filtros seccomp
  • Para o gerenciamento de threads em segundo plano do runtime do Fil-C, foi adicionada a API zlock_runtime_threads(), que controla o funcionamento das threads dentro do sandbox
  • O Fil-C implementa a aplicação sincronizada de chamadas prctl em todas as threads do runtime, para que no_new_privs e os filtros seccomp sejam aplicados de forma consistente a todo o processo

Relação entre segurança de memória e sandboxing

  • Segurança de memória e sandboxing são camadas de segurança diferentes, e apenas uma delas não oferece proteção completa
    • Exemplo de algo com segurança de memória, mas sem sandbox: um programa Java que pode sobrescrever arquivos por meio de entrada do usuário
    • Exemplo de algo em sandbox, mas sem segurança de memória: um programa escrito em assembly com privilégios restritos
  • Na prática, sandboxes reais têm brechas estruturais, como a permissão de comunicação com um processo broker
  • Por isso, combinar segurança de memória e sandboxing é a melhor forma de defesa

Integração entre Fil-C e o sandbox do OpenSSH

  • O Fil-C é uma implementação com segurança de memória para C/C++, mantendo essa segurança no nível das chamadas de sistema do Linux
    • O runtime do Fil-C pode funcionar até em componentes de sistema de baixo nível, como init e udevd
    • O OpenSSH funciona normalmente no Fil-C e utiliza um sandbox seccomp-BPF
  • No Linux, o OpenSSH constrói o sandbox com as seguintes ferramentas
    • chroot para restringir o acesso ao sistema de arquivos
    • execução como usuário/grupo sshd
    • setrlimit para limitar abertura de arquivos e criação de processos
    • filtro seccomp-BPF para permitir apenas chamadas de sistema autorizadas
  • O Fil-C oferece suporte básico a chroot e troca de usuário, mas setrlimit e seccomp-BPF podem entrar em conflito com o funcionamento do runtime, exigindo ajustes adicionais

Controle de threads no runtime do Fil-C

  • O runtime do Fil-C usa threads em segundo plano para coleta de lixo, interrompendo e reiniciando essas threads automaticamente quando necessário
  • O sandbox setrlimit do OpenSSH tem como objetivo proibir a criação de novos processos, então a criação de threads pelo runtime pode violar essa política
  • Para resolver isso, foi adicionada a API zlock_runtime_threads() em <stdfil.h>
    • O runtime cria imediatamente as threads de que precisa e depois desativa o encerramento automático delas
    • A chamada é feita na função ssh_sandbox_child do OpenSSH antes de setrlimit ou seccomp-BPF

Ajustes no filtro seccomp do OpenSSH

  • Depois da aplicação de zlock_runtime_threads(), a maior parte das funções do sandbox continua funcionando normalmente
  • As seguintes mudanças foram feitas no filtro seccomp
    • Em caso de violação, foi configurado SECCOMP_RET_KILL_PROCESS, para que as threads em segundo plano do Fil-C também sejam encerradas
    • MAP_NORESERVE foi adicionado à lista de permissões, dando suporte ao uso do alocador de memória do Fil-C
    • A chamada sched_yield foi permitida, pois é usada na implementação de locks do Fil-C
  • As chamadas futex usadas para sincronização no Fil-C já eram permitidas, então não foi necessária nenhuma mudança adicional

Como o Fil-C implementa prctl

  • O OpenSSH usa duas chamadas prctl ao instalar o filtro seccomp
    • PR_SET_NO_NEW_PRIVS para bloquear a obtenção de privilégios adicionais
    • PR_SET_SECCOMP, SECCOMP_MODE_FILTER para ativar o filtro
  • O problema é que prctl se aplica apenas à thread que faz a chamada, o que cria o risco de outras threads do runtime do Fil-C ficarem sem filtro
  • Para evitar isso, o Fil-C usa a API filc_runtime_threads_handshake() para fazer a aplicação sincronizada em todas as threads do runtime
    • Garante que cada thread execute a mesma chamada prctl
    • Se houver várias threads de usuário, gera um erro de segurança do Fil-C, reforçando a proteção

Conclusão

  • Combinar segurança de memória e sandboxing é a composição de segurança mais forte
  • O Fil-C integra completamente o sandbox baseado em seccomp do OpenSSH, mantendo a segurança de memória sem reduzir o nível de proteção
  • Em ambientes Linux, usar o Fil-C permite obter ao mesmo tempo segurança em nível de sistema e segurança em nível de linguagem

1 comentários

 
GN⁺ 2025-12-14
Comentários do Hacker News
  • Fico me perguntando por que não houve nenhuma menção ao landlock

  • Existe uma abordagem híbrida de compilação que segue C → WASM → C
    Isso permite controlar completamente a interação com o SO e fazer sandbox do acesso à memória como no WASM, enquanto tecnicamente continua sendo código C
    Mais informações podem ser vistas em RLBox

    • O sandbox de WASM não garante a correção (soundness) do programa
      Ele pode corromper a memória, mas o alcance disso fica restrito ao sandbox
      Sistemas como SECCOMP são burocráticos porque exigem definir minuciosamente todas as políticas de interação
      Já o Fil-C adota uma abordagem em que a própria linguagem e o runtime tentam garantir o funcionamento correto do programa
      Binários do Fil-C são executáveis comuns, então também podem ser usados junto com sandboxes como o SECCOMP
      Se o Linux levou 20 anos para criar a interface prctl, parece que vamos ter de esperar uns 10 anos para ver algo parecido no WASI
    • RLBox é uma tecnologia de sandbox, não uma tecnologia de segurança de memória
      Mesmo dentro do sandbox, ainda é possível criar fluxos de execução estranhos
  • O autor do Fil-C faz invenções tecnicamente interessantes muito bem, mas há preocupação sobre se a implementação foi validada o suficiente
    Foi dito que dá para compilar programas setuid com Fil-C, mas a parte modificada do ld.so pode ser arriscada
    Aplicativos setuid precisam ser escritos de forma muito defensiva em relação a variáveis de ambiente, descritores de arquivo, rlimit, sinais etc.
    Essas partes ainda parecem inacabadas, então há risco em usar isso numa infraestrutura real
    Ainda assim, testar uma base de código com Fil-C pode revelar bugs interessantes

    • Estou testando se existe uma forma prática de quebrar o Fil-C
    • Se isso realmente preocupa, o ideal é fazer experimentos você mesmo e compartilhar os resultados
    • O objetivo é tornar o runtime transparente para permitir auditoria (auditing)
      A modificação no ld.so é pequena, basicamente para ensinar o layout da libc
      O bug de getenv relacionado a setuid já foi corrigido para secure_getenv
      No que foi apontado, há um pouco de verdade misturado com um pouco de FUD
    • É uma pena que o autor do Fil-C responda às críticas de forma pouco colaborativa
      No Fil-C, em situações de corrida de dados, ponteiros e capabilities podem ficar inconsistentes
      Isso pode causar violação de segurança de memória
      O autor nega isso, mas comparar com Java é inadequado
      A tecnologia é excelente, mas a postura do autor reduz a confiança
  • WASM é ao mesmo tempo sandbox e ambiente de execução, e dependendo de como for usado pode oferecer algum grau de segurança de memória
    Se você compilar C para WASM, os bugs continuam existindo, mas o alcance do impacto fica limitado
    Portanto, faz sentido classificar o WASM como tecnologia de sandbox, mas como ambiente de execução ele oferece mais possibilidades

    • No fim das contas, WASM é uma tecnologia de sandbox
      Um bug no módulo B pode permitir ler dados do módulo A
      Ou seja, WASM não passa de um substituto para sandbox leve de processos
    • Dizer que “depende de como é usado” já é, por si só, prova de que o WASM não é completamente seguro
      Porque também dá para dizer que C é “seguro dependendo de como é usado”
    • O WASM não entende o modelo de memória de C, então não consegue implementar proteções como as do Fil-C
      O WASM impede escapar do runtime, mas não impede escapar da memória do programa interno
  • Foi pedido um comparativo entre Fil-C e Rust

    • As duas tecnologias são excelentes, mas seguem abordagens diferentes
      Fil-C é adequado para reforçar programas C existentes com foco em compatibilidade e segurança
      Rust é melhor para criar novas bases de código com segurança estática e desempenho
      Fil-C tem uma pequena perda de desempenho, mas é útil para código C existente (ffmpeg, nginx, sudo etc.)
      Rust se destaca em multithreading e no sistema de tipos
    • O Fil-C introduz GC, então pode haver queda de desempenho
      O objetivo é garantir segurança de memória, mais do que melhorar o design da linguagem
      Os concorrentes estão mais próximos de D, Nim e Go do que de Rust
    • O Fil-C usa verificações em runtime para detectar acessos inseguros à memória e encerrar o programa
      Rust previne isso em tempo de compilação
      As duas abordagens são ortogonais, e seria possível adicionar verificações em runtime no estilo Fil-C ao Rust
  • MicroVMs estão ficando cada vez mais populares
    Fico curioso para saber como o Fil-C poderia aproveitar isso

    • Exigiria um pouco de portabilidade, mas talvez fosse possível trazer algumas funcionalidades de microVM para o userland com segurança de memória do Fil-C
  • Espero que esse projeto receba mais atenção
    Seria ótimo ver ferramentas centrais como sudo e polkit distribuídas com segurança de memória

  • Seria bom ver mais uso de sandboxing mesmo em linguagens com segurança de memória
    É uma pena que até em Rust ou Go quase não se use sandbox no nível do SO

    • O Seccomp tem portabilidade e capacidade de composição fracas
      É difícil configurá-lo no nível de biblioteca, e ele é sensível à versão do kernel e à implementação da libc
      Também há a limitação de não conseguir filtrar entradas por trás de ponteiros, como caminhos de arquivos
      Por isso, normalmente ele precisa ser configurado manualmente no nível da aplicação
    • Rust quase não tem runtime, então é adequado para sandboxing
      Já o Go tem um runtime grande, o que dificulta torná-lo seguro como no Fil-C
  • Fiquei curioso sobre a diferença entre Fil-C e o Address Sanitizer (ASan) do clang
    Se for só uma questão de gerar pânico em runtime, fica difícil chamar isso de “segurança de memória”, não?

    • O ASan é ótimo para encontrar bugs, mas não garante segurança de memória completa
      Alguns bugs não são detectados
      Ele funciona colocando “red zones” ao redor da memória, então a detecção depende um pouco de sorte
    • Se um erro de memória for barrado por um pânico antes de causar efeito, isso também pode ser considerado segurança de memória
      Segurança de memória não significa “não trava”, e sim “acessos incorretos não conseguem produzir efeito”
  • Foi perguntado por que não usar uma VM completa como sandbox

    • VMs são excelentes sandboxes, mas aplicativos como Chrome ou OpenSSH precisam de separação de privilégios
      Um processo analisa entradas sem privilégios, enquanto outro roda com privilégios elevados
      Os dois processos se comunicam por IPC
      Usar VM aumenta a segurança, mas traz muito overhead, e recursos como GPU ou acesso a arquivos ficam mais complexos
      Por isso, em geral, o sandbox no nível do SO acaba sendo mais limpo
    • VMs resolvem a maioria dos problemas, mas aceleração gráfica em desktop ainda é difícil
      É preciso atribuir a GPU de forma dedicada, e o Qubes também só se conecta via encaminhamento de X11, então não há aceleração