Perl图形脚本命令行运行正常 作为CGI调用gdBrushed时崩溃
故障现象
- 存量Perl脚本已稳定运行多年,功能为生成PNG格式折线图供内网网页展示,原作为Apache CGI程序部署在Windows 7主机,长期运行无异常
- 近期将Apache服务从Windows 7迁移至Windows 10主机后出现故障:脚本作为CGI运行时无法调用Chart::Lines模块生成图形文件,但将脚本改为普通Perl程序在命令行执行时,所有功能完全正常
- 逐行排查定位故障触发点位于
C:\Strawberry\perl\site\lib\Chart\Base.pm文件的_draw_right_legend子程序内,触发崩溃的代码行如下:
sub _draw_right_legend { . . # draw the point $self->{'gd_obj'}->line(int(($x3+$x2)/2), $y2, int(($x3+$x2)/2), $y2, gdBrushed); . . }
- 脚本作为CGI运行时,执行到调用
gdBrushed的该行代码会直接终止;初期排查阶段Apache日志无任何相关报错记录,因命令行运行无异常,无法直接获取错误信息 - 临时测试结果:将该行参数中的
gdBrushed替换为同子程序内定义、用于绘制线条的$color变量,CGI脚本不会崩溃,但代码预期绘制的点位无法在生成的图形中显示,不满足功能要求;目前临时规避方案为注释该行,跳过点位绘制逻辑
部署背景
- 初期在Windows 10主机安装GD.pm与Chart::Lines模块时,因CPAN源无法检索到对应安装包,常规安装流程失败,最终采用直接从运行正常的Windows 7主机拷贝整个Perl程序目录到新主机的方式完成部署,该操作疑似与故障相关
- 拷贝部署完成后,除
gdBrushed点位绘制功能外,Chart::Lines模块其余功能均运行正常 - 后续修复了GD与Chart模块的安装问题后,故障仍未解决,程序尝试绘制点位时依然崩溃
已定位的报错信息
最终在Apache的error.log中查到明确报错内容:
Insecure dependency in eval while running with -T switch at C:/Strawberry/perl/site/lib/GD.pm line 87.
定位到GD.pm中触发报错的语句位于AUTOLOAD子程序内,对应代码段如下:
sub AUTOLOAD { # This AUTOLOAD is used to 'autoload' constants from the constant() # XS function. If a constant is not found then control is passed # to the AUTOLOAD in AutoLoader. my($constname); ($constname = $AUTOLOAD) =~ s/.*:://; undef $!; my $val = constant($constname); if ($! != 0) { if ($! =~ /Invalid/) { $AutoLoader::AUTOLOAD = $AUTOLOAD; goto &AutoLoader::AUTOLOAD; } else { my($pack,$file,$line) = caller; die "Your vendor has not defined GD macro $pack\:\:$constname, used at $file line $line $!.\n"; } } eval "sub $AUTOLOAD { $val }"; # 报错触发行 goto &$AUTOLOAD; }
进一步排查确认,报错发生时$AUTOLOAD变量值为GD::gdBrushed,可确定报错与gdBrushed常量的调用直接相关。
根因分析
故障核心原因为Perl污染模式(-T开关)的安全拦截:
- Apache运行CGI脚本时默认开启Perl的-T(污染检查)开关,命令行运行脚本时未开启该开关,因此出现「命令行运行正常、CGI模式运行崩溃」的差异表现
- 旧版本GD模块的AUTOLOAD子程序中,使用字符串eval动态生成常量子例程的写法不符合-T模式要求:eval执行的字符串内容来自XS模块返回的常量值,污染检查机制会判定该操作存在不安全依赖,直接拦截终止程序
- GD常量仅在首次调用时才会触发AUTOLOAD逻辑执行eval,其余提前加载完成的GD、Chart功能不受影响,因此表现为仅调用
gdBrushed时崩溃,其余功能正常。
解决方案
按优先级从高到低可选择以下方案:
- 升级GD模块至最新版本:新版GD模块已修复AUTOLOAD在-T模式下的兼容问题,不再使用字符串eval的方式动态生成常量子例程,可彻底解决该报错
- 若无法升级模块,可手动修改GD.pm的AUTOLOAD子程序,将字符串eval替换为符合污染模式要求的子例程引用赋值写法,避免动态字符串eval触发安全拦截
- 若确认脚本运行于内网可信环境、无外部输入注入风险,可调整Apache的CGI运行配置,移除Perl脚本启动参数中的-T开关,关闭污染检查(该方案存在安全风险,仅建议临时使用)
内容的提问来源于stack exchange,提问作者Jay C
相关产品推荐
相关产品推荐

