galegoga
Membros-
Total de ítens
38 -
Registro em
-
Última visita
Últimos Visitantes
O bloco dos últimos visitantes está desativado e não está sendo visualizado por outros usuários.
galegoga's Achievements
-
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
@Arimateia Jr Blz, Vou dar um olhada nesses links. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa tarde, Arimateia! Fiz o teste e funcionou muito bem. Você conseguiu através do link em que postei ? Pergunto porque esse método "OPENSSL_sk_pop_free" não está informado, lá falava sobre "sk_X509_pop_free". Ele são similares ou equivalentes ? (Levando em conta que o artigo no fórum já tem 22 anos e muita coisa pode ter mudado). -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Tudo bem. Irei aguardar. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa tarde, Daniel. Você chegou a ver o post acima ? No dia em que postei tive um problema e acabei quebrando ele em dois. Tentei editar para ficar em um só, mas o tempo de edição havia expirado. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Continuação do post acima // Set Certificate Verification chain if Result and (ca <> nil) then SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); // <---- AQUI Adicionei esse código e fiz os testes e vi que parou de vazar memória. meu código de teste foi (loop de 1000): procedure TForm2.MetodoTeste(ALoop: Integer); var vDFeSSL: TDFeSSL; vCT: Integer; vSenha: String; vFileName: String; begin vFileName := edtPfxFileName.Text; vSenha := edtSenhaPFX.Text; for vCT := 1 to ALoop do begin vDFeSSL := TDFeSSL.Create; try vDFeSSL.SSLCryptLib := cryOpenSSL; vDFeSSL.SSLType := LT_TLSv1_2; vDFeSSL.DadosPFX := LoadCertificado(vFileName); vDFeSSL.Senha := AnsiString(vSenha); vDFeSSL.CarregarCertificado; finally vDFeSSL.Free; end; end; end; Realmente os sistema com ReportMemoryLeaksOnShutdown := True; não acusa nada. Mas o memory ocorre. Aproveitando essa discussão. Acho que você não acompanhou a discussão desde o início. Digo isso porque quando tive o problema cheguei a achar algo parecido com o meu problema. chequei a mencionar no fórum antes de propor essa modificação no código. Um certo usuário mencionava despejo de memória relacionado a variável "Ca". A solução veio do Dr. Stephen Henson, que informava que ele devia chamar sk_X509_pop_free(Ca, X509_free) ao final. Achei que fosse isso. No acbr também possui ela com o nome SkX509PopFree, mas ela nunca é executada por que me parece que ela não existe na dll. _SkX509PopFree dentro de ssl_openssl_lib.pas sempre está nula. A solução é antiga, acho que no inicio dos anos 2000. O link é : https://openssl-users.openssl.narkive.com/F1OAAID0/pkcs12-memory-leak#post2 Vou adicionar o código fonte de teste. Se precisar do certificado digital é só me falar. PrjReadPfx02.zip -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa tarde, Daniel! Fiz o teste e continua apresentando o problema. Primeiramente quero dizer que meu conhecimento referente as bibliotecas OpenSSL é zero. Também pouco sei sobre certificado digital. Nesses aspectos sou usuário final, dessa forma eu não tenho a capacidade de discutir os detalhes internos da biblioteca. Mas vou te passar os passos que levaram até esse ponto. Desenvolvi uma aplicação para consultar a Distribuição, é um serviço que roda 24 horas, com mais de 1000 empresas, mas acho que certificados são uns 500, visto que tem muitas filiais. No decorrer do tempo a aplicação ia crescendo até ficar sem memória. Fiz um rastreio, fui isolando as partes até que descobri que o despejo era quando acessava a certificado. Um detalhe importante é que o problema ocorre somente com alguns certificados. Depois que descobri em qual unit (ACBrDFeOpenSSL.pas) estava o problema eu comecei estudar o que estava sendo criado e que ficava para trás. Nesse processo descobrir a unit ssl_openssl.pas na qual me ajudou, fui comparando o código vendo as diferenças até que cheguei a um certo ponto, onde existia um código na ssl_openssl.pas que não existia na unit do acbr. O código era: -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Arquivo em anexo. ACBrDFeOpenSSL.pas -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa tarde! No meus testes essa modificação não funcionou. Continua vazando memória. A linha 576, código: SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); não está sendo executada de acordo com a condicional estabelecida. Por que ela não pode ser executada somente através da condicional "if Result and (ca <> nil) then" ? porque ela tem que depender da condicional "if LibVersionIsGreaterThan1_0_0 then begin" ser falsa ? Assim não funcionou: // Set Certificate Verification chain if Result and (ca <> nil) then begin if LibVersionIsGreaterThan1_0_0 then begin iTotal := OPENSSL_sk_num(ca); if iTotal > 0 then begin Store := SSL_CTX_get_cert_store(Fctx); for I := 0 to iTotal - 1 do begin certx := OPENSSL_sk_value(ca, I); if certx <> nil then begin if X509_STORE_add_cert(Store, pX509(certx)) = 0 then begin // already exists end; //X509_free(Cert); end; end; end; end else SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); end; Assim funcionou: // Set Certificate Verification chain if Result and (ca <> nil) then begin if LibVersionIsGreaterThan1_0_0 then begin iTotal := OPENSSL_sk_num(ca); if iTotal > 0 then begin Store := SSL_CTX_get_cert_store(Fctx); for I := 0 to iTotal - 1 do begin certx := OPENSSL_sk_value(ca, I); if certx <> nil then begin if X509_STORE_add_cert(Store, pX509(certx)) = 0 then begin // already exists end; //X509_free(Cert); end; end; end; end; SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); end; -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Sim, vou efetuar os testes. Reporto assim que tiver os resultados. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Desculpe, só vi sua mensagem agora. Devo ter recebido o e-mail de notificação do fórum, mas também não vi. Segue em anexo o arquivo atualizado. Modificações somente no arquivo ACBrDFeOpenSSL.pas. ACBrDFeOpenSSL.pas -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Seria bom que colocasse esse ajuste, recentemente tive que atualizar o ACBr e tive que modificar novamente a unit. Não consegui solucionar utilizando a versão que utiliza componentes do Windows. Ele gera diversos arquivos numa pasta do AppData ficando enorme. Usando o OpenSSL consegui resolver o problema que consumia muita memória, em uma hora de uso a aplicação consumia 500MB só de leitura de certificado e continuava a crescer até dar crash no aplicativo. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa noite! Segue o arquivo com a sugestão de correção para evitar memoryLeak na leitura de alguns certificados. ACBrDFeOpenSSL.pas -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Boa tarde! Acho que consegui algo. Consegui reproduzir o teste do inicio do post sem que ocorra vazamento de memória na leitura do certificado. Mas antes de prosseguir volto ao post anterior, onde é mencionado uma solução com o método sk_X509_pop_free, ele até existe no arquivo ssl_openssl.pas, mas é inócuo, já que o método não existe na DLL. "_SkX509PopFree := GetProcAddr(SSLUtilHandle, 'SK_X509_POP_FREE');" sempre retorna nulo. A solução que encontrei foi uma comparação dos arquivos ssl_openssl.pas e ACBrDFeOpenSSL.pas. Comparei os métodos TSSLOpenSSL.LoadPFX e TDFeOpenSSL.LerPFXInfo e peguei trechos de código de um e adicionei ao outro. Código comparados: Código da classe: ssl_openssl.TSSLOpenSSL function TSSLOpenSSL.LoadPFX(pfxdata: Ansistring): Boolean; var cert, pkey, ca: SslPtr; b: PBIO; p12: SslPtr; begin Result := False; b := BioNew(BioSMem); try BioWrite(b, pfxdata, Length(PfxData)); p12 := d2iPKCS12bio(b, nil); if not Assigned(p12) then Exit; try cert := nil; pkey := nil; ca := nil; try {pf} if PKCS12parse(p12, FKeyPassword, pkey, cert, ca) > 0 then if SSLCTXusecertificate(Fctx, cert) > 0 then if SSLCTXusePrivateKey(Fctx, pkey) > 0 then Result := True; // Set Certificate Verification chain if Result and (ca <> nil) then SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); {pf} finally EvpPkeyFree(pkey); X509free(cert); SkX509PopFree(ca,_X509Free); // for ca=nil a new STACK was allocated... end; {/pf} finally PKCS12free(p12); end; finally BioFreeAll(b); end; end; Código da classe ACBrDFeOpenSSL.TDFeOpenSSL: function TDFeOpenSSL.LerPFXInfo(const PFXData: Ansistring): Boolean; var ca, p12: Pointer; b: PBIO; begin Result := False; DestroyKey; b := BioNew(BioSMem); try BioWrite(b, PFXData, Length(PFXData)); p12 := d2iPKCS12bio(b, nil); if not Assigned(p12) then Exit; try DestroyCert; DestroyKey; ca := nil; if PKCS12parse(p12, FpDFeSSL.Senha, FPrivKey, FCert, ca) > 0 then begin if (FCert <> nil) then begin GetCertInfo( FCert ); Result := True; end; end; finally PKCS12free(p12); end; finally BioFreeAll(b); end; end; Basicamente o que encontrei diferente foi o código, que passei para ACBrDFeOpenSSL : if SSLCTXusecertificate(Fctx, Fcert) > 0 then if SSLCTXusePrivateKey(Fctx, FPrivKey) > 0 then Result := True; // Set Certificate Verification chain if Result and (ca <> nil) then SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); A modificação Final ficou assim: TDFeOpenSSL = class(TDFeSSLCryptClass) private .... Fctx: PSSL_CTX; // Adicionado proteced ... procedure FreeContext; // Adicionado end; constructor TDFeOpenSSL.Create(ADFeSSL: TDFeSSL); begin inherited Create(ADFeSSL); ... Fctx := nil; // Adicionado end; destructor TDFeOpenSSL.Destroy; begin ... FreeContext; // Adicionado inherited Destroy; end; procedure TDFeOpenSSL.FreeContext; // Adicionado begin if assigned (Fctx) then begin SslCtxFree(Fctx); Fctx := nil; ErrRemoveState(0); end; end; function TDFeOpenSSL.LerPFXInfo(const PFXData: Ansistring): Boolean; procedure SetContex; // Adicionado begin FreeContext; Fctx := nil; case FpDFeSSL.SSLType of LT_SSLv2: Fctx := SslCtxNew(SslMethodV2); LT_SSLv3: Fctx := SslCtxNew(SslMethodV3); LT_TLSv1: Fctx := SslCtxNew(SslMethodTLSV1); LT_TLSv1_1: Fctx := SslCtxNew(SslMethodTLSV1_1); LT_TLSv1_2: Fctx := SslCtxNew(SslMethodTLSV1_2); LT_TLSv1_3: Fctx := SslCtxNew(SslMethodTLSV1_3); LT_all: begin //try new call for OpenSSL 1.1.0 first Fctx := SslCtxNew(SslTLSMethod); if Fctx=nil then //callback to previous versions Fctx := SslCtxNew(SslMethodV23); end; else Exit; end; end; var ca, p12: Pointer; b: PBIO; begin Result := False; DestroyKey; SetContex; // Adicionado b := BioNew(BioSMem); try BioWrite(b, PFXData, Length(PFXData)); p12 := d2iPKCS12bio(b, nil); if not Assigned(p12) then Exit; try DestroyCert; DestroyKey; ca := nil; if PKCS12parse(p12, FpDFeSSL.Senha, FPrivKey, FCert, ca) > 0 then begin // Adicionado Inicio ================================================= if SSLCTXusecertificate(Fctx, Fcert) > 0 then if SSLCTXusePrivateKey(Fctx, FPrivKey) > 0 then Result := True; // Set Certificate Verification chain if Result and (ca <> nil) then SslCtxCtrl(Fctx, SSL_CTRL_CHAIN, 0, ca); // Adicionado Fim ==================================================== if (FCert <> nil) then begin GetCertInfo( FCert ); Result := True; end; end; finally PKCS12free(p12); end; finally BioFreeAll(b); end; end; Depois dessa modificação o memoryleak parou de acontecer. Não sei o que esses métodos fazem, mas me parece que ele cria um vinculação da Variável "ca" com Fctx e quando o método FreeContex finaliza Fctx ele também deve estar eliminando os objetos apontados por "ca". Li na documentação de PKCS12parse que quando é passado "ca" como null ele cria um objeto internamente. Talvez seja isso. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Acho que encontrei alguma coisa. O problema está na chamada "PKCS12parse" dentro do método TDFeOpenSSL.LerPFXInfo. Efetuei uma pesquisa e encontrei um link que menciona memoryleak nesse método, não exatamente no método, mas a falta de execução de um método para Limpar o conteúdo. A solução é data por Stephen Henson, veja o problema relatado com a solução: in order to try and prove that the memory leak i am seeing in PKCS12_parse() is not specific to my embedded system, i compiled the following using OpenSSL 0.9.6 on solaris void parsetest(BIO *databio) { EVP_PKEY *Pkey=NULL; X509 *Cert=NULL; STACK_OF(X509) *Ca=NULL; PKCS12 *PK12=NULL; PK12 = d2i_PKCS12_bio(databio, NULL); PKCS12_parse(PK12, NULL, &Pkey, &Cert, &Ca); PKCS12_free(PK12); if (Pkey) EVP_PKEY_free(Pkey); if (Cert) X509_free(Cert); if (Ca) sk_X509_free(Ca); The above line is the problem, it just frees up the STACK, not its contents. It should be: sk_X509_pop_free(Ca, X509_free); Steve. Link oficial do problema relatado. Ele menciona que o método sk_X509_pop_free deve ser executado para limpar o conteúdo e não somente o STACK(não sei o que é). Tentei localizar esse método na Unit OpenSSLExt, mas não encontrei. Mas no arquivo ssl_openssl_lib.pas dentro da pasta "ACBr\Fontes\Terceiros\synalist" eu encontrei. Tentei transpo-lo para o arquivo OpenSSLExt para utiliza-lo mas não consegui. Existem algumas diferenças entre os arquivos e me falta conhecimento para código de mais baixo nível, além de não conhecer nada sobre a documentação do OpenSSL. Se alguém puder ajudar eu agradeço. -
Vazamento de memória TDFeSSL
galegoga replied to galegoga's tópico in NFC-e - Nota Fiscal do Consumidor Eletrônica
Esse código é de teste. Criei para ver o comportamento do componente. Na aplicação real recebo ele via REST em Base64. Agora, existe algum problema no componente em carregar o arquivo físico do disco ?