urMQTT num VPS ARM64 real: sem ecrã, sem gerador de números aleatórios por hardware e com um handshake demasiado lento
O que aconteceu depois do texto sobre a v0.3.0: a primeira execução num servidor aarch64 real com KVM (instalação no seu disco, um corte de energia, números do disco), uma entrada de diagnóstico que reporta pela rede porque a consola é invisível, o que encontrou sobre o gerador de números aleatórios, um assembly P-256 para ARM64 que tornou os handshakes completos cerca de 1,5 vezes mais frequentes, e os erros que cometemos pelo caminho.

O texto anterior, urMQTT v0.3.0, terminava com quatro pontos sob "O que se segue": uma decisão sobre o broker de demonstração público (D1), fuzzing dos analisadores de PCI e virtio e um teste para as tabelas de páginas bloqueadas, um diálogo para a chave na página do construtor, e bare metal. Nenhum dos quatro foi feito nesta ronda, e dizemo-lo primeiro. O que fizemos em vez disso é o que sempre faltou: executámos o urMQTT num servidor ARM64 real com KVM, e quase tudo o que se segue vem dessa máquina. O primeiro ponto da lista antiga, o CI, foi posto em ordem (o texto anterior e as notas da versão que o davam como vermelho foram corrigidos).
O aviso prévio mantém-se: o projeto está em fase pré-alfa, os números medem código, testes e experiências concretas numa máquina, não maturidade em produção. Onde um número não foi medido na máquina de destino, dizemo-lo.
Como foi ao longo do tempo
Desde o último texto (3 de novembro, 02:43) até hoje (4 de novembro, 03:18), o main tem 29 commits novos, 244 no total. Como antes, o projeto é construído com o Claude Code (os commits trazem Co-Authored-By: Claude). Os números são calculados com o método dos dois últimos textos (todos os ficheiros .rs fora de vendor/ e fuzz/, funções #[test], títulos ## ADR- em docs/DECISIONS.md); o método dá os mesmos números de então no commit antigo.
| Momento | Commit | Hora | Linhas de Rust | Testes | ADR | Commits |
|---|---|---|---|---|---|---|
| último texto | f1a7fbc | 3 de novembro, 02:43 | 115 148 | 962 | 133 | 215 |
| hoje | efddf6e | 4 de novembro, 03:18 | 117 084 | 978 | 134 | 244 |
Crescimento: +1936 linhas, +16 testes, +1 ADR. Outra vez pouco código novo; o próprio assembly P-256 (1509 linhas) está incorporado como código de terceiros (vendored) e não entra no número de linhas.
Uma segunda máquina real
Até agora o projeto tinha um só servidor real: um VPS da Contabo com um AMD EPYC (x86_64, KVM), usado para a comparação de desempenho com o Mosquitto. O ARM64 só tinha corrido em QEMU, com aceleração por hardware num Mac e sem ela no CI. A máquina nova é um netcup VPS 500 ARM G12.5: um núcleo Neoverse N1 a cerca de 3 GHz, 2 vCPU, 3,9 GiB de RAM, um disco virtio de 64 GiB, QEMU/KVM com firmware UEFI, uma placa de rede virtio. Tudo foi conduzido através da API REST do fornecedor com um cliente de linha de comandos: carregamos a ISO, ligamo-la como CD virtual, mudamos o dispositivo de arranque, desligamos e voltamos a ligar.
O que fizemos nela, por ordem:
- Primeiro um instantâneo do sistema original. Com UEFI não é possível um instantâneo online, pelo que o servidor foi desligado e fez-se um instantâneo offline.
- Instalação. O instalador da ISO escreveu a imagem no disco (com a contagem decrescente de 30 segundos incluída, cerca de 40 segundos no total) e desligou a máquina por si próprio, como previsto.
- O arranque a partir do disco instalado demorou cerca de 35 segundos até um handshake TLS.
- Uma mensagem retida sobreviveu a um corte de energia brusco. Publicámo-la, cortámos a alimentação através da API, arrancámos de novo e lemo-la.
- O disco instalado, medido pela entrada de diagnóstico (apenas leituras; o teste de escrita foi omitido porque a disposição instalada não deixa espaço por atribuir, o que é intencional): leitura sequencial 332 MiB/s, leituras aleatórias de 4 KiB 22 147 por segundo com p50 10,4 µs, p90 28,3 µs, p99 671 µs, máximo 9,9 ms (2000 leituras).
Cinco pontos não são uma bateria de testes: é uma máquina, uma só execução de cada um, e o caminho de instalação foi exercitado uma vez no disco real. O que mostra é que o controlador virtio-blk do próprio kernel, o instalador e o arranque funcionam num convidado de nuvem real e não apenas num emulador de laboratório.
Sem ecrã, por isso o diagnóstico vai pela rede
A entrada de diagnóstico da v0.3.0 desenha os seus números no framebuffer. Neste servidor a consola do fornecedor diz "Display output is not active": a máquina virtual tem uma placa gráfica virtio para a qual não temos controlador, e a porta série não está exposta. Os números existiam e ninguém os podia ler.
A solução é uma palavra de arranque, diag-serve: a máquina executa as mesmas medições, depois arranca como um broker live normal e publica o relatório como um único documento JSON na mensagem retida $SYS/diag/report, que só um certificado de operador pode ler. Uma pequena ferramenta, urmqtt-diag-png, desenha o painel a partir desse JSON com o mesmo código que o kernel usa no ecrã (a mesma disposição e o mesmo tipo de letra 8x8), pelo que a imagem é o ecrã que a máquina teria mostrado:

Pelo caminho acrescentaram-se ao ecrã duas coisas de que sentíamos falta: o relógio da CPU (o contador de ciclos da PMU face à base de tempo: de 2,95 a 2,98 GHz aqui; onde só se consegue ler um temporizador de frequência fixa, diz "não mensurável" em vez de imprimir a frequência do temporizador como se fosse a da CPU) e um painel para o gerador de números aleatórios.
De que é feito o gerador de números aleatórios
O painel indica de onde o gerador foi inicializado e testa bytes novos de cada fonte de hardware. Nesta máquina diz:
- virtio-rng: nenhum dispositivo.
RNDR(a instrução própria da CPU): não disponível nesta CPU. - O gerador de números aleatórios EFI do carregador: 8 de 8 valores diferentes de zero.
- O recurso alternativo de jitter dos tempos da CPU (ADR-038): 1024 amostras de 64 passos através do contador de ciclos da PMU, 3 bits estimados por amostra, 768 bits creditados (um quarto da estimativa, por desenho), testes de integridade passados.
- A saída do gerador, um teste de sanidade sobre 4096 bytes: 49,6 % de uns, a sequência mais longa de 16 bits. É uma verificação de que nada está preso ou enviesado, não uma certificação estatística.
Portanto o gerador deste servidor assenta no recurso alternativo de jitter e no que o firmware entregou. É exatamente o que o ADR-038 descreve, e é a inicialização mais fraca que o kernel aceita. Não lhe chamamos avariada: os bits creditados estão acima do limiar que o kernel exige (256), e o estimador é conservador. Também não podemos demonstrar que seja tão boa como uma fonte de hardware. Se uma imagem de produção deve recusar-se a arrancar sem uma fonte de hardware, a menos que o operador o aceite expressamente, é uma pergunta em aberto para o proprietário do projeto (está no roteiro, mais abaixo). Na prática é também uma pergunta para o fornecedor: um dispositivo de números aleatórios por hardware para a máquina virtual resolvê-la-ia.
Um handshake demasiado lento
A entrada de diagnóstico mostrou também quanto custa no servidor um handshake completo com TLS mútuo: 1,08 ms. Quase tudo é aritmética de chave pública: duas verificações ECDSA P-256 (o certificado do cliente e a prova do cliente), cerca de 508 µs cada, X25519 176 µs, uma assinatura ECDSA 141 µs. Em x86_64 tínhamo-lo resolvido antes (ADR-130, o assembly OpenSSL/Intel: P-256 três a quatro vezes mais rápido); em ARM64 continuava a correr o código portátil do RustCrypto.
Fizemos o mesmo para o ARM64 (ADR-255). O assembly é o nistz256 ARMv8 do OpenSSL (Apache-2.0), tirado do ring 0.17.14 e incorporado com a sua proveniência e cada alteração mecânica registada. O que contou nos pormenores:
- Só inteiros. O assembly não usa registos NEON. As duas funções de seleção na tabela que o original implementa com NEON foram retiradas e escrevemos em Rust varrimentos mascarados em tempo constante (custam no máximo 1,4 µs de uma assinatura de 12 µs).
- Proteção de saltos. O kernel é compilado com BTI e autenticação de apontadores. Cada um dos 8 pontos de entrada exportados tem um landing pad, verificado no desassemblado do kernel, e os seis que guardam o link register assinam-no.
- Testes, porque isto é criptografia na base em que confiamos. Toda a bateria de x86_64 corre agora também em aarch64 Linux: aritmética do corpo e do escalar face a uma referência escolar, toda a tabela do gerador (2368 entradas) face ao RustCrypto, assinaturas iguais bit a bit às do RustCrypto, veredictos de verificação, ECDH com entradas fora da curva e zero, e os testes de resposta conhecida que correm em cada arranque pelos caminhos de produção. Numa máquina virtual de quatro núcleos sob uma tempestade de interrupções correram 59 526 rondas de resposta conhecida com 0 discrepâncias.
- Um teste de temporização com um controlo, e um sinal que era o nosso próprio teste. Uma multiplicação deliberadamente com fugas obtém |t| de 482 a 1755, pelo que o teste consegue ver uma fuga a esta escala. Num núcleo Apple dentro de um contentor, o ECDH obteve de 0,2 a 2,1, a assinatura de 0,5 a 1,9, a geração de chaves de 0,8 a 5,5. No núcleo de servidor arm64 do runner do CI, a assinatura ECDSA obteve |t| = 8,6, 9,1 e 8,3 em três tentativas, acima do nosso limite de 7, enquanto o ECDH (3,9) e a geração de chaves (1,9) passaram na mesma máquina. Um resultado que se repete não é ruído, pelo que o registámos como um achado em aberto (A255-W1) e, durante algumas horas, aumentámos o limite para esse único caso para 12, para que a compilação pudesse passar. Depois encontrámos a causa, e era a estrutura de teste: o caso assinava uma única mensagem fixa, pelo que a chave esparsa produzia sempre a mesma assinatura e as chaves aleatórias uma diferente de cada vez, e a codificação DER de uma assinatura repetida é mais rápida (|t| de 49 a 57 só para a codificação). Com uma mensagem aleatória por medição nas duas classes, o caso obtém 1,4 e 1,8 no runner (uma máquina virtual Neoverse N2), a geração do nonce sozinha 2,7 e 1,1, e o limite voltou a 7. Uma coisa continua em aberto (A255-W2): a flag do processador que torna o tempo do multiplicador independente dos dados nos núcleos Arm (DIT) não é ativada em lado nenhum, nem pelo kernel nem em volta das chamadas do lado do anfitrião. Nenhuma medição mostra uma diferença, mas a máquina virtual do runner não expõe a funcionalidade, pelo que o teste também não a podia mostrar.
O que ganhou
Primeiro o microbenchmark na máquina onde o desenvolvemos, um Apple M4 Pro (não é a CPU de destino, pelo que as proporções no servidor podem diferir), com o mesmo código que o handshake chama, antes e depois:
| Operação | antes (µs) | depois (µs) | proporção |
|---|---|---|---|
| geração de chaves | 30,8 | 7,0 | 4,4x |
| ECDH P-256 | 135,2 | 25,0 | 5,4x |
| assinatura ECDSA | 34,7 | 11,6 | 3,0x |
| verificação ECDSA (chave desconhecida) | 147,4 | 29,1 | 5,1x |
| verificação de CA (emissor registado) | 54,2 | 9,6 | 5,6x |
| X25519 (código inalterado) | 20,4 | 20,1 | 1,0x |
| estimativa do handshake (soma do anterior) | 292 | 104 | 2,8x |
Depois o real: o mesmo gerador de carga, o mesmo método do texto anterior sobre desempenho (handshake TLS 1.3 completo com certificado de cliente, sem retoma, MQTT CONNECT, o servidor fecha primeiro), executado a partir de uma máquina a 9 ms do servidor, contra o mesmo VPS da netcup, antes e depois da alteração. A imagem do benchmark tem os limites do handshake relaxados, como na comparação com x86_64; três repetições, o número é a mediana.
| Threads | antes (lig/s) | depois (lig/s) | proporção | erros |
|---|---|---|---|---|
| 32 | 848 (408-855) | 1317 (957-1317) | 1,55x | 0 |
| 64 | 827 (815-839) | 1398 (1352-1416) | 1,69x | 0 |
| 128 | 778 (714-788) | 1316 (1313-1383) | 1,69x | 0 |
| Parte TLS de uma ligação, p50, 32 threads | 18,5 ms | 13 ms |
Os handshakes completos por segundo subiram cerca de 1,55 a 1,7 vezes, não as três vezes que a aritmética sozinha daria. A razão é simples e vale a pena dizê-la: acelerámos o P-256, e o X25519 (176 µs), a pilha TCP/IP, a camada de registos e o MQTT não mudam, pelo que a fração que resta ganha peso (lei de Amdahl). O próximo candidato é o X25519.
Para orientação, ao lado do servidor x86_64 do texto anterior. Estes números não são comparáveis como numa corrida: CPU diferentes, um número diferente de núcleos (4 contra 2), uma distância diferente ao gerador (15 ms contra 9 ms) e, em ambos os casos, um anfitrião de carga partilhado que durante as execuções perdeu de 5 a 60 % da sua própria CPU. A coluna comparável é o custo de CPU do servidor por handshake:
| Contabo, x86_64 EPYC (asm) | netcup, ARM64 N1 antes | netcup, ARM64 N1 depois | |
|---|---|---|---|
| CPU do servidor por handshake | 0,26 ms (a consola do broker) | 1,08 ms (diagnóstico) | 0,55 ms (diagnóstico, uma execução, ver abaixo) |
| handshakes/s, 32 threads | 169 (bench) | 848 | 1317 |
| handshakes/s, 64 threads | 278 (bench) | 827 | 1398 |
| handshakes/s, 128 threads | 413 (bench) | 778 | 1316 |
No mesmo dia a entrada de diagnóstico correu de novo nesta máquina, com o assembly (numa compilação que contém também o controlador gráfico descrito na atualização no fim): o custo de CPU do servidor de um handshake TLS mútuo foi de 553 µs contra 1,08 ms antes, 1,95 vezes menos. A verificação ECDSA demorou 192 µs (508 µs antes), a assinatura 78 µs (141 µs) e o X25519 209 µs (176 µs; esse código não mudou, e o relógio da CPU nesta execução era de 2,58 GHz contra 2,95 a 2,98 GHz nas anteriores, pelo que valores isolados nesta máquina partilhada variam em dezenas de por cento). É uma execução, num anfitrião partilhado. Que o ritmo de handshakes tenha subido 1,55 a 1,7 vezes enquanto o custo do próprio servidor por handshake desceu 1,95 vezes é compatível: o resto de uma ligação é a pilha, o gerador e a rede. A variação entre execuções vê-se nos ficheiros em bruto: cada série de três tem um valor atípico (408 antes e 957 depois com 32 threads), quando o anfitrião de carga perdeu grande parte da sua CPU para outras máquinas virtuais. Com 128 threads, em ambas as máquinas é o gerador, não o servidor, que está no limite.
O resto da bateria nesta máquina
A mesma bateria do servidor x86_64 (débito, latência, fan-out, ligações inativas) correu por inteiro contra a nova compilação. Para estas linhas não há um "antes": a execução foi interrompida para construir o assembly, pelo que são o estado depois da alteração, e o assembly não toca nestes caminhos, exceto nos handshakes que abrem as ligações. Um facto explica a maior parte do que se segue: esta máquina virtual tem duas vCPU. O núcleo 0 trata da placa de rede, do despacho e de todo o trabalho de MQTT e TLS, o núcleo 1 é o da base de dados e esteve ocupado 2 a 3 % o tempo todo. O servidor x86_64 tinha quatro vCPU e dois núcleos de trabalho. Tudo o que se segue é, portanto, desempenho do broker com um só núcleo.
| Ligações inativas | 5000 | 10 000 | 20 000 |
|---|---|---|---|
| ligadas | 5000 de 5000 em 4,3 s | 10 000 em 9,3 s | 20 000 em 21,2 s |
| crescimento do heap do kernel por ligação | 0,34 KB | 0,41 KB | 0,43 KB |
| erros | 0 | 0 | 0 |
(De 0,50 a 0,66 KB por ligação no servidor x86_64 no commit de H7. O número do heap é $SYS/broker/memory/used menos o seu valor antes da primeira ligação.)
| Latência a um ritmo modesto (8 pares x 100 mensagens/s, 64 B) | p50 | p99 |
|---|---|---|
| QoS 0 | 4,0 ms | 6,9 ms |
| QoS 1 | 4,1 ms | 7,0 ms |
| Fan-out (1 publicador, 64 B) | entregues por segundo | p50 | p99 |
|---|---|---|---|
| 100 subscritores, QoS 0, 50 msg/s | 5000 (todas) | 5,9 ms | 8,6 ms |
| 100 subscritores, QoS 1, 50 msg/s | 5000 (todas) | 6,3 ms | 10,0 ms |
| 1000 subscritores, QoS 0, 10 msg/s | 8805 de 10 000 | 207 ms | 5,4 s |
| Flood, 16 pares, publicadores à máxima velocidade que o broker aceita | publicadas/s | entregues/s | p50 / p99 | ligações perdidas |
|---|---|---|---|---|
| QoS 0, 64 B | 231 317 | 85 140 | 1,36 s / 2,8 s | 0 |
| QoS 0, 1 KiB | 62 110 | 25 506 | 336 ms / 873 ms | 0 |
| QoS 1, 64 B | 107 349 | 55 965 | 21 ms / 29 ms | 0 |
| QoS 1, 1 KiB | 51 369 | 25 054 | 47 ms / 60 ms | 0 |
| QoS 1, 64 B, 64 pares | 130 462 | 69 411 | 69 ms / 87 ms | 0 |
Como ler. A latência a um ritmo modesto é a contribuição do broker mais uma viagem até ao gerador: o gerador está mais perto deste servidor do que o da comparação com x86_64, pelo que as duas colunas de latência não são comparáveis. O flood de QoS 0 mostra o quadro habitual de um único núcleo sobrecarregado: o broker aceita 231 000 mensagens por segundo e entrega 85 000 (o resto é descartado pelas regras de contrapressão, que é para isso que existem) com latências de segundos, e nenhuma ligação se perdeu; no servidor x86_64 o mesmo tipo de flood custava várias ligações por execução ao watchdog de 20 ms (desde então o ADR-221 mudou a forma como esse orçamento é contado). A linha dos 1000 subscritores tem o mesmo carácter de antes, caudas de segundos, em parte do próprio gerador. Os números no commit de H7 no servidor x86_64 estão no texto anterior; não os pomos na mesma tabela que estes, porque o código entre as duas medições mudou no caminho dos dados (do ADR-221 ao ADR-224) e as máquinas diferem em tudo.
Erros que encontrámos nós próprios
- A imagem dizia "nenhum disco encontrado" e o disco estava no relatório. A primeira versão do
urmqtt-diag-pngnão lia sequer o campodisks. O proprietário do projeto olhou para a imagem, disse que parecia errada, e tinha razão. Corrigido, e agora um teste volta a ler um disco do JSON. - Demos o instalador por terminado cedo demais. O estado do servidor passou a "desligado" duas vezes: uma no fim do ciclo de alimentação e outra quando o instalador terminou. O nosso script tomou a primeira pela segunda, desligou a ISO e arrancou o sistema antigo, que reportou o número de versão antigo e nos fez procurar no instalador uma falha que não existia. Agora o script espera primeiro por um arranque novo.
- Perdeu-se o histórico de progresso. Uma análise para o roteiro descobriu que o commit do marco S6 tinha substituído
docs/PROGRESS.md(1763 linhas, marcos de M0 a S5) pela sua própria entrada de 26 linhas. Passou despercebido durante dias, embora o README chame a este ficheiro "a fonte de verdade". Foi restaurado a partir do histórico. - O CI falhou depois do assembly, em dois sítios. Três testes de fumo tinham o tamanho da imagem de arranque escrito neles ("10 MiB"), que passou a 11 MiB com o kernel maior; já não assumem o tamanho. A outra falha foi o teste de temporização descrito acima: à primeira pareceu ruído num runner partilhado, pelo que fizemos o teste repetir-se três vezes; falhou nas três tentativas, e foi assim que soubemos que era um sinal, e o sinal revelou-se ser o nosso próprio teste (assinava sempre a mesma mensagem). Aumentámos esse único limite durante algumas horas, dissemo-lo abertamente e voltámos a baixá-lo quando a causa foi corrigida.
- A documentação conta três palavras de arranque e o código tem quatro (
diag-serve), e a tabela de marcos do README continua a mostrar como planeado o trabalho da última semana. Ambas as coisas estão no roteiro como pontos a corrigir.
Um roteiro, com perguntas
Pela primeira vez há um roteiro: cerca de 40 pontos em três fases (v0.4 endurecimento e operação; protocolo e rede; scale-out, mais nuvens e bare metal), cada um com uma razão tirada de um documento, um esforço, um risco e uma forma de verificação, 16 perguntas a que só o proprietário do projeto pode responder, e uma lista do que fica fora do âmbito. Algumas coisas di-las sem rodeios: só o MQTT 3.1.1 é suportado, uma publicação QoS 2 fecha a ligação, não há IPv6 (o fornecedor atribui um /64), nem WebSocket nem ligações de saída a partir do broker, e não há controladores para os dispositivos de rede e de armazenamento das grandes nuvens.
O que ainda não está demonstrado
- O custo de CPU por handshake com o assembly foi medido no núcleo Neoverse apenas uma vez (553 µs contra 1,08 ms antes, a um relógio mais baixo, num anfitrião partilhado); o fator de 1,55 a 1,7 vem do ritmo de handshakes completos, e o de 3 a 5 de um núcleo Apple. O lado x86_64 da refatoração (o código movido para o seu próprio ficheiro) foi verificado nos tipos e compilado, mas os seus testes unitários não foram executados e o kernel x86_64 não foi arrancado depois dela: as suites x86_64 não estão no CI, são executadas à mão no Contabo VPS, e isso ainda não foi feito para esta alteração.
- O tempo constante: para a geração de chaves P-256, o ECDH e a assinatura (a chave e o nonce), o teste estatístico não encontrou dependência de um segredo esparso. Correu num núcleo Apple e no runner do CI (uma máquina virtual Neoverse N2), não no núcleo Neoverse N1 da netcup, e nunca com a flag DIT ativa num núcleo de servidor (A255-W2, em aberto: o kernel não a ativa). Não correu para o X25519 nem para o Ed25519. Um teste de caixa preta limita apenas aquilo que consegue resolver ao seu tamanho de amostra.
- Um servidor, um fornecedor, uma instalação. Vários resultados (instalação, um corte de energia) são uma única execução. Além dos handshakes, as linhas de desempenho não têm um "antes", e uma máquina de duas vCPU dá ao broker um só núcleo de dados.
- O gerador de números aleatórios deste servidor é inicializado a partir do jitter e do firmware, como descrito acima.
- A chave de assinatura continua a ser uma chave de teste, as imagens da versão são compilações de desenvolvimento, e as traduções deste texto foram escritas por IA e não foram verificadas por falantes nativos.
- Sem auditoria independente, sem bare metal, sem IOMMU.
O que se segue
- X25519 em assembly para ARM64 (são 176 µs do handshake restante), e repetir a medição por handshake no núcleo Neoverse mais do que uma vez.
- Uma decisão sobre o valor por omissão do gerador de números aleatórios: recusar arrancar sem uma fonte de hardware, a menos que o operador diga o contrário, ou manter o recurso alternativo de jitter e dizê-lo claramente.
- A lista antiga: uma decisão sobre D1, fuzzing dos analisadores de PCI e virtio, o diálogo para a chave na página do construtor.
- Do roteiro, para a próxima versão: uma chave de assinatura real e uma PKI de produção, MQTT 5.0 e QoS 2.
O código está em github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md continua a ser a fonte de verdade sobre o que funciona, docs/PERFORMANCE.md sobre o que foi medido (a secção 10 tem os números do assembly), docs/AUDIT.md sobre o que não está demonstrado. Pré-alfa, como antes; relatórios de erros e relatórios de segurança que sigam as instruções de SECURITY.md são bem-vindos.
Atualização, 4 de novembro, mais tarde no mesmo dia. Quatro coisas das horas a seguir à publicação deste texto.
- O valor em falta. A entrada de diagnóstico correu de novo no servidor da netcup com o assembly e deu o número que falta na primeira tabela: 553 µs de CPU do servidor por handshake TLS mútuo contra 1,08 ms antes (detalhes por baixo dessa tabela).
- O sinal na assinatura era o nosso teste. A causa do sinal de temporização descrito acima foi encontrada e corrigida na estrutura de teste (assinava sempre a mesma mensagem); o limite voltou a 7. O que continua em aberto é que a flag DIT não é ativada em lado nenhum (A255-W2).
-
O problema do ecrã está resolvido, e está no
main. Escrevemos um controlador para a placa gráfica virtio (apenas 2D, ADR-256), ligado por uma chave de configuração que fica gravada na imagem (console.virtio_gpu, por omissãooff;autoeonexigem o formato de configuração 5). Comon, o separador Ecrã do fornecedor mostra o painel de diagnóstico em direto no servidor da netcup, a mesma imagem de cima, incluindo o último ecrã com "press any key". O seu alvo de fuzzing encontrou um erro real na primeira versão: uma resposta de informação do ecrã com quatro bytes a menos do que a especificação era aceite como válida (A256-W1); um dispositivo real envia sempre a resposta inteira, pelo que o servidor não foi afetado, e o analisador rejeita agora a curta. O que não verificámos: que o ecrã escuro tenha mesmo vindo de o firmware reiniciar a placa quando passa o controlo ao kernel (provável, não confirmado), a interação com o Secure Boot e o x86_64 numa máquina real (só correu sob emulação). Um limite conhecido (A256-W2): a definiçãoautonão distingue um framebuffer emulado ativo junto de uma placa virtio de um morto, pelo que nessa única configuração escolhe a placa virtio. O controlador acrescenta ao kernel um analisador das respostas do hipervisor, razão pela qual está desligado a menos que a configuração o peça. -
A versão. Tudo isto está na v0.4.0, publicada na mesma noite como pré-versão (versão no GitHub). A página do construtor neste site oferece-a primeiro; precisa do formato de configuração 6, e os modelos mais antigos (v0.2.0 a v0.3.0) recusam as chaves novas com uma mensagem que diz para usar uma versão mais recente. Os limites da v0.3.0 mantêm-se: a chave de assinatura continua a ser uma chave de teste, e as imagens são compilações de desenvolvimento com certificados de teste. O CI no commit da versão só ficou verde depois de uma tarefa ser corrida de novo (um teste de cliente dentro de um cenário smoke excedeu o tempo limite uma vez e passou à segunda). O que verificámos nos ficheiros publicados: a assinatura minisign, as somas de verificação, as atestações de compilação do kernel e do SBOM, e o modelo com as chaves novas (a v0.4.0 aceita-as, a v0.3.0 recusa-as). O controlador para os novos dispositivos virtio (consola, balão, entrada, vsock) está na versão, mas desligado por omissão, e não correu num servidor real.
Comentários
Regras dos comentários