Problem ImportExport z nowym destruktorem Varien_Image_Adapter_Gd2 w 1.9.2.0

23

Czy ktoś może wyjaśnić, do czego służy następujący kod wprowadzony między Magento CE 1.9.1.0 a 1.9.2.0?

class Varien_Image_Adapter_Gd2:

public function __construct()
{
    // Initialize shutdown function
    register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function destruct()
{
    @imagedestroy($this->_imageHandler);
}

Po dodaniu tych dwóch funkcji nasz import obrazów galerii produktów z interfejsem ImportExport przestał działać. Błąd wynika z limitu pamięci (który jest limitem maksymalnej wielkości otwartego pliku).

Mój pomysł jest taki, że pliki otwarte przez import nie zostaną poprawnie zamknięte.

Widziałem także, że wprowadzono pewne puste destruct()funkcje ( Mage_ImportExport_Model_Import_Adapter_Abstract) - ale rozszerzenie tych, aby pasowały do ​​logiki nadrzędnej, nie pomaga.

Achim Rosenhagen
źródło

Odpowiedzi:

14

Wygląda na to, że próbowali zniszczyć zasób obrazu, ale zamiast tego wprowadzili wyciek pamięci. Szczerze mówiąc, nie mogę wymyślić prawidłowego powodu dla tego kodu, ale mogę wyjaśnić, co zostało zmienione:

Pierwotnie imagedestroy()byłby wywoływany w desctructor__destruct()

function __destruct()
{
    @imagedestroy($this->_imageHandler);
}

Destruktor jest wywoływany za każdym razem, gdy moduł śmieciowy PHP niszczy nieużywane obiekty (tj. Obiekty w pamięci, do których już się nie odwołuje).

Teraz imagedestroy()zamiast tego jest wywoływany w funkcji zamykania, a ponieważ jest to wywołanie zwrotne do metody Varien_Image_Adapter_Gd2obiektu, nie można nawet wyrzucać śmieci do samego końca. W ten sposób wszystkie zasoby graficzne pozostają otwarte do momentu zakończenia wykonywania skryptu.

Fabian Schmengler
źródło
Dzięki za wyjaśnienie - o tym myślałem. Ogólnie rzecz biorąc, wprowadzony kod sprawia, że ​​większość importów jest bezużyteczna w wersji 1.9.2. w moich oczach. Mam nadzieję, że zostanie to wkrótce naprawione. Czy masz jakieś porady, gdzie otworzyć raport o błędzie?
Achim Rosenhagen
6

Mam te same problemy z moim Magento 1.9.2.0 ...

Pracuję tylko, zmieniając Varien_Image_Adapter_Gd2 w /lib/Varien/Image/Adapter/Gd2.phpnastępujący sposób:

public function __construct()
{
    // Initialize shutdown function
    // register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function __destruct()
{
    @imagedestroy($this->_imageHandler);
}
  • usuń linię za pomocą register_shutdown_function (lub skomentuj)
  • zmień nazwę funkcji destruct na __destruct

Ustawiłem memory_limit z powrotem na 1G (wcześniej zwiększyłem do 32 GB), a teraz działa ...

Ten projekt implementuje wspomnianą procedurę w sposób przyjazny dla modmana. Po prostu zainstaluj go z kompozytorem i gotowe.

dkr
źródło
To tak naprawdę nie odpowiada na pytanie. Jeśli masz inne pytanie, możesz je zadać, klikając Zadaj pytanie . Możesz także dodać nagrodę za zwrócenie większej uwagi na to pytanie, gdy będziesz mieć wystarczającą reputację .
Rajeev K Tomy
Tak, to nie odpowiada na pytanie, ale pomoże ludziom, którzy potrzebują tymczasowego rozwiązania i braku dyskusji
dkr
naprawiono problem z zużyciem pamięci podczas importu. Ciekawe, czy Magento testuje w jakiś sposób to, co wypuszcza?
klipach
To rozwiązuje nie tylko problem z importem. To rozwiązuje problem pochłaniania dużej ilości pamięci przez proces tworzenia / odtwarzania pamięci podręcznej i wersji o zmienionym rozmiarze dla obrazów każdego produktu. Jeśli prześlę zdjęcia png do moich produktów, bez tego „hackowania” nie mogę pracować i otrzymuję wiele błędów wyczerpania pamięci.
Simbus82,
Dzisiaj znalazłem tę sugestię. Zaimplementowałem to i wyciek pamięci zniknął. Następnie stworzyłem ten github.com/borasocom-team/magento-gd2-memoryleak, aby zainstalować go w czysty sposób.
Dr Gianluigi Zane Zanettini
5

Było to częścią naprawiania problemów związanych z bezpieczeństwem za pomocą unserialize. Magiczne metody, takie jak __destruct, mają nieodłączne problemy z serializacją.

Widzieliśmy proponowane exploity wykorzystujące serializację i __destruct do tworzenia plików w systemie plików - i ta zmiana (zobaczysz więcej podobnych zmian w innych miejscach) została wprowadzona, aby tego uniknąć.

Czy powoduje wyciek pamięci lub po prostu zużywa więcej pamięci, dopóki skrypt się nie skończy?

/security/77549/is-php-unserialize-exploitable-without-any-interesting-methods

Piotr Kamiński
źródło
Dzięki za kontekst. Czy dokonano tej konkretnej zmiany, aby zapobiec konkretnemu exploitowi, czy tylko dla pewności?
Fabian Schmengler
I nie, to prawdopodobnie powoduje, że skrypt zużywa więcej pamięci, a nie prawdziwy wyciek pamięci
Fabian Schmengler
Powoduje to ogromny wyciek pamięci, szczególnie podczas importowania obrazów, ponieważ zachowa wszystkie pliki graficzne otwarte do końca przetwarzania importu. W ten sposób możemy zaimportować tylko około 50 produktów (jeszcze zanim będziemy w stanie wykonać import> 2k). Uruchomiłem test na lokalnej maszynie wirtualnej z 8 GB pamięci RAM, a pliki źródłowe mają rozmiar około 300 KB. Przed zmianą pamięć używana przez PHP ponownie zatrzymuje się na 1k podczas całego importu.
Achim Rosenhagen
fschmengler ma rację - to może nie być „wyciek pamięci”, ale zużycie idzie w górę po wzgórzu ;-)
Achim Rosenhagen
1
@ Alex dziękuję za poradę. Poprawiłem to na odwrót. Teraz wyciek pamięci zniknął, ale nie ma rozwiązania na przyszłość.
Arne,
4

Podniosłem więc błąd w Magento, w tym „rozwiązanie”, które powinno rozwiązać problemy z wykorzystaniem pamięci w procesie importowania obrazu.

Rozwiązanie można znaleźć na github pod https://github.com/sitewards/import_image_memory_leak_fix, ale podstawową ideą jest.

Naprawienie Mage_Catalog_Helper_Image::validateUploadFilewywołania destructmetody na procesorze obrazu. Niestety wygląda na to, że domyślna Varien_Imageopcja nie zajmuje się destructtak, więc musieliśmy dodać własną klasę, która to robi.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image extends Varien_Image
{
    /**
     * Constructor,
     * difference from original constructor - we register a destructor here.
     *
     * @param string $sFileName
     * @param Varien_Image_Adapter $oAdapter Default value is GD2
     */
    public function __construct($sFileName = null, $oAdapter = Varien_Image_Adapter::ADAPTER_GD2)
    {
        parent::__construct($sFileName, $oAdapter);

        // Initialize shutdown function
        register_shutdown_function(array($this, 'destruct'));
    }

    /**
     * Destroy object image on shutdown
     */
    public function destruct()
    {
        $oAdapter = $this->_getAdapter();
        if (method_exists($oAdapter, 'destruct')) {
            $oAdapter->destruct();
        } else {
            Mage::log('Image can not be destructed properly, adapter doesn\'t support the method.');
        }
    }
}

A potem przepisanie pomocnika.

<?xml version="1.0"?>
<config>
    <modules>
        <Sitewards_ImportImageMemoryLeakFix>
            <version>0.1.0</version>
        </Sitewards_ImportImageMemoryLeakFix>
    </modules>
    <global>
        <models>
            <sitewards_importimagememoryleakfix>
                <class>Sitewards_ImportImageMemoryLeakFix_Model</class>
            </sitewards_importimagememoryleakfix>
        </models>
        <helpers>
            <catalog>
                <rewrite>
                    <image>Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image</image>
                </rewrite>
            </catalog>
        </helpers>
    </global>
</config>

Nowa funkcja wywołuje nową możliwą do zniszczenia klasę obrazu.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image extends Mage_Catalog_Helper_Image
{
    /**
     * Check - is this file an image
     *
     * Difference from original method - we destroy the image object here,
     * i.e. we are not wasting memory, without that fix product import with images
     * easily goes over 4Gb on memory with just couple hundreds of products.
     *
     * @param string $sFilePath
     *
     * @return bool
     * @throws Mage_Core_Exception
     */
    public function validateUploadFile($sFilePath) {
        if (!getimagesize($sFilePath)) {
            Mage::throwException($this->__('Disallowed file type.'));
        }

        /** @var Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image $oImageProcessor */
        $oImageProcessor = Mage::getModel('sitewards_importimagememoryleakfix/destructable_image', $sFilePath);
        $sMimeType       = $oImageProcessor->getMimeType();
        $oImageProcessor->destruct();

        return $sMimeType !== null;
    }
}
David Manners
źródło