Leitura e escrita fora dos limites em `qsort()` da glibc

Uma leitura/escrita fora dos limites recentemente identificada em `qsort()` da glibc surge apenas quando programas lhe passam uma função de comparação “ruim”, como uma que transborda ao subtrair inteiros ou viola regras de ordenação com floats e NaNs. Comentadores debatem se isso é realmente uma vulnerabilidade de segurança na glibc ou apenas comportamento indefinido causado por código incorreto do chamador, e se merece um CVE dada a raridade e a dificuldade de exploração. A discussão se amplia para uma comparação entre culturas de segurança em C/C++ e Rust, além de outras linguagens, argumentando sobre quanta responsabilidade bibliotecas devem assumir para se defender contra uso incorreto versus preservar desempenho e simplicidade.

Natureza do bug do qsort

  • O bug é uma leitura/escrita fora dos limites no qsort da glibc quando o chamador fornece uma função de comparação não transitiva (com defeito).
  • Muitos veem isso como “erro do usuário” (parâmetro inválido), análogo a chamar memcpy com ponteiros ruins, mas ainda assim concordam que corrigir verificações de limites é uma boa prática defensiva.
  • Outros enfatizam que a implementação “óbvia” do comparador (a - b) está sutilmente errada devido a overflow de inteiros, então isso não é puramente um caso extremo exótico.

“Segurando errado” vs vulnerabilidade de segurança

  • Uma corrente argumenta que isso é principalmente um bug no código da aplicação, já que os padrões exigem explicitamente uma ordenação total; o comportamento indefinido é responsabilidade do chamador.
  • Outra corrente diz que, se um uso comum pode causar corrupção de memória, isso é um problema legítimo de segurança e merece um CVE também para a implementação da biblioteca.
  • Há críticas à postura de “é UB então vale tudo” da cultura de C/C++, em contraste com normas mais defensivas em outros lugares.

Comparadores, NaNs e ponto flutuante

  • Ordenar floats com NaNs ou infinitos mal tratados pode violar os requisitos do comparador, causando travamentos ou comportamento incorreto.
  • Exemplos mostram como NaNs quebram reflexividade e transitividade, confundindo algoritmos de ordenação. Alguns sugerem que tentar ordenar NaNs já costuma ser um bug de lógica.

Culturas de segurança de linguagem e overflow de inteiros

  • Rust é citado como tratando casos de “você está segurando errado” como questões de segurança: comparadores ruins podem dar resultados errados, mas não UB; traits como Ord são seguras, com “unsafe traits” separados para invariantes que podem causar UB se violados.
  • Discussão sobre tratamento de overflow:
    • operações verificadas/saturadas/por wrap de Rust e comportamento debug vs release.
    • overflow com sinal em C/C++ como UB; wraparound sem sinal muitas vezes não é o que programadores pretendem.
    • Ada, Pascal e WUFFS como exemplos de linguagens ou DSLs que codificam intervalos/refinamentos para mais segurança.

Desempenho e alternativas ao qsort

  • qsort é descrito como relativamente lento; para código de alto desempenho, pessoas recomendam std::sort de C++ ou bibliotecas especializadas, às vezes via um wrapper fino em C++ chamável de C.
  • Alguns se preocupam com o custo de desempenho de verificações extras de segurança; outros argumentam que o custo geralmente é negligenciável.

Explorabilidade, CVEs e pontuação

  • A explorabilidade é debatida: exige um comparador já defeituoso e muitas vezes um contexto privilegiado (por exemplo, setuid-root); nenhum exploit em ambiente real é conhecido na discussão.
  • Há opiniões divididas sobre emitir ou não um CVE:
    • A favor: incentiva usuários a auditar comparadores e atualizar.
    • Contra: CVEs são custosos para ambientes regulados e devem ser reservados para problemas claramente exploráveis.
  • Frustração mais ampla com as pontuações CVSS: alguns bugs graves e facilmente exploráveis recebem nota baixa, enquanto bugs impraticáveis do tipo DoS recebem pontuações altas, tornando a priorização difícil.