Pergunta

o que está acontecendo é que eu estou lendo os pacotes de criptografia, e eu encontrar um pacote corrompido que dá de volta um grande número aleatório para o comprimento.

size_t nLengthRemaining = packet.nLength - (packet.m_pSource->GetPosition() - packet.nDataOffset);

seckey.SecretValues.m_data.resize(nLengthRemaining);

Neste m_Data código é um std::vector<unsigned char>. nLengthRemaining é muito grande devido a um pacote de dados corrompido, portanto, a função de redimensionamento joga. O problema não é que redimensionamento lança (lidamos com as exceções), mas que redimensionamento tem memória corrompida já e isso leva a mais exceções posteriores.

O que eu quero fazer é saber se o comprimento é muito grande antes que eu chame de redimensionamento, em seguida, redimensionar única chamada, se é ok. Eu tentei colocar esse código antes da chamada para redimensionar:

std::vector<unsigned char>::size_type nMaxSize = seckey.SecretValues.m_data.max_size();
if(seckey.SecretValues.m_data.size() + nLengthRemaining >=  nMaxSize) {
    throw IHPGP::PgpException("corrupted packet: length too big.");
}
seckey.SecretValues.m_data.resize(nLengthRemaining);

Este código está usando a função de membro max_size std :: vector para testar se o nLengthRemaining é maior. Isso não deve ser embora confiável, porque nLengthRemaining ainda menos de nMaxSize é, mas aparentemente ainda grande o suficiente para causar redimensionamento ter um problema (nMaxSize foi 4xxxxxxxxx e nLengthRemaining é 3xxxxxxxxx).

Além disso, eu não determinar o redimensionamento exceção está jogando. Não é um std :: length_error e não é um std :: bad_alloc. O exceção é jogar realmente não é muito importante para mim, mas estou curioso para saber.

btw, só assim você sabe, este código funciona corretamente em casos normais. Este caso de um pacote de dados corrompido é o único lugar onde ele fica louco. Por favor ajude! obrigado.

UPDATE:

@ Michael. Por agora eu vou simplesmente ignorar o pacote se é maior do que 5 MB. Vou discutir com outros membros da equipe sobre a possibilidade de validar os pacotes (ele já pode estar lá e eu apenas não sabem disso). Eu estou começando a pensar que realmente é um bug na nossa versão do STL, a exceção lança nem sequer é um std :: exceção, que me surpreenderam. Vou tentar descobrir a partir de meu supervisor qual a versão do STL estamos correndo demais (como eu verificar?).

Outra atualização: Eu só provar que é um bug na versão STL eu estou usando em minha máquina de desenvolvimento do Visual Studio 6. Eu escrevi este aplicativo de exemplo:

// VectorMaxSize.cpp: Define o ponto de entrada para o aplicativo console. //

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <math.h>
#include <typeinfo>

typedef std::vector<unsigned char> vector_unsigned_char;

void fill(vector_unsigned_char& v) {
    for (int i=0; i<100; i++) v.push_back(i);
}


void oput(vector_unsigned_char& v) {
    std::cout << "size: " << v.size() << std::endl;
    std::cout << "capacity: " << v.capacity() << std::endl;
    std::cout << "max_size: " << v.max_size() << std::endl << std::endl;
}

void main(int argc, char* argv[]) {
    {
        vector_unsigned_char v;

        fill(v);

        try{
            v.resize(static_cast<size_t>(3555555555));
        }catch(std::bad_alloc&) {
            std::cout << "caught bad alloc exception" << std::endl;
        }catch(const std::exception& x) {
            std::cerr << typeid(x).name() << std::endl;
        }catch(...) {
            std::cerr << "unknown exception" << std::endl;
        }

        oput(v);    
        v.reserve(500);
        oput(v);
        v.resize(500);
        oput(v);
    }

    std::cout << "done" << std::endl;
}

Na minha máquina dev VS6 tem o mesmo comportamento tem o projeto de criptografia, ele faz com que todos os tipos de estragos. Quando eu construir e executá-lo no meu Visual Studio 2008 máquina, redimensionamento lançará uma exceção std :: bad_alloc eo vetor não será corrompido, assim como teríamos esperado! Tempo para algum futebol EA Esporte NCAA hehe!

Foi útil?

Solução

Eu acho que vector::max_size() é praticamente sempre uma coisa 'codificado' - é independente da quantidade de memória do sistema / biblioteca é preparado para alocar dinamicamente. Seu problema parece ser um bug na implementação vector que corrompe as coisas quando uma alocação falhar.

'Bug' pode ser muito forte de uma palavra. vector::resize() é definido em termos de vector::insert() eo padrão diz o seguinte sobre vector::insert():

Se uma exceção é lançada de forma diferente da construtor de cópia ou operador de atribuição de T não existem efeitos

Assim parece que pode haver momentos em que a operação resize() é permitido vector um corrupto, mas ainda seria bom se a operação estavam a salvo exceção (e eu acho que não seria fora de linha para esperar a biblioteca para fazer isso, mas talvez seja mais difícil do que eu imagino).

Você parece ter algumas opções razoáveis:

  • mudança ou atualização para uma biblioteca que não tem o bug corrupção (o compilador / versão da biblioteca você está usando?)
  • em vez de verificar contra o conjunto vector::max_size() nMaxSize para o seu próprio máximo razoável e fazer o que você tem acima, mas utilizando esse limiar em seu lugar.

Editar:

Eu vejo que você está usando VC6 - há definitivamente um bug no vector::resize() que pode ter algo a ver com seu problema, embora olhando para o patch Eu honestamente não vejo como (na verdade, é um bug no vector::insert(), mas como mencionado, resize() chama insert()). Eu acho que valeria a pena visitar a página Dinkumwares' para correções de bugs para VC6 e aplicar as correções.

O problema também pode ter algo a ver com o patch <xmemory> nessa página - não está claro qual é o erro que do discutidos lá, mas vector::insert() faz chamada _Destroy() e vector<> faz definir o _Ty nome assim que você pode estar executando em que problema. Uma coisa agradável - você não terá que se preocupar em gerenciar as alterações para os cabeçalhos, como a Microsoft nunca é tocá-los novamente. Apenas certifique-se as manchas torná-lo no controle de versão e se documentado.

Note que Scott Meyers em "STL eficaz" sugere o uso da SGI ou de STLport biblioteca para obter um melhor suporte STL que vem com VC6. Eu não fiz isso, então eu não tenho certeza o quão bem as bibliotecas de trabalho (mas eu também não usei VC6 com STL muito). Claro, se você tem a opção de mudar para uma versão mais recente do VC, por todos os meios fazê-lo.


Mais uma edição:

Obrigado pelo programa de teste ...

implementação _Allocate() de VC6 para o alocador padrão (em <xmemory>) usa um int assinado para especificar o número de elementos para alocar, e se o tamanho passado é negativo (que aparentemente é o que está fazendo - certamente no programa de teste você é) a função forças _Allocate() o tamanho de alocação solicitada a zero e prossegue. Note-se que um pedido de atribuição de tamanho zero vai quase sempre ter sucesso (não que os controlos vector para um qualquer maneira falha), então a função vector::resize() tenta alegremente para mover seu conteúdo para o novo bloco, que não é muito grande o suficiente para dizer o mínimo . Assim, a pilha fica corrompido, ele provavelmente vai bater uma página de memória inválido, e independentemente -. Seu programa é metralhado

Assim, a linha inferior é não sempre perguntar VC6 para alocar mais do que objetos INT_MAX de uma só vez. Provavelmente não é uma grande idéia na maioria das circunstâncias (VC6 ou não).

Além disso, você deve ter em mente que VC6 usa uma linguagem pré-padrão de retornar 0 de new quando uma alocação falhar rao invés de jogar código gêneros codice tag.

Outras dicas

Gostaria de sugerir que você verifique seus dados para corrupções antes de chamar funções de biblioteca com argumentos talvez erradas!

Use algum tipo de código de hash ou cheque algoritmo de soma em seus pacotes. Você não pode contar com a biblioteca para ajudá-lo, uma vez que não é capaz de fazer: Pode ser que você lhe dá uma (do ponto de vista da biblioteca) tamanho corrompido, mas ainda válido que é realmente grande para que ele atribui, por exemplo, 768MB de RAM. Isso pode funcionar se não houver memória livre suficiente no sistema, mas pode falhar se existem outros programas em execução que consomem muita memória em sua máquina 1024MB.

Assim como disse acima: Verifique primeiro

!

Eu não tenho idéia o que você quer dizer quando diz "redimensionamento tem memória corrompida". Como você determina isso?

FWIW, eu não concordo com de Michael resposta . Se std::vector<>::resize() joga na expansão vector, vejo duas possibilidades:

  1. De qualquer um dos construtores usado para preencher o novo espaço (ou copiar os elementos) jogou ou
  2. o alocador usado para crescer o vetor fez
  3. ou o vector determinado de antemão que o tamanho solicitado é demais e joga.

Com std::vector<unsigned char> podemos descartar com segurança # 1, de modo que as folhas # 2. Se você não usar qualquer alocador especial, então std::allocator deve ser usado e, AFAIK, que irá chamar new para alocar memória. E new jogaria std::bad_alloc. No entanto, você diz que não pode pegar isso, então eu não sei o que acontece.

Seja o que for, deve ser derivado de std::exception, assim que você poderia fazer isso para descobrir:

try {
  my_vec.resize( static_cast<std::size_t>(-1) );
} catch(const std::exception& x) {
  std::cerr << typeid(x).name() << '\n';
}

O que é o resultado disso?

De qualquer forma, qualquer que seja, estou bastante certeza de que ele não deve corromper a memória. Ou isso é um bug na implementação do lib std (improvável, se você me perguntar, a menos que você use muito antiga), ou que você tenha feito algo errado em outro lugar.


Editar agora que você disse que você está usando VS6 ...

Você deveria ter dito isso antes. VC6 foi lançado mais de uma década atrás, depois de MS haviam perdido a votação na comissão std porque não tinha aparecido para as reuniões por muito tempo. A implementação lib std eles enviado era de Dinkumware (bom), mas devido a problemas legais que era a pessoa certa para VC5 (muito ruim), que teve muitos bugs menores e maiores e nem sequer tem suporte para modelos de membros, embora o compilador VC6 apoiaram. Honestamente, o que você espera de tal produto de um velho?

Se você não pode alternar para uma versão decente VC (eu recomendo pelo menos VC7.1 aka VS.NET 2003, como este foi o que fez o grande salto em direção a conformidade padrão), pelo menos ver se Dinkumware ainda vender uma versão VC6t de sua excelente biblioteca. (Na verdade, eu ficaria surpreso, mas eles costumavam ter um e nunca se sabe ...)

Quanto às exceções: Na versão anterior do VC (isso inclui VC6 e não inclui VC8 aka VS.NET de 2005, eu não tenho certeza sobre VC7.1, embora) por violações de acesso padrão podem ser capturados por catch(...). Então, se tal um bloco catch pego alguma coisa, você não saberia se isso ainda era uma exceção de C ++. Meu conselho seria a única catch(...) uso em confunction com throw;, a fim de deixar passar essa exceção no. Se você fizer isso, você obter um verdadeiro acidente em AV de e são capazes de empilhar-trace-los no depurador. Se não o fizer, de AV vai ser engolido e, em seguida, você está preso com um aplicativo que é berserk ido mesmo sem você saber. Mas fazer qualquer coisa, mas abortar com uma aplicação AV'ed não faz sentido. Um AV é um resultado de comportamento indefinido e depois disso, todas as apostas estão fora.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top