PHP assert 함수에 대한 고찰
2020-08-28 / dmbs335

code execution 이 가능한 상황에서

assert 는 다중 함수를 실행하는데 있어 편리한 함수 중 하나였습니다.

그런데 PHP 7 버전 부터는 assert 함수가 간혹 예상 했던 대로 동작 하지 않는 경우가 많았습니다.

정확한 원인을 파악하고자 분석을 해보았습니다.


예를 들어, assert("phpinfo()"); 는 분석 환경에서 phpinfo 를 실행하지 않는 반면

call_user_func("assert", "phpinfo()"); 는 잘 동작합니다.

반면에 call_user_func($_GET['a'], $_GET['b']); 에서

a=assert&b=phpinfo() 로 줄 경우는 동작하지 않습니다.


PHP Manual: assert

링크의 설명을 참고하면 zend.assertions 의 값이 -1 인 경우

assert 가 코드를 생성하지 않고 참을 반환합니다.

분석 환경의 zend.assertions 값은 -1 이기 때문에

assert("phpinfo()") 가 동작하지 않는 이유는 쉽게 설명이 가능합니다.

그러나 이러한 설정 값이 적용되어 있는데도 불구하고

두 번째 코드가 동작하는 이유는 알 수 없었습니다.

더 자세히 알아보기 위해 PHP github 에서 zend.assertions 을 검색해보았습니다.


1


위 함수는 zend.assertions 설정의 값에 따라 분기합니다.

만약 실행된다면 중요한 역할을 할 것이라 추측할 수 있습니다.


2


추가로 체크해볼 만한 함수가 있는지 알아보기 위해

gdb 상에서 함수 심볼을 검색해 보았고

PHP 함수 assert 의 본체인 zif_assert 까지 포함하여

두 곳에 브레이크 포인트를 걸고 gdb 로 다음 코드를 실행해보았습니다.


1
2
3
4
5
6
7
8
9
10
11
<?php
assert("phpinfo()");

call_user_func("assert", "phpinfo()");

$a1 = "assert";

$a2 = "phpinfo()";

call_user_func($a1, $a2);
?>


zend_compile_assert 가 실행되는 시점의 스택 프레임은

다음 사진과 같은 상태로 코드의 compile 이 이루어지는 과정에서

assert 문을 컴파일 하기 위한 함수라는 것을 알 수 있었습니다.

즉, zend.assertions 에 대한 검사는 런타임이 아닌 컴파일 타임에 이루어지는 것입니다.

문자열로 구성되어 call_user_func 의 인자로 들어가는

assert 는 이 시점에 컴파일 되지 않기 때문에

해당 분기에 영향을 받지 않고 assert 가 실행되는 것입니다.


3


반면 3번째 코드는 그럼에도 불구하고 실행되지 않습니다.

그 이유는 PHP 상에서 call_user_func 의 처음 인자로

assert 가 dynamic 하게 들어가는 경우 실행하지 않고 Warning 을 띄우기 때문이었습니다.


1
2
3
4
5
6
7
8
<?php
ini_set("display_errors", 1);
error_reporting(E_ALL);

$a1 = "assert";
$a2 = "phpinfo()";
call_user_func($a1, $a2);
?>


ini_seterror_reporting 을 설정한 후 call_user_funcassert 를 dynamic 하게 넘겨주면 다음 에러 메세지를 볼 수 있습니다.


4