Temos alguns sistemas de construção em produção com os quais ninguém se importa e essas máquinas executam versões antigas do GCC como GCC 3 ou GCC 2.
E não consigo convencer a gerência a atualizá-la para uma versão mais recente: eles dizem: "se não está quebrado, não conserte".
Como mantemos uma base de código muito antiga (escrita nos anos 80), esse código C89 compila muito bem nesses compiladores.
Mas não tenho certeza se é uma boa ideia usar essas coisas antigas.
Minha pergunta é:
O uso de um compilador C antigo pode comprometer a segurança do programa compilado?
ATUALIZAR:
O mesmo código foi criado pelo Visual Studio 2008 para destinos do Windows, e o MSVC ainda não suporta C99 ou C11 (não sei se o MSVC mais recente suporta), e posso construí-lo na minha caixa Linux usando o GCC mais recente. Portanto, se introduzirmos um GCC mais recente, provavelmente criaremos tão bem quanto antes.
-O3 -Wall -Wextra -fsanitize=undefined
gcc e pelo clang moderno deve ajudar.Respostas:
Na verdade, eu argumentaria o contrário.
Existem vários casos em que o comportamento é indefinido pelo padrão C, mas é óbvio o que aconteceria com um "compilador burro" em uma determinada plataforma. Casos como permitir que um número inteiro assinado transborde ou acessar a mesma memória através de variáveis de dois tipos diferentes.
Versões recentes do gcc (e clang) começaram a tratar esses casos como oportunidades de otimização, não importando se eles mudam o comportamento do binário na condição "comportamento indefinido". Isso é muito ruim se sua base de código foi escrita por pessoas que trataram C como um "montador portátil". Com o passar do tempo, os otimizadores começaram a examinar blocos cada vez maiores de código ao fazer essas otimizações, aumentando a chance de o binário acabar fazendo algo diferente de "o que um binário construído por um compilador burro" faria.
Existem opções de compilador para restaurar o comportamento "tradicional" (-fwrapv e -fno-strict-aliasing para os dois que eu mencionei acima), mas primeiro você precisa saber sobre eles.
Embora, em princípio, um bug do compilador possa transformar um código compatível em uma falha de segurança, eu consideraria o risco disso insignificante no grande esquema das coisas.
fonte
people who treated C like a "portable assembler"
não é o que C é?Existem riscos nos dois cursos de ação.
Compiladores mais antigos têm a vantagem da maturidade, e o que quer que tenha sido quebrado neles provavelmente (mas não há garantia) foi resolvido com êxito.
Nesse caso, um novo compilador é uma fonte potencial de novos bugs.
Por outro lado, os compiladores mais novos vêm com ferramentas adicionais :
A instrumentação do seu binário com os desinfetantes (desinfetante de endereço, desinfetante de memória ou desinfetante de comportamento indefinido) e depois difundi-lo (usando o American Fuzzy Lop, por exemplo) descobriu vulnerabilidades em vários softwares de alto nível, consulte, por exemplo, este artigo do LWN.net .
Essas novas ferramentas e todas as ferramentas futuras estão inacessíveis para você, a menos que você atualize seu compilador.
Ao permanecer em um compilador com pouca potência, você está colocando a cabeça na areia e cruzando os dedos para que nenhuma vulnerabilidade seja encontrada. Se o seu produto for um alvo de alto valor, peço que você reconsidere.
Nota: mesmo se você NÃO atualizar o compilador de produção, convém usar um novo compilador para verificar a vulnerabilidade; esteja ciente de que, como esses são compiladores diferentes, as garantias são diminuídas.
fonte
Seu código compilado contém bugs que podem ser explorados. Os bugs vêm de três fontes: bugs no seu código-fonte, bugs no compilador e nas bibliotecas e comportamento indefinido no código-fonte que o compilador se transforma em um bug. (O comportamento indefinido é um erro, mas ainda não um erro no código compilado. Como exemplo, i = i ++; em C ou C ++ é um erro, mas em seu código compilado ele pode aumentar i em 1 e ficar OK ou definir i para algum lixo e ser um bug).
A taxa de erros no seu código compilado é presumivelmente baixa devido a testes e à correção de erros devido a relatórios de erros do cliente. Portanto, pode ter havido um grande número de bugs inicialmente, mas isso diminuiu.
Se você atualizar para um compilador mais recente, poderá perder erros introduzidos por erros do compilador. Mas todos esses erros seriam erros que, a seu conhecimento, ninguém encontrou e ninguém explorou. Mas o novo compilador pode ter bugs por conta própria e, o mais importante é que os compiladores mais novos tendem a transformar comportamentos indefinidos em bugs no código compilado.
Então você terá muitos novos bugs no seu código compilado; todos os bugs que os hackers poderiam encontrar e explorar. E, a menos que você faça muitos testes e deixe seu código com os clientes para encontrar bugs por um longo tempo, será menos seguro.
fonte
getaddrinfo()
: access.redhat.com/articles/2161461 . Na verdade, esse exemplo não é uma falha de segurança do compilador, mas há mais de 10 anos provavelmente haverá algumas falhas fixas conhecidas.Se não estiver quebrado, não conserte
Seu chefe parece certo ao dizer isso, no entanto, o fator mais importante é a proteção de entradas, saídas e estouros de buffer. A falta deles é invariavelmente o elo mais fraco da cadeia desse ponto de vista, independentemente do compilador usado.
No entanto, se a base de código for antiga e foram criados trabalhos para mitigar os pontos fracos do K&R C usado, como falta de segurança de tipo, recursos inseguros, etc., avalie a questão " Atualizando o compilador para um C99 mais moderno / Padrões C11 quebram tudo? "
Desde que haja um caminho claro para migrar para os padrões C mais recentes, o que pode induzir efeitos colaterais, pode ser melhor tentar uma bifurcação da antiga base de código, avaliá-la e colocar verificações de tipo extras, verificações de sanidade e determinar se está atualizando para o compilador mais recente tem algum efeito nos conjuntos de dados de entrada / saída.
Em seguida, você pode mostrá-lo ao seu chefe: " Aqui está a base de código atualizada, refatorada, mais alinhada com os padrões C99 / C11 aceitos pelo setor ... ".
Essa é a aposta que teria que ser ponderada com muito cuidado , a resistência à mudança pode aparecer lá naquele ambiente e pode se recusar a tocar nas coisas mais recentes.
EDITAR
Apenas relaxei por alguns minutos, percebi isso, o código gerado pela K&R poderia estar sendo executado em uma plataforma de 16 bits, é provável que a atualização para um compilador mais moderno pudesse realmente quebrar a base de código, estou pensando em termos de arquitetura, o código de 32 bits seria gerado , isso pode ter efeitos colaterais engraçados nas estruturas usadas para conjuntos de dados de entrada / saída, que é outro grande fator a ser ponderado com cuidado.
Além disso, como o OP mencionou o uso do Visual Studio 2008 para criar a base de código, o uso do gcc poderia induzir a introdução no ambiente do MinGW ou Cygwin, que poderia causar uma mudança de impacto no ambiente, a menos que o alvo seja o Linux, seria vale a pena tentar, pode ser necessário incluir opções adicionais no compilador para minimizar o ruído na antiga base de códigos K&R; a outra coisa importante é realizar muitos testes para garantir que nenhuma funcionalidade seja quebrada; pode ser um exercício doloroso.
fonte
Claro que sim, se o compilador antigo contiver erros conhecidos que você sabe que afetariam seu programa.
A questão é, não é? Para ter certeza, você precisaria ler todo o registro de alterações da sua versão até a data atual e verificar todos os erros corrigidos ao longo dos anos.
Se você não encontrar evidências de bugs do compilador que afetariam seu programa, a atualização do GCC apenas por isso parece um pouco paranóica. Você deve ter em mente que as versões mais recentes podem conter novos bugs, que ainda não foram descobertos. Muitas mudanças foram feitas recentemente com o suporte do GCC 5 e C11.
Dito isto, o código escrito nos anos 80 provavelmente já está cheio de brechas de segurança e confiança em comportamentos mal definidos, independentemente do compilador. Estamos falando de C pré-padrão aqui.
fonte
Há um risco de segurança em que um desenvolvedor mal-intencionado pode invadir a porta dos fundos por meio de um bug do compilador. Dependendo da quantidade de bugs conhecidos no compilador em uso, o backdoor pode parecer mais ou menos discreto (em qualquer caso, o ponto é que o código está correto, mesmo que complicado, no nível de origem. um compilador sem buggy não encontrará a porta dos fundos, porque a porta dos fundos não existe nessas condições). Para obter pontos extras de negação, o desenvolvedor mal-intencionado também pode procurar por erros de compilador anteriormente desconhecidos por conta própria. Novamente, a qualidade da camuflagem dependerá da escolha dos erros do compilador encontrados.
Este ataque é ilustrado no programa sudo neste artigo . O bcrypt escreveu um ótimo acompanhamento para os minificadores de Javascript .
Além desta preocupação, a evolução dos compiladores C tem sido a de explorar o comportamento indefinido mais e mais e mais agressivamente, assim que o código antigo C que foi escrito em boa fé na verdade seria mais seguro compilado com um compilador C do tempo, ou compilado pelo -O0 (mas algumas novas otimizações de exploração de UBs que quebram programas são introduzidas em novas versões de compiladores, mesmo em -O0 ).
fonte
Compiladores mais antigos podem não ter proteção contra ataques de hackers conhecidos. A proteção contra quebra de pilha, por exemplo, não foi introduzida até o GCC 4.1 . Então, sim, o código compilado com compiladores mais antigos pode ser vulnerável de maneiras que os compiladores mais novos protegem.
fonte
Outro aspecto para se preocupar é o desenvolvimento de um novo código .
Compiladores mais antigos podem ter um comportamento diferente para alguns recursos de linguagem do que é padronizado e esperado pelo programador. Essa incompatibilidade pode atrasar o desenvolvimento e introduzir erros sutis que podem ser explorados.
Compiladores mais antigos oferecem menos recursos (incluindo recursos de idioma!) E também não otimizam. Os programadores irão solucionar essas deficiências - por exemplo, reimplementando os recursos ausentes ou escrevendo códigos inteligentes que são obscuros, mas que correm mais rapidamente - criando novas oportunidades para a criação de bugs sutis.
fonte
Não
O motivo é simples: o compilador antigo pode ter bugs e explorações antigos, mas o novo compilador terá novos bugs e explorações.
Você não está "corrigindo" nenhum bug atualizando para um novo compilador. Você está trocando bugs e explorações antigas por novos bugs e explorações.
fonte
Bem, há uma probabilidade maior de que todos os erros no compilador antigo sejam bem conhecidos e documentados, em vez de usar um novo compilador, para que ações possam ser tomadas para evitar esses erros, codificando-os. Portanto, de uma maneira que não é suficiente como argumento para a atualização. Temos as mesmas discussões em que trabalho, usamos o GCC 4.6.1 em uma base de código para software embarcado e há uma grande relutância (entre os gerentes) em atualizar para o compilador mais recente devido ao medo de novos erros não documentados.
fonte
Sua pergunta se divide em duas partes:
Talvez você possa responder encontrando uma falha explorável em sua base de código existente e mostrando que um compilador mais recente a teria detectado. É claro que seu gerenciamento pode dizer "você achou isso com o compilador antigo", mas pode ressaltar que isso custa um esforço considerável. Ou você pode executá-lo no novo compilador para encontrar a vulnerabilidade e depois explorá-la, se você puder / puder compilar o código com o novo compilador. Você pode precisar de ajuda de um hacker amigável, mas isso depende de confiar neles e poder mostrar a eles o código (e usar o novo compilador).
Mas se o seu sistema não estiver exposto a hackers, talvez você esteja mais interessado em saber se uma atualização do compilador aumentaria sua eficácia: a Análise de código do MSVS 2013 geralmente encontra erros em potencial muito mais cedo que o MSVS 2010 e suporta mais ou menos o C99 / C11 - não tenho certeza se isso acontece oficialmente, mas as declarações podem seguir as instruções e você pode declarar variáveis em
for
-loops.fonte