首页
社区
课程
招聘
[原创]php pwn分析与phpgdb插件
发表于: 6天前 1849

[原创]php pwn分析与phpgdb插件

6天前
1849

国内比赛最近非常喜欢出php的pwn,php解释器本身没有太多的可利用点,出题一般把漏洞埋在php的拓展。掌握了php的调试、函数传参、堆内存管理以后这类题难度都不大。
由于php题的难度主要在调试方面,但是又没有很好用的gdb插件,因此自己写了一个phpgdb用于调试。

安装php,并查看版本,

推荐使用源码安装,因为这样会有调试符号,便于本地调试(尤其是学习堆的时候)

这样是由完整调试符号和源码的:

image

主要关注其中的disable_functions​、disable_classes​和extension​,前二者限制了可以用于编写php利用脚本的函数和类,后者一般是pwn选手需要关注的带有漏洞的拓展文件。

下载对应版本的php源码,进入ext​目录,创建一个拓展

在拓展名对应的目录具有如下结构:

其中easy_phppwn_arginfo.h​头文件与拓展的参数信息有关,不需要手动修改,在easy_phppwn.stub.php​中修改对应的文件即可。默认生成的只有test​和test2​两个函数,加入test3​:

随后自动构建easy_phppwn_arginfo.h

添加函数功能

编译,configure​生成的Makefile​需要删去-O2​优化,否则会加上FORTIFY​保护,导致memcpy​函数加上长度检查变为__memcpy_chk​函数:

modules​目录下会生成编译好的拓展文件easy_phppwn.so​。

默认的拓展路径通过命令查看:

拓展在Linux下是一个动态链接库,通常在php.ini​中导入,并将so文件移动到上步输出的拓展路径下:

或者直接通过命令运行,而无需导入:

自己写了个gdb python脚本phpdbg,用于php调试,功能会逐步完善。

首先编写一个php代码:

运行说明成功

gdb进行调试

根据php启动过程,在php_module_startup()​函数中加载拓展:

因此下断点跑完这个函数就能看到模块被加载进来:

image

此时可以接着下断点到zif_test1​,这里需要注意php编译之后的是函数名会加上zif_​前缀

image

test3​中可以看到栈溢出:

image

这里就不再赘述这个案例的利用方式了,结合后续题目进行介绍。

​​

反编译的代码来看基本上除了参数处理以外就是原生的C代码,可读性比较强。

image

但是从题目来看,一般都是直接给的二进制文件,所以需要具体了解zend_parse_parameters​的传参规则,这里的讲解不会涉及[底层细节](23aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1L8$3!0C8M7%4c8S2j5$3E0Q4x3X3g2U0L8W2)9J5c8Y4u0W2j5h3c8Q4x3V1k6H3K9s2l9%4i4K6u0V1K9h3&6@1k6i4u0F1j5h3I4Q4x3V1j5%4i4K6u0V1k6Y4g2F1j5#2)9J5k6h3#2V1i4K6t1K6y4#2)9J5k6e0k6Q4x3X3f1J5 %E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90):

对于参数类型而言,常用参数对照表:

字符串类型解析:

在PHP 7中,字符串解析有两种形式:char*和zend_string。其中:

复合类型规范:

在实际使用中,可以将多个类型规范符组合使用,以表示多个参数的类型。例如:

可选参数:

在类型规范字符串中,可以使用|​符号来表示后续的参数是可选的。例如:

但是很多时候都是直接用z​来代替参数,在后面通常会有一个形如v15[8] == 6​的比较操作,这实际上是在确定参数的类型:

image

具体对应关系是:

84cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6H3K9s2m8Q4x3V1k6H3K9s2m8Q4x3X3c8K6M7X3y4Q4x3V1k6T1L8r3!0T1i4K6u0r3x3U0p5J5j5U0t1^5x3K6c8W2z5h3k6T1j5$3t1&6j5e0b7^5j5U0W2U0j5U0M7H3z5e0M7I4x3$3t1$3j5$3t1I4z5e0M7$3x3o6N6U0j5#2)9J5c8X3c8G2j5%4y4Q4x3V1k6K6L8%4g2J5j5$3g2Q4x3V1k6U0L8%4u0W2i4K6u0r3k6r3q4@1j5g2)9J5k6s2y4@1M7Y4g2U0N6s2g2J5k6i4y4Q4x3V1k6*7N6X3q4D9i4K6u0W2M7Y4y4@1

虽说是php的内存管理,但是实际上是其内部zend引擎的内存管理机制。PHP采取“预分配方案”,提前向操作系统申请一个chunk(2M,利用到hugepage特性),并且将这2M内存切割为不同规格(大小)的若干内存块,当程序申请内存时,直接查找现有的空闲内存块即可;

PHP将内存分配请求分为3种情况:

huge内存:针对大于2M-4K的分配请求,直接调用mmap分配;

large内存:针对小于2M-4K,大于3K的分配请求,在chunk上查找满足条件的若干个连续page;

small内存:针对小于3K的分配请求;PHP拿出若干个页切割为8字节大小的内存块,拿出若干个页切割为16字节大小的内存块,24字节,32字节等等,将其组织成若干个空闲链表;每当有分配请求时,只在对应的空闲链表获取一个内存块即可;

在large和small两类chunk的第一个page里,会存储chunk的控制信息,这个结构体是_zend_mm_chunk​,所有的chunk会形成一个双向链表,zend_mm_page_map​利用位图记录512个page的使用情况,0代表空闲,1代表已经分配。zend_mm_page_info​通过uint32_t​存储FLAG信息,

然后是\_zend\_mm\_heap​,是chunk的上级管理结构,存储与堆分配相关的全局信息:

如果不方便看的话可以直接看gdb的结果:

image

堆的最上层结构体是封装了zend_mm_heap​的zend_alloc_globals​:

alloc_globals​是类似于glibc中main_arena​的变量,通过它即可逐步获取整个堆:

这里只介绍small类型的内存分配,而这也是与我们攻击直接相关的部分。简单来说,small类型内存的空闲链表类似于2.27下的tcache空闲链表,也是单链表形式,并且没有任何保护,因此只需要修改链表中任一节点,即可劫持free的空闲链表。它的结构类似于:

image

下面从源码来分析一下,当申请small类型heap时:

如果free_slot​资源不够,则会调用zend_mm_alloc_small_slow​创建一个对应大小的free_slot​:

释放时,直接将free的small heap链入末尾:

网上没有搜到比较合适的,自己写了个phpgdb,目前支持4个命令。

运行到php加载完所有拓展之后,此时可以设置断点。

查看最上层的堆信息:image

查看small slot链表:

image

查看给定地址所属于的element(最终分配的堆块)

image

83dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6h3g2H3N6h3&6C8i4K6u0W2K9h3y4#2i4K6u0r3M7r3S2H3i4K6u0V1M7s2N6F1i4K6u0r3

02aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2A6L8h3!0G2j5#2)9J5k6h3y4G2L8g2)9J5c8X3q4J5N6r3W2U0L8r3g2Q4x3V1j5#2x3e0p5J5y4l9`.`.

php类题型一般能够通过include​包含文件,因此可以直接从/proc/self/maps​中读出地址(其实vmmap​命令就是在读这个文件):

一般而言,php拓展编译成动态链接库,默认编译选项下其got表是可写的,因此通常可以利用任意写劫持got表来劫持执行流。

一般php pwn都会在远程服务器运行一个php代码,很可能不能通过nc拿到交互的shell,因此通常执行反弹shell或者sendfile等。

php pwn部分就是泄露地址+溢出ret2libc,可以作为入门题目。

题目来源:De1CTF 2020

参考:18dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2x3h3g2^5i4K6u0W2L8$3&6D9K9h3&6W2i4K6u0r3x3U0l9J5x3g2)9J5c8U0l9K6i4K6u0r3x3e0W2Q4x3V1k6%4k6h3u0H3N6$3&6Q4x3U0g2q4y4g2)9J5y4f1q4p5i4K6t1#2b7e0k6Q4x3U0g2q4y4q4)9J5y4f1t1&6i4K6t1#2b7e0m8Q4x3V1j5`.

题目来源:第一届“长城杯”信息安全铁人三项赛决 夺取闯关 pwn numbergame

分析给的numberGame.so​文件,发现是一个类似堆题的增删改查功能,其中zif_show_chunk​调用了一个自定义的_quicksort​,漏洞点在这个位置:

image

但是要去具体分析_quicksort​的代码来找到漏洞形成原因会比较困难,这里使用LLM生成fuzz代码来把这个漏洞测出来:

image

这是deepseek r1自动生成的代码,根据ida的代码可以进行细微的调整:

拿到代码不需要改,直接跑,几秒钟找到十几个error输入:

image

这个测试了一下,主要报错都是由于edit(16....)​导致的,这个属于是没什么用的洞。在fuzz里把这个问题修一下,顺便改一改参数:

这样跑起来几分钟就可以测出段错误:

跑起来验证一下也就是_quicksort​排序的时候越界的问题,把size修改得任意大了,甚至name字段也被覆盖了:

image

这个时候可以手工删减poc,以确定触发漏洞的输入:

这样就可以确定是排序导致的问题了,这个时候可以进一步针对这个数组序列构造fuzz:

这样跑起来就得到了很简单的poc:

会把size改大:

image

这样可以得到一个在php堆上的下溢任意地址写:

image

思路也比较简单,就是第一次利用越界写修改下一个chunk的name​指针,再利用这个name​指针实现任意地址写。这里给出直接打本地的脚本,远程同理:

题目还是一个典型的堆菜单,分析结构体有点抽象,感觉是为了埋洞之后方便利用搞的:

image

漏洞点是addHakcer​的时候存在一个off by null的漏洞:

image

按照如下布局:

即可覆盖chunkList[1].ptr->str1_ptr​:

image

结合editHacker​的修改能力:

能在堆上进行一定的篡改:

image

通过适当构造可以得到任意地址写:

image

exp:

题目来源:D3CTF 2024 PwnShell

参考:bdbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1j5&6j5h3&6#2P5q4)9J5k6h3!0J5k6#2)9J5c8U0t1H3x3U0c8Q4x3V1j5H3y4q4)9J5c8U0t1&6i4K6u0r3k6o6y4U0N6r3j5J5x3o6t1@1i4K6u0r3K9h3&6V1k6i4S2Q4x3X3g2Z5N6r3#2D9

题目来源:D3CTF 2021 hackphp

参考:

574K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6g2c8g2y4#2M7r3g2J5c8$3q4@1k6g2)9J5c8V1b7K6b7#2c8r3i4K6u0V1f1$3!0#2M7X3y4W2i4K6u0r3j5X3I4G2j5W2)9J5c8X3#2S2M7%4c8W2M7W2)9J5c8X3S2S2j5$3E0H3K9s2m8Q4x3V1k6W2P5s2m8Q4x3X3g2H3K9s2l9`.

216K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8Y4q4#2j5h3&6C8k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8Q4x3V1k6A6k6q4)9J5c8U0t1K6y4e0t1K6y4#2)9J5x3$3R3J5i4K6u0V1y4b7`.`.

题目来源:第二届长城杯半决赛 phpmaster

参考:https://bbs.kanxue.com/thread-286086.htm

97cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8Y4q4#2j5h3&6C8k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8Q4x3V1k6A6k6q4)9J5c8U0t1H3y4o6b7H3y4l9`.`.

333K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6L8h3I4*7K9o6q4Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8Y4m8G2M7%4c8K6i4K6u0r3f1p5S2b7i4K6u0V1f1$3!0Q4x3X3c8b7N6$3&6Q4x3V1k6Q4x3U0y4*7k6h3&6V1i4K6g2X3M7r3q4J5M7$3g2Q4y4h3k6H3j5i4u0S2L8h3g2@1k6i4u0K6

a88K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1L8$3!0C8M7%4c8S2j5$3E0Q4x3X3g2U0L8W2)9J5c8Y4u0W2j5h3c8Q4x3V1k6H3K9s2l9%4i4K6u0V1K9h3&6@1k6i4u0F1j5h3I4Q4x3V1j5%4i4K6u0V1K9h3#2H3L8r3g2E0k6h3&6@1i4K6u0W2L8h3b7`.

fc4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5N6h3q4F1P5s2g2S2L8X3u0D9K9h3&6Y4j5X3I4A6L8X3N6Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8X3y4@1k6W2)9J5c8Y4m8%4L8W2)9J5c8U0t1H3x3U0m8Q4x3V1j5H3y4g2)9J5c8U0l9#2i4K6u0r3L8h3W2^5N6s2g2J5k6g2)9J5c8R3`.`.

11aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4P5W2g2G2K9e0M7$3P5e0g2y4L8#2g2a6h3g2m8K6g2W2k6K9N6X3&6E0f1g2)9K6c8Y4m8%4k6q4)9K6c8s2R3@1z5h3j5`.

sudo apt install php php-dev
❯ php -v
PHP 8.3.6 (cli) (built: Mar 19 2025 10:08:38) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
sudo apt install php php-dev
❯ php -v
PHP 8.3.6 (cli) (built: Mar 19 2025 10:08:38) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
$ git clone https://github.com/php/php-src.git \
    --branch=PHP-8.3.15
$ cd php-src
$ ./buildconf --force
$ ./configure \
    --enable-cli \
    --enable-debug
$ make && make test && make install
$ git clone https://github.com/php/php-src.git \
    --branch=PHP-8.3.15
$ cd php-src
$ ./buildconf --force
$ ./configure \
    --enable-cli \
    --enable-debug
$ make && make test && make install
❯ php ./ext/ext_skel.php --ext easy_phppwn --onlyunix
Copying config scripts... done
Copying sources... done
Copying tests... done
 
Success. The extension is now ready to be compiled. To do so, use the
following steps:
 
cd /home/l1qu1d/pwn/chall/php_pwn/php_test/php-src-php-8.3.15/ext/easy_phppwn
phpize
./configure
make
 
Don't forget to run tests once the compilation is done:
make test
 
Thank you for using PHP!
❯ php ./ext/ext_skel.php --ext easy_phppwn --onlyunix
Copying config scripts... done
Copying sources... done
Copying tests... done
 
Success. The extension is now ready to be compiled. To do so, use the
following steps:
 
cd /home/l1qu1d/pwn/chall/php_pwn/php_test/php-src-php-8.3.15/ext/easy_phppwn
phpize
./configure
make
 
Don't forget to run tests once the compilation is done:
make test
 
Thank you for using PHP!
❯ tree ./easy_phppwn
./easy_phppwn
├── config.m4
├── easy_phppwn.c
├── easy_phppwn.stub.php
├── easy_phppwn_arginfo.h
├── php_easy_phppwn.h
└── tests
    ├── 001.phpt
    ├── 002.phpt
    └── 003.phpt
 
2 directories, 8 files
❯ tree ./easy_phppwn
./easy_phppwn
├── config.m4
├── easy_phppwn.c
├── easy_phppwn.stub.php
├── easy_phppwn_arginfo.h
├── php_easy_phppwn.h
└── tests
    ├── 001.phpt
    ├── 002.phpt
    └── 003.phpt
 
2 directories, 8 files
<?php
 
/**
 * @generate-class-entries
 * @undocumentable
 */
 
function test1(): void {}
 
function test2(string $str = ""): string {}
 
function test3(string $name): string {}
<?php
 
/**
 * @generate-class-entries
 * @undocumentable
 */
 
function test1(): void {}
 
function test2(string $str = ""): string {}
 
function test3(string $name): string {}
php ../../build/gen_stub.php --ext=easy_phppwn ./easy_phppwn.stub.php
php ../../build/gen_stub.php --ext=easy_phppwn ./easy_phppwn.stub.php
PHP_FUNCTION(test3)
{
    char *arg = NULL;
    size_t arg_len, len;
    char buf[100];
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
        return;
    }
    memcpy(buf, arg, arg_len);
    php_printf("The baby phppwn.\n");
    return SUCCESS;
}
PHP_FUNCTION(test3)
{
    char *arg = NULL;
    size_t arg_len, len;
    char buf[100];
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
        return;
    }
    memcpy(buf, arg, arg_len);
    php_printf("The baby phppwn.\n");
    return SUCCESS;
}
❯ phpize
Configuring for:
PHP Api Version:         20230831
Zend Module Api No:      20230831
Zend Extension Api No:   420230831
configure.ac:165: warning: The macro `AC_PROG_LIBTOOL' is obsolete.
configure.ac:165: You should run autoupdate.
build/libtool.m4:100: AC_PROG_LIBTOOL is expanded from...
configure.ac:165: the top level
❯ ./configure --with-php-config=/usr/bin/php-config
...
make
❯ phpize
Configuring for:
PHP Api Version:         20230831
Zend Module Api No:      20230831
Zend Extension Api No:   420230831
configure.ac:165: warning: The macro `AC_PROG_LIBTOOL' is obsolete.
configure.ac:165: You should run autoupdate.
build/libtool.m4:100: AC_PROG_LIBTOOL is expanded from...
configure.ac:165: the top level
❯ ./configure --with-php-config=/usr/bin/php-config
...
make
php -i | grep -i extension_dir
php -i | grep -i extension_dir
php -d extension=./modules/easy_phppwn.so test.php
php -d extension=./modules/easy_phppwn.so test.php
<?php
test1();
?>
<?php
test1();
?>
❯ php -d extension=./modules/easy_phppwn.so test.php
The extension easy_phppwn is loaded and working!
❯ php -d extension=./modules/easy_phppwn.so test.php
The extension easy_phppwn is loaded and working!
gdb --args php -d extension=./modules/easy_phppwn.so test.php
gdb --args php -d extension=./modules/easy_phppwn.so test.php
zend_parse_parameters(int num_args, const char *type_spec, ...);
zend_parse_parameters(int num_args, const char *type_spec, ...);
类型规范符 对应的C语言类型 说明
b​或i int 整数类型,b​通常表示bool类型,而i​表示int类型
l long 长整型
d double 浮点数类型
s char* 字符串,表示C语言中的字符指针
S zend_string PHP 7中的 zend_string 类型字符串
a zval* PHP数组类型
o zval* PHP对象类型
r zval* PHP资源类型
z zval* PHP变量(可以是任何类型)
N 表示参数为NULL
#define IS_UNDEF     0 /* A variable that was never written to. */
#define IS_NULL      1
#define IS_FALSE     2
#define IS_TRUE      3
#define IS_LONG      4 /* An integer value. */
#define IS_DOUBLE    5 /* A floating point value. */
#define IS_STRING    6
#define IS_ARRAY     7
#define IS_OBJECT    8
#define IS_RESOURCE  9
#define IS_REFERENCE 10
#define IS_UNDEF     0 /* A variable that was never written to. */
#define IS_NULL      1
#define IS_FALSE     2
#define IS_TRUE      3
#define IS_LONG      4 /* An integer value. */
#define IS_DOUBLE    5 /* A floating point value. */
#define IS_STRING    6
#define IS_ARRAY     7
#define IS_OBJECT    8
#define IS_RESOURCE  9
#define IS_REFERENCE 10
struct _zend_mm_chunk {
    zend_mm_heap      *heap;
    zend_mm_chunk     *next;
    zend_mm_chunk     *prev;
    uint32_t           free_pages;              /* number of free pages */
    uint32_t           free_tail;               /* number of free pages at the end of chunk */
    uint32_t           num;
    char               reserve[64 - (sizeof(void*) * 3 + sizeof(uint32_t) * 3)];
    zend_mm_heap       heap_slot;               /* used only in main chunk */
    zend_mm_page_map   free_map;                /* 512 bits or 64 bytes */
    zend_mm_page_info  map[ZEND_MM_PAGES];      /* 2 KB = 512 * 4 */
};
struct _zend_mm_chunk {
    zend_mm_heap      *heap;
    zend_mm_chunk     *next;
    zend_mm_chunk     *prev;
    uint32_t           free_pages;              /* number of free pages */
    uint32_t           free_tail;               /* number of free pages at the end of chunk */
    uint32_t           num;
    char               reserve[64 - (sizeof(void*) * 3 + sizeof(uint32_t) * 3)];
    zend_mm_heap       heap_slot;               /* used only in main chunk */
    zend_mm_page_map   free_map;                /* 512 bits or 64 bytes */
    zend_mm_page_info  map[ZEND_MM_PAGES];      /* 2 KB = 512 * 4 */
};
struct _zend_mm_heap {
#if ZEND_MM_CUSTOM
    int                use_custom_heap;
#endif
#if ZEND_MM_STORAGE
    zend_mm_storage   *storage;
#endif
#if ZEND_MM_STAT
    size_t             size;                    /* current memory usage */
    size_t             peak;                    /* peak memory usage */
#endif
    uintptr_t          shadow_key;              /* free slot shadow ptr xor key */
    zend_mm_free_slot *free_slot[ZEND_MM_BINS]; /* free lists for small sizes */
#if ZEND_MM_STAT || ZEND_MM_LIMIT
    size_t             real_size;               /* current size of allocated pages */
#endif
#if ZEND_MM_STAT
    size_t             real_peak;               /* peak size of allocated pages */
#endif
#if ZEND_MM_LIMIT
    size_t             limit;                   /* memory limit */
    int                overflow;                /* memory overflow flag */
#endif
 
    zend_mm_huge_list *huge_list;               /* list of huge allocated blocks */
 
    zend_mm_chunk     *main_chunk;
    zend_mm_chunk     *cached_chunks;           /* list of unused chunks */
    int                chunks_count;            /* number of allocated chunks */
    int                peak_chunks_count;       /* peak number of allocated chunks for current request */
    int                cached_chunks_count;     /* number of cached chunks */
    double             avg_chunks_count;        /* average number of chunks allocated per request */
    int                last_chunks_delete_boundary; /* number of chunks after last deletion */
    int                last_chunks_delete_count;    /* number of deletion over the last boundary */
#if ZEND_MM_CUSTOM
    struct {
        void      *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        void       (*_free)(void*  ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        void      *(*_realloc)(void*, size_t  ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        size_t     (*_gc)(void);
        void       (*_shutdown)(bool full, bool silent);
    } custom_heap;
    union {
        HashTable *tracked_allocs;
        struct {
            bool    poison_alloc;
            uint8_t poison_alloc_value;
            bool    poison_free;
            uint8_t poison_free_value;
            uint8_t padding;
            bool    check_freelists_on_shutdown;
        } debug;
    };
#endif
    pid_t pid;
    zend_random_bytes_insecure_state rand_state;
};
struct _zend_mm_heap {
#if ZEND_MM_CUSTOM
    int                use_custom_heap;
#endif
#if ZEND_MM_STORAGE
    zend_mm_storage   *storage;
#endif
#if ZEND_MM_STAT
    size_t             size;                    /* current memory usage */
    size_t             peak;                    /* peak memory usage */
#endif
    uintptr_t          shadow_key;              /* free slot shadow ptr xor key */
    zend_mm_free_slot *free_slot[ZEND_MM_BINS]; /* free lists for small sizes */
#if ZEND_MM_STAT || ZEND_MM_LIMIT
    size_t             real_size;               /* current size of allocated pages */
#endif
#if ZEND_MM_STAT
    size_t             real_peak;               /* peak size of allocated pages */
#endif
#if ZEND_MM_LIMIT
    size_t             limit;                   /* memory limit */
    int                overflow;                /* memory overflow flag */
#endif
 
    zend_mm_huge_list *huge_list;               /* list of huge allocated blocks */
 
    zend_mm_chunk     *main_chunk;
    zend_mm_chunk     *cached_chunks;           /* list of unused chunks */
    int                chunks_count;            /* number of allocated chunks */
    int                peak_chunks_count;       /* peak number of allocated chunks for current request */
    int                cached_chunks_count;     /* number of cached chunks */
    double             avg_chunks_count;        /* average number of chunks allocated per request */
    int                last_chunks_delete_boundary; /* number of chunks after last deletion */
    int                last_chunks_delete_count;    /* number of deletion over the last boundary */
#if ZEND_MM_CUSTOM
    struct {
        void      *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        void       (*_free)(void*  ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        void      *(*_realloc)(void*, size_t  ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
        size_t     (*_gc)(void);
        void       (*_shutdown)(bool full, bool silent);
    } custom_heap;
    union {
        HashTable *tracked_allocs;
        struct {
            bool    poison_alloc;
            uint8_t poison_alloc_value;
            bool    poison_free;
            uint8_t poison_free_value;
            uint8_t padding;
            bool    check_freelists_on_shutdown;
        } debug;
    };
#endif
    pid_t pid;
    zend_random_bytes_insecure_state rand_state;
};
typedef struct _zend_alloc_globals {
    zend_mm_heap *mm_heap;
} zend_alloc_globals;
typedef struct _zend_alloc_globals {
    zend_mm_heap *mm_heap;
} zend_alloc_globals;
static zend_alloc_globals alloc_globals;
static zend_alloc_globals alloc_globals;
static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
#if ZEND_MM_STAT
    do {
        size_t size = heap->size + bin_data_size[bin_num];
        size_t peak = MAX(heap->peak, size);
        heap->size = size;
        heap->peak = peak;
    } while (0);
#endif
 
    if (EXPECTED(heap->free_slot[bin_num] != NULL)) {
        zend_mm_free_slot *p = heap->free_slot[bin_num];
        heap->free_slot[bin_num] = p->next_free_slot;
        return p;
    } else {
        return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
    }
}
static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
#if ZEND_MM_STAT
    do {
        size_t size = heap->size + bin_data_size[bin_num];
        size_t peak = MAX(heap->peak, size);
        heap->size = size;
        heap->peak = peak;
    } while (0);
#endif
 
    if (EXPECTED(heap->free_slot[bin_num] != NULL)) {
        zend_mm_free_slot *p = heap->free_slot[bin_num];
        heap->free_slot[bin_num] = p->next_free_slot;
        return p;
    } else {
        return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
    }
}
static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint32_t bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
    zend_mm_chunk *chunk;
    int page_num;
    zend_mm_bin *bin;
    zend_mm_free_slot *p, *end;
 
#if ZEND_DEBUG
    bin = (zend_mm_bin*)zend_mm_alloc_pages(heap, bin_pages[bin_num], bin_data_size[bin_num] ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#else
    bin = (zend_mm_bin*)zend_mm_alloc_pages(heap, bin_pages[bin_num] ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#endif
    if (UNEXPECTED(bin == NULL)) {
        /* insufficient memory */
        return NULL;
    }
 
    chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(bin, ZEND_MM_CHUNK_SIZE);
    page_num = ZEND_MM_ALIGNED_OFFSET(bin, ZEND_MM_CHUNK_SIZE) / ZEND_MM_PAGE_SIZE;
    chunk->map[page_num] = ZEND_MM_SRUN(bin_num);
    if (bin_pages[bin_num] > 1) {
        uint32_t i = 1;
 
        do {
            chunk->map[page_num+i] = ZEND_MM_NRUN(bin_num, i);
            i++;
        } while (i < bin_pages[bin_num]);
    }
 
    /* create a linked list of elements from 1 to last */
    end = (zend_mm_free_slot*)((char*)bin + (bin_data_size[bin_num] * (bin_elements[bin_num] - 1)));
    heap->free_slot[bin_num] = p = (zend_mm_free_slot*)((char*)bin + bin_data_size[bin_num]);
    do {
        zend_mm_set_next_free_slot(heap, bin_num, p, (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]));
#if ZEND_DEBUG
        do {
            zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
            dbg->size = 0;
        } while (0);
#endif
        p = (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]);
    } while (p != end);
 
    /* terminate list using NULL */
    p->next_free_slot = NULL;
#if ZEND_DEBUG
        do {
            zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
            dbg->size = 0;
        } while (0);
#endif
 
    /* return first element */
    return bin;
}
static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint32_t bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
    zend_mm_chunk *chunk;
    int page_num;
    zend_mm_bin *bin;
    zend_mm_free_slot *p, *end;
 
#if ZEND_DEBUG
    bin = (zend_mm_bin*)zend_mm_alloc_pages(heap, bin_pages[bin_num], bin_data_size[bin_num] ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#else
    bin = (zend_mm_bin*)zend_mm_alloc_pages(heap, bin_pages[bin_num] ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
#endif
    if (UNEXPECTED(bin == NULL)) {
        /* insufficient memory */
        return NULL;
    }
 
    chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(bin, ZEND_MM_CHUNK_SIZE);
    page_num = ZEND_MM_ALIGNED_OFFSET(bin, ZEND_MM_CHUNK_SIZE) / ZEND_MM_PAGE_SIZE;
    chunk->map[page_num] = ZEND_MM_SRUN(bin_num);
    if (bin_pages[bin_num] > 1) {
        uint32_t i = 1;
 
        do {
            chunk->map[page_num+i] = ZEND_MM_NRUN(bin_num, i);
            i++;
        } while (i < bin_pages[bin_num]);
    }
 
    /* create a linked list of elements from 1 to last */
    end = (zend_mm_free_slot*)((char*)bin + (bin_data_size[bin_num] * (bin_elements[bin_num] - 1)));
    heap->free_slot[bin_num] = p = (zend_mm_free_slot*)((char*)bin + bin_data_size[bin_num]);
    do {
        zend_mm_set_next_free_slot(heap, bin_num, p, (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]));
#if ZEND_DEBUG
        do {
            zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
            dbg->size = 0;
        } while (0);
#endif
        p = (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num]);
    } while (p != end);
 
    /* terminate list using NULL */
    p->next_free_slot = NULL;
#if ZEND_DEBUG
        do {
            zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
            dbg->size = 0;
        } while (0);
#endif
 
    /* return first element */
    return bin;
}
static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, int bin_num)
{
    ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);
 
    zend_mm_free_slot *p;
 
#if ZEND_MM_STAT
    heap->size -= bin_data_size[bin_num];
#endif
 
#if ZEND_DEBUG
    do {
        zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)ptr + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
        dbg->size = 0;
    } while (0);
#endif
 
    p = (zend_mm_free_slot*)ptr;
    zend_mm_set_next_free_slot(heap, bin_num, p, heap->free_slot[bin_num]);
    heap->free_slot[bin_num] = p;
}
static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, int bin_num)
{
    ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);
 
    zend_mm_free_slot *p;
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回