[php] “zend_mm_heap이 손상되었습니다”란 무엇입니까

갑자기 내가 전에는 본 적이없는 응용 프로그램에 문제가있었습니다. Apache의 오류 로그를 확인하기로 결정한 후 “zend_mm_heap 손상됨”이라는 오류 메시지를 발견했습니다. 이것은 무엇을 의미 하는가.

운영체제 : Fedora Core 8 Apache : 2.2.9 PHP : 5.2.6



답변

많은 시행 착오 끝에 output_bufferingphp.ini 파일 의 값을 늘리면 이 오류가 사라지는 것을 알았 습니다.


답변

구성 옵션을 변경하여 해결할 수있는 문제는 아닙니다.

구성 옵션을 변경하면 때로는 긍정적 인 영향을 미치지 만 상황을 쉽게 악화 시키거나 전혀 아무것도하지 않을 수 있습니다.

오류의 본질은 다음과 같습니다.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    void **mem = malloc(sizeof(char)*3);
    void *ptr;

    /* read past end */
    ptr = (char*) mem[5];

    /* write past end */
    memcpy(mem[5], "whatever", sizeof("whatever"));

    /* free invalid pointer */
    free((void*) mem[3]);

    return 0;
}

위의 코드는 다음과 같이 컴파일 할 수 있습니다.

gcc -g -o corrupt corrupt.c

valgrind로 코드를 실행하면 세분화 오류로 인해 많은 메모리 오류를 볼 수 있습니다.

krakjoe@fiji:/usr/src/php-src$ valgrind ./corrupt
==9749== Memcheck, a memory error detector
==9749== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9749== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==9749== Command: ./corrupt
==9749==
==9749== Invalid read of size 8
==9749==    at 0x4005F7: main (an.c:10)
==9749==  Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749==
==9749== Invalid read of size 8
==9749==    at 0x400607: main (an.c:13)
==9749==  Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749==
==9749== Invalid write of size 2
==9749==    at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749==    by 0x40061B: main (an.c:13)
==9749==  Address 0x50 is not stack'd, malloc'd or (recently) free'd
==9749==
==9749==
==9749== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==9749==  Access not within mapped region at address 0x50
==9749==    at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749==    by 0x40061B: main (an.c:13)
==9749==  If you believe this happened as a result of a stack
==9749==  overflow in your program's main thread (unlikely but
==9749==  possible), you can try to increase the size of the
==9749==  main thread stack using the --main-stacksize= flag.
==9749==  The main thread stack size used in this run was 8388608.
==9749==
==9749== HEAP SUMMARY:
==9749==     in use at exit: 3 bytes in 1 blocks
==9749==   total heap usage: 1 allocs, 0 frees, 3 bytes allocated
==9749==
==9749== LEAK SUMMARY:
==9749==    definitely lost: 0 bytes in 0 blocks
==9749==    indirectly lost: 0 bytes in 0 blocks
==9749==      possibly lost: 0 bytes in 0 blocks
==9749==    still reachable: 3 bytes in 1 blocks
==9749==         suppressed: 0 bytes in 0 blocks
==9749== Rerun with --leak-check=full to see details of leaked memory
==9749==
==9749== For counts of detected and suppressed errors, rerun with: -v
==9749== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault

모르는 경우 이미 mem힙 할당 메모리 임을 알았습니다 . 힙은 프로그램이 명시 적으로 요청했기 때문에 (이 경우 malloc을 사용하여) 런타임에 프로그램에서 사용 가능한 메모리 영역을 나타냅니다.

당신이 끔찍한 코드를 가지고 놀면, 명백하게 잘못된 진술이 모두 세그먼테이션 결함 (치명적인 종료 오류)을 초래하지는 않습니다.

예제 코드에서 이러한 오류를 명시 적으로 만들었지 만 메모리 관리 환경에서 동일한 종류의 오류가 매우 쉽게 발생합니다. 예를 들어 일부 코드가 변수 (또는 다른 기호)의 참조 횟수를 올바른 방식으로 유지하지 않으면 너무 일찍 사용 가능하면 이미 사용 가능한 메모리에서 다른 코드 조각을 읽을 수 있습니다. 주소를 잘못 저장하면 다른 코드가 유효하지 않은 메모리에 쓸 수 있습니다. 두 번 해제 될 수 있습니다 …

PHP에서 디버깅 할 수있는 문제는 아니며 내부 개발자의주의가 필요합니다.

행동 과정은 다음과 같아야합니다.

  1. http://bugs.php.net 에서 버그 보고서를 엽니 다.
    • segfault가있는 경우 역 추적 을 제공하십시오.
    • opcache 포함 최적화 수준을 사용하는 경우 특히 적절한 것으로 보이는만큼 구성 정보를 포함하십시오.
    • 업데이트에 대한 버그 보고서를 계속 확인하십시오. 자세한 정보가 필요할 수 있습니다.
  2. opcache가로드 된 경우 최적화를 비활성화하십시오.
    • 나는 opcache를 선택하지 않고 있지만 훌륭하지만 일부 최적화는 결함을 일으키는 것으로 알려져 있습니다.
    • 그래도 작동하지 않으면 코드 속도가 느려도 opcache를 먼저 언로드하십시오.
    • 이 중 하나라도 변경되거나 문제가 해결되면 작성한 버그 보고서를 업데이트하십시오.
  3. 한 번에 불필요한 확장을 모두 비활성화 하십시오 .
    • 각 구성 변경 후 철저히 테스트하여 모든 확장을 개별적으로 활성화하십시오.
    • 문제 확장을 찾으면 추가 정보로 버그 보고서를 업데이트하십시오.
  4. 이익.

이익이 없을 수도 있습니다 … 처음에 말했다, 당신은 구성을 엉망으로 증상을 변경하는 방법을 찾을 수 있지만, 이것은 매우 치명적이며 그리워하고 다음 번에는 도움이되지 않습니다 같은 zend_mm_heap corrupted메시지에는 구성 옵션이 너무 많습니다.

버그를 발견 할 때 버그 보고서를 작성하는 것이 매우 중요합니다. 버그를 공격 할 다음 사람이 그럴 것이라고 가정 할 수는 없습니다. 그렇지 않을 경우 실제 해결 방법은 신비하지 않습니다. 올바른 사람들은 문제를 알고 있습니다.

USE_ZEND_ALLOC

USE_ZEND_ALLOC=0환경에서 설정 하면 Zend의 자체 메모리 관리자가 비활성화됩니다. Zend의 메모리 관리자는 각 요청에 자체 힙이 있고 요청이 끝날 때 모든 메모리가 해제되도록하며 PHP에 적합한 크기의 메모리 할당에 최적화되어 있습니다.

이를 비활성화하면 이러한 최적화가 비활성화되고, 더 중요한 것은 Zend MM에 의존하여 요청이 끝날 때 메모리를 비우기 위해 많은 확장 코드가 있기 때문에 메모리 누수가 발생할 가능성이 있다는 것입니다 (tut, tut).

증상 을 숨길 수도 있지만 Zend의 힙과 정확히 같은 방식으로 시스템 힙이 손상 될 수 있습니다.

더 관대하거나 덜 관대 해 보일 수 있지만 문제의 근본 원인을 고칠 수는 없습니다 .

이를 비활성화하는 기능은 내부 개발자의 이익을위한 것입니다. Zend MM이 비활성화 된 상태에서 PHP를 배포 해서는 안됩니다 .


답변

PHP 5.5에서 이와 동일한 오류가 발생하여 출력 버퍼링을 늘리는 것이 도움이되지 않았습니다. 나는 APC를 실행하지 않았으므로 문제가되지 않았습니다. 나는 마침내 그것을 opcache 추적했다 . 나는 단순히 cli에서 그것을 비활성화해야했다. 이에 대한 특정 설정이 있습니다.

opcache.enable_cli=0

zend_mm_heap로 전환하면 손상된 오류가 사라졌습니다.


답변

Linux 상자에있는 경우 명령 행에서 시도하십시오.

export USE_ZEND_ALLOC=0


답변

unset()s를 확인하십시오 . 당신이하지 않는 확인 unset()받는 참조 $this소멸자에서 (또는 등가물)하고 unset()좀 연구를 발견 한 같은 객체에 대한 참조 카운트가 0으로 드롭하지 않는 원인 소멸자에들의 일반적으로 힙의 원인을 것을 부패.

있다 zend_mm_heap 손상에 대한 PHP의 버그 리포트 오류가. [2011-08-31 07:49 UTC] f dot ardelian at gmail dot com그것을 재현하는 방법에 대한 예 는 주석 을 참조하십시오 .

다른 모든 “솔루션”(변경 php.ini, 더 적은 모듈로 소스에서 PHP 컴파일 등)이 문제를 숨기고 있다고 생각합니다 .


답변

내가 시도 할 때까지 이전 답변 중 아무것도 작동하지 않았습니다.

opcache.fast_shutdown=0

그것은 지금까지 작동하는 것 같습니다.

PHP-FPM 및 Apache proxy_fcgi와 함께 PHP 5.6을 사용하고 있습니다.


답변

필자의 경우이 오류의 원인은 배열 중 하나가 매우 커지고 있다는 것입니다. 반복 할 때마다 배열을 재설정하도록 스크립트를 설정했으며 문제가 정렬되었습니다.