Sandboxes de VM em Sub-Milissegundo com Copy-on-Write
Como o fork de memória copy-on-write cria sandboxes de VM em menos de 1 milissegundo, mudando serverless e o isolamento de segurança.

Iniciar um container Docker leva cerca de 500 milissegundos. Uma microVM Firecracker leva cerca de 125 milissegundos. Um isolate do V8 leva cerca de 5 milissegundos. Mas uma nova geração de sandboxes leves, que usa fork de memória com copy-on-write, consegue subir um ambiente de execução isolado em menos de 1 milissegundo, muitas vezes na faixa de 50 a 200 microssegundos. É rápido o suficiente para criar um sandbox novo para cada chamada de função.
Isso não é só uma melhoria incremental. É uma mudança qualitativa no que o sandboxing pode fazer. Quando criar um sandbox custa 500 ms, você os cria com parcimônia e reaproveita. Quando custa 50 μs, você cria um para cada entrada não confiável, cada invocação de plugin e cada requisição de usuário. O modelo de segurança muda de 'isolar tenants' para 'isolar operações individuais'.
O Que Copy-on-Write Realmente Significa
Copy-on-write (CoW) é uma técnica de sistema operacional em que você cria uma 'cópia' de uma região de memória sem copiar dado nenhum de fato. Tanto o original quanto a cópia apontam para as mesmas páginas físicas de memória, marcadas como somente leitura. Eles são indistinguíveis, pois ambos veem exatamente os mesmos dados. A cópia real só acontece quando um dos dois tenta escrever em uma página. Nesse momento, o kernel intercepta a escrita, copia apenas aquela página e deixa a escrita prosseguir na cópia.
A chamada de sistema fork() do Unix usa isso desde os anos 1990. Quando você faz fork de um processo, o filho recebe uma cópia completa da memória do pai, mas graças ao CoW nenhum dado é de fato copiado. Se o filho chamar imediatamente exec() (como é o típico), ele substitui toda a memória e as páginas CoW são simplesmente liberadas. O fork saiu quase de graça.
// Classic fork() — copy-on-write in action
pid_t pid = fork();
// At this point:
// - Parent and child share ALL physical memory pages
// - Both have identical virtual address spaces
// - Pages are marked read-only in both
// - Zero data has been copied
if (pid == 0) {
// Child process
// Reading shared_data is free — same physical page
int value = shared_data[0];
// Writing triggers CoW — only THIS page gets copied
shared_data[0] = 42;
// Now child has its own copy of this one page
// Parent's page is unchanged
}
Do Fork ao Sandbox
A sacada do sandbox com CoW é: em vez de subir uma VM ou container novo do zero, você pré-inicializa um ambiente 'template' (com runtime, bibliotecas e estado inicial carregados) e depois faz fork dele com CoW para criar cópias instantâneas. Cada cópia começa exatamente no estado em que o template parou, totalmente inicializada e pronta para executar, mas roda em seu próprio espaço de memória isolado.
A diferença de desempenho é enorme. A inicialização tradicional de uma VM envolve: carregar um kernel, inicializar o hardware, montar sistemas de arquivos, subir o sistema de init, carregar o código da aplicação e inicializar o runtime. Mesmo com otimizações agressivas (o Firecracker simplifica bastante isso), você ainda está gastando centenas de milissegundos em trabalho de inicialização.
O fork com CoW pula tudo isso. O template já fez a inicialização, e o fork cria uma cópia pré-inicializada em microssegundos. O 'custo de startup' é só a contabilidade do kernel para criar um novo espaço de endereçamento e duplicar as entradas da tabela de páginas, algumas milhares de operações, independentemente de quanta memória o template usa.
Onde Isso Muda o Jogo
Funções Serverless
Cold starts são o pesadelo da computação serverless. O AWS Lambda leva de 100 a 500 ms em um cold start, e mais em runtimes baseados em JVM. Isso é inaceitável para cargas sensíveis à latência, o que obriga os usuários a manter instâncias aquecidas (anulando o propósito do serverless) ou aceitar latência imprevisível.
Com sandboxes CoW, os cold starts caem para menos de 1 milissegundo. Toda invocação pode ser um 'cold start', porque cold starts são basicamente de graça. Não há necessidade de pools aquecidos, nem desperdício de memória com instâncias ociosas, nem estado residual entre invocações. Cada execução de função recebe um ambiente isolado e impecável, sem pagar o custo de inicialização.
Sistemas de Plugins e Extensões
Executar plugins não confiáveis com segurança é um dos problemas mais difíceis do software. Os navegadores resolveram isso para JavaScript com os isolates do V8. Mas para código arbitrário, como extensões compiladas, linguagens de script e plugins binários, as opções de isolamento ficaram limitadas a containers (lentos demais para isolamento por requisição) ou WebAssembly (ecossistema e suporte a linguagens limitados).
Sandboxes CoW oferecem um meio-termo: rodar código arbitrário em uma cópia isolada do ambiente do host, com configuração e destruição em menos de 1 milissegundo. O plugin enxerga um ambiente de SO completo (sistema de arquivos, rede, bibliotecas), mas suas modificações ficam contidas. Quando o sandbox encerra, todas as mudanças desaparecem. Isso é ideal para código enviado por usuários em sistemas de CI, ambientes de notebook e ferramentas de build.
Isolamento de Segurança
Ao processar entradas não confiáveis, como analisar um PDF enviado, renderizar HTML fornecido pelo usuário ou executar uma consulta no banco, rodar a operação em um sandbox isolado limita o raio de impacto de qualquer exploit. Se o parser de PDF tiver um buffer overflow, o atacante ganha controle de um sandbox descartável que está prestes a ser destruído, e não do servidor de aplicação.
Essa abordagem, que é isolamento de processo para toda operação não confiável, era impraticável com sandboxing tradicional porque o overhead superava o tempo de processamento. Se analisar um PDF leva 10 ms, gastar 500 ms criando um container não faz sentido. Mas gastar 100 μs criando um sandbox CoW é trivialmente barato.
Os Detalhes de Implementação
Construir um sistema prático de sandbox CoW exige resolver vários problemas além de simplesmente chamar fork().
- Contabilidade de memória. O CoW torna o uso de memória ambíguo. Se um template usa 1 GB e você faz fork de 100 cópias que modificam cada uma 10 MB, o uso físico de memória é de ~2 GB (1 GB compartilhado + 100 × 10 MB exclusivos), e não 100 GB. O kernel rastreia páginas compartilhadas versus privadas, mas obter o uso preciso por sandbox exige analisar
/proc/[pid]/smaps. - Isolamento de sistema de arquivos. O CoW resolve a memória, mas as escritas em arquivos precisam de isolamento próprio. Overlay filesystems (overlayfs) oferecem semântica CoW para arquivos: o sandbox enxerga o sistema de arquivos do template, mas as escritas vão para uma camada separada. Ao sair do sandbox, o overlay é descartado.
- Isolamento de rede. Cada sandbox precisa do seu próprio network namespace para evitar interferência. Os namespaces do Linux fornecem isso, mas criar network namespaces tem um overhead mensurável. Alguns sistemas reaproveitam um pool de namespaces pré-criados.
- Limites de recursos. Um sandbox que aloca memória sem limite ou consome CPU sem teto é um vetor de negação de serviço. Os cgroups fornecem limites de recursos (memória, CPU, I/O), mas criar e destruir cgroups adiciona overhead. De novo, o uso de pools ajuda.
- Limpeza determinística. Quando um sandbox encerra, todos os seus recursos (memória, descritores de arquivo, conexões de rede, objetos IPC) precisam ser limpos com confiabilidade. Os PID namespaces ajudam: mate o processo init do namespace e todos os descendentes são encerrados.
CoW vs. Sandboxes WebAssembly
WebAssembly (Wasm) é a outra grande tecnologia de sandboxing leve. Vale comparar as duas, porque elas fazem trade-offs fundamentalmente diferentes.
Sandboxes Wasm executam código em uma máquina virtual com segurança de memória e um modelo de memória linear. O sandbox não consegue acessar nada fora da sua memória linear: nada de sistema de arquivos, rede ou chamadas de sistema (a menos que sejam fornecidas explicitamente via WASI). Isso é extremamente seguro, mas restritivo: o código existente precisa ser recompilado para Wasm, e nem todas as linguagens compilam bem para Wasm.
Sandboxes CoW executam código nativo em um ambiente de SO isolado. O sandbox tem acesso a uma interface de SO completa (possivelmente restringida por filtros seccomp), pode rodar qualquer binário e usa bibliotecas de sistema normais. É menos restritivo, porém menos seguro, pois a fronteira de isolamento é o modelo de processos do SO, que tem uma superfície de ataque maior que a VM minimalista do Wasm.
Escolha Wasm quando: você controla o código que será isolado, sua carga de trabalho compila limpo para Wasm e você precisa do isolamento mais forte possível. Escolha sandboxes CoW quando: você precisa rodar binários existentes arbitrários, sua carga de trabalho exige capacidades de nível de SO (sistema de arquivos, rede, processos filhos) e você quer priorizar compatibilidade em vez de superfície de ataque mínima.
A Armadilha: Fork em Programas Multithread
Existe uma armadilha bem conhecida com fork(): ele copia apenas a thread que o chamou. Se o pai tem 20 threads, o filho fica com uma. Quaisquer mutexes mantidos pelas outras 19 threads continuam marcados como travados na memória do filho, mas as threads que os seguravam não existem mais. O filho vai travar (deadlock) na primeira vez em que tentar adquirir um desses mutexes.
Sistemas de sandbox CoW contornam isso garantindo que o processo template seja single-threaded no momento do fork. Normalmente isso significa: inicializar tudo no template (carregar bibliotecas, configurar o runtime, preparar o estado inicial), depois parar todas as threads exceto a principal, fazer o fork e deixar que cada filho recrie threads conforme necessário. O custo de inicialização é pago uma vez, e o fork evita o risco de threads.
Algumas abordagens mais novas usam userfaultfd ou handlers customizados de page fault para implementar semânticas parecidas com CoW sem depender de fork(). Elas evitam o problema do multithreading, mas adicionam complexidade e exigem mais coordenação em nível de kernel.
O Que Acompanhar
Sandboxing em sub-milissegundo ainda está no início, mas os blocos de construção são sólidos (fork, namespaces, cgroups e overlayfs são todos maduros). Os sistemas construídos sobre eles, para computação serverless, CI/CD e execução segura de código, estão provando que o isolamento por operação é prático em escala. À medida que essas ferramentas amadurecem, a suposição de que sandboxing é caro vai ficar tão defasada quanto a ideia de que coleta de lixo é lenta demais para aplicações em tempo real. O overhead está desaparecendo, e os benefícios de segurança de 'isolar tudo' estão ficando difíceis de ignorar.


