LISP 8.7.2 堵住漏洞2——修复变量名的漏洞

接上一节,先执行如下代码,观看效果:


(do-primes-3-1 (ending-value 0 10) (format t "~d~t" ending-value))




大笑,死机没?

最后一个需要堵上的漏洞是由于使用了变量名ending-value而产生的。

问题在于这个名字(其应当完全属于宏实现内部的细节)它可以跟传递给宏的代码或是宏被调用的上下文产生交互。

上述代码将无限循环下去,由于ending-value永远不会大于其余自身。


为了补上这一漏洞,需要一个永远不会在宏展开代码之外被用到的符号,你可以深度使用一个真正罕见的名字

但即便如此也无法做到万无一失


函数gensym在其每次被调用时返回唯一的符号。

这是一个没有被lisp读取器读过的符号并且永远不会被读到,因为它不会进入到任何包里

因而就可以在每次执行被展开时生成一个新的符号以替代ending-value这样的字面名称


;;;修复ending-value调用名漏洞
(defmacro do-primes-3-2 ( (var start end) &body body)
  (let ((ending-value (gensym)))
    `(do ((,var(next-prime ,start) (next-prime(1+ ,var)))
	  (,ending-value ,end))
	 ((> ,var ,ending-value))
       ,@body)))

生成展开式

(DO ((ENDING-VALUE (NEXT-PRIME 0) (NEXT-PRIME (1+ ENDING-VALUE)))
        (#:G251 19)) 
        ((> ENDING-VALUE #:G251)) 
    (FORMAT T "~a~t" ENDING-VALUE))

现在用来保存循环终值的变量的生成符号, #:G251

该符号名字由gensym函数生成,带有前缀 #:

注:当ending-value为展开式中的变量时,需要在代码中ending-value前加    逗号


在宏编写中只须遵循下面所概括的这些规则使你可以将许多漏洞预先被堵上:


1.除非有特殊理由,否则需要将展开式中的任何子形式放在一个位置上,使其求值顺序与宏调用的子形式相同。

2.除非有特殊理由,否则需要确保子形式仅被求值一次,方法是在展开式中创建变量来持有求值参数形式所得到的值,然后在展开式中所有需要用到该值的地方使用这个变量。

3.在宏展开期使用gensym来创建展开式中用到的变量名。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值