代码精进之路
范学雷
前 Oracle 首席软件工程师,Java SE 安全组成员,OpenJDK 评审成员
38234 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
结束语 (1讲)
代码精进之路
15
15
1.0x
00:00/00:00
登录|注册

34 | 数组和集合,可变量的安全陷阱

分享文章给朋友或同事
欢迎分享发现
数组的使用安全问题检查
变量的拷贝有浅拷贝和深拷贝两种形式;可变量的浅拷贝无法解决竞态危害的威胁
可变量局部化是解决可变量竞态危害的一种常用办法
可变量的传递存在竞态危害的安全风险
浅拷贝与深拷贝
支持实例拷贝
防范漏洞的方法
安全漏洞案例分析
JavaScript引擎实现数组join()方法的内部C代码
JavaScript代码示例
一起来动手
小结
麻烦的集合
可变量局部化
TOCTOU竞态危害
可变量的安全问题
不可变量的好处
数组和集合,可变量的安全陷阱

该思维导图由 AI 生成,仅供参考

在前面的章节里,我们讨论了不少不可变量的好处。在代码安全中,不可变量也减少了很多纠葛的发生,可变量则是一个非常难缠的麻烦。

评审案例

我们一起看下这段 JavaScript 代码。
var mutableArray = [0, {
toString : function() {
mutableArray.length = 0;
}
}, 2];
console.log("Array before join(): ", mutableArray);
mutableArray.join('');
console.log("Array after join(): ", mutableArray);
调用 mutableArray.join() 前后,你知道数组 mutableArray 的变化吗?调用 join() 前,数组 mutableArray 包含两个数字,一个函数 ({10, {}, 20})。调用 join() 后,数组 mutableArray 就变成一个空数组了。这其中的秘密就在于 join() 的实现,执行了数组中 toString() 函数。而 toString() 函数的实现,把数组mutableArray 设置为空数组。
下面的代码,就是 JavaScript 引擎实现数组 join() 方法的一段内部 C 代码。
static JSBool
array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
JSString *sepstr, CallArgs &args) {
// snipped
size_t seplen;
// snipped
StringBuffer sb(cx);
if (!locale && !seplen && obj->isDenseArray() &&
!js_PrototypeHasIndexedProperties(cx, obj)) {
// Elements beyond the initialized length are
// 'undefined' and thus can be ignored.
const Value *beg = obj->getDenseArrayElements();
const Value *end =
beg + Min(length, obj->getDenseArrayInitializedLength());
for (const Value *vp = beg; vp != end; ++vp) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
if (!vp->isMagic(JS_ARRAY_HOLE) &&
!vp->isNullOrUndefined()) {
if (!ValueToStringBuffer(cx, *vp, sb))
return false;
}
}
}
// snipped
}
这段代码,把数组的起始地址记录在 beg 变量里,把数组的结束地址记录在 end 变量里。然后,从 beg 变量开始,通过调用 ValueToStringBuffer() 函数,把数组里的每一个变量,转换成字符串。
我们一起来看看第一段代码,是怎么在这段 join() 实现的 for 循环代码里执行的。
vp 指针初始化后,指向数组的起始地址;
如果 vp 的地址不等于数组的结束地址 end,就把数组变量转换成字符串,然后变换 vp 指针到下一个地址 。我们一起来看看这段代码是如何操作数组 mutableArray 的:
a. 数组的第一个变量是 0。0 被转换成字符,vp 指针换到下一个地址;
b. 数组的第二个变量是 toString() 函数。toString() 函数被调用后,就会把 mutableArray 这个数组设置为空数组,vp 指针换到下一个地址;
c. 数组的第三个变量本来应该是 2。但是,由于数组在上一步被置为空数组,数组的第三个变量的指针指向数组外地址。
由于数组已经被设置为空数组,原数组的地址可能已经被其他数据占用,访问空数组外的地址就会造成内存泄漏或者程序崩溃。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了可变量的安全问题及解决方法,通过具体的代码案例和技术分析,对读者进行了深入的技术启发和警示。文章首先通过JavaScript代码示例展示了可变数组在join()方法中的安全漏洞,并分析了可变量的安全威胁,指出了TOCTOU竞态危害的常见形式。随后,介绍了通过拷贝将可变变量局部化来防范这种漏洞的方法,包括支持实例拷贝的方法,如静态实例化、拷贝构造函数、公开拷贝方法等,并讨论了浅拷贝和深拷贝的区别。此外,还探讨了集合类的浅拷贝和深拷贝的问题,以及数组的拷贝效率等相关内容。总的来说,本文通过深入的技术分析,提出了解决可变量安全问题的方法,并强调了可变量的传递存在竞态危害的安全风险,可变量局部化是解决可变量竞态危害的一种常用办法,以及可变量的浅拷贝无法解决竞态危害的威胁。读者可以通过本文了解到如何识别和解决可变量的安全问题,以及在编码中如何处理数组和集合的安全隐患。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《代码精进之路》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(5)

  • 最新
  • 精选
  • 彩色的沙漠
    老师您好,调用 join() 前,数组 mutableArray 包含两个数字,一个函数 ({10, {}, 20})。对这个(10,{},20)不理解,变化前数组mutableArray应该是包含两个数字,一个对象(0,{},2)?

    作者回复: 👍,这是我的失误。原来的例子我用的是(10, {}, 20),后来觉得{0, {}, 2}更直观些,就换成了{0, {}, 2}。 描述部分漏掉了,没改过来。 多谢多谢!

    2019-05-31
    4
  • 天佑
    toctou不能用线程同步解决,线程同步解决的是有序执行的问题,解决可变量的根本问题是变量局部化,隔离可变因素,老师我理解的对否。 实际场景中,可变类应该很多,动不动就拷贝,好像不现实,是不是只要传递的可变量都要局部化啊?单线程环境下应该不用考虑吧。

    作者回复: 这样理解没问题,不过有时候线程同步也可以起到阻断变化的使用。 后面我们还会讲代码的边界,什么时候拷贝,什么时候不拷贝,我们稍后讨论。

    2019-03-22
    3
  • ifelse
    对于集合来说,我们该怎么解决可变量的竞态危害这个问题呢?最主要的办法,就是不要把集合使用在可能产生竞态危害的场景中。--记下来
    2022-07-30归属地:北京
    1
  • 刚毅坚卓
    小白想问一下浅拷贝和深拷贝是应用于哪些场景呢
    2022-05-01
  • aoe
    学到了新知识: TOCTOU(time-of-check time-of-use)竞态危害
    2021-12-29
收起评论
显示
设置
留言
5
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部