articles
articles copied to clipboard
chromium issues
chromium issues
主要记录我阅读或者复现过的一些浏览器漏洞,如果复现复杂会单独开文章,个人对这方面积累有限,难免会存在错误,欢迎批评指正。
阅读这些漏洞不一定是为了深挖什么,然后挖掘相关的漏洞,更多的是为了自己通过对以前漏洞熟悉,然后去了解浏览器的各方面知识,浏览器涉及的内容,实在太多了,直接入手会让人很迷茫,了解漏洞,然后去了解修复的原理,该部分的一些设计模式,代码编写习惯等等。
其中很多术语,感觉中文翻译后可能会存在歧义,我一般会使用原始英文术语
1174551
这个漏洞是直接fuzz
出来的,poc
没有权限拿到,所以直接来看修复代码,修复的review 2653810
看修复代码前,首先需要理解javascript
两个概念,ArrayBuffers
和SharedArrayBuffers
,我在后面提供了一个相关知识链接,可以通过阅读它来做简要的了解。
主线程和
web workers
属于不同的线程,对于javascript
来说,不同的线程不会共享内存
简单点说,ArrayBuffers
就是javascript
提供的一个能够存储原始数据的对象,对于存储的数据,它没有作任何的标识,就是单纯的二进制数据,如果需要类型化,可能需要typed arrays
。ArrayBuffers
不支持线程间共享,如果想实现线程共享,比如主线程和web workers
线程想同时访问它,只能通过postMessage进
行transferring memory
,但是有个缺点就是transfer
之后,原始线程不能再访问该数据,已经被detached
,可以参见mdn Transferable objects
Transferrable objects* are commonly used to share resources that can only be safely exposed to a single JavaScript thread at a time. For example, an
ArrayBuffer
is a transferrable object that owns a block of memory. When such a buffer is transferred between threads, the associated memory resource is detached from the original buffer and attached to the buffer object created in the new thread. The buffer object in the original thread is no longer usable because it no longer owns a memory resource.
而SharedArrayBuffers
则弥补了这部分的缺点,postMessage
之后线程间依然可以读写该数据,但需要注意读写锁的控制。
再回头来看1174551
的review
代码,主要修复在这里
Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
Handle<JSArrayBuffer> array_buffer) {
if (array_buffer->is_shared()) {
if (!delegate_) {
ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer);
return Nothing<bool>();
}
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
Maybe<uint32_t> index = delegate_->GetSharedArrayBufferId(
v8_isolate, Utils::ToLocalShared(array_buffer));
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
WriteTag(SerializationTag::kSharedArrayBuffer);
WriteVarint(index.FromJust());
return ThrowIfOutOfMemory();
}
->>>>>>
if (!array_buffer->is_detachable()) { // 因为使用的是ArrayBuffer,所以需要增加对是否detached的判断
ThrowDataCloneError(
MessageTemplate::kDataCloneErrorNonDetachableArrayBuffer);
return Nothing<bool>();
}
<<<<<<-
1166012
clusterfuzz
测试出来的,基础信息是这样的
Crash Type: Heap-buffer-overflow READ 8
Crash Address: 0x6040002d4288
Crash State:
ash::ScrollableShelfView::ShouldCountActivatedInkDrop
ash::ScrollableShelfView::CreateScopedActiveInkDropCount
ash::ScrollableShelfView::CreateScopedActiveInkDropCount
修复的描述是这样的
[Shelf] Handle the edge case of no tappable icon
It is a crash reported by fuzz. Based on ScrollableShelfView::CalculateTappableIconIndices(), `first_tappable_app_index_` and `last_tappable_app_index_` may be both illegal. This edge case should be handled by the function where the crash occurs.
在shelf
中没有tappable icon
,理解这个含义,需要大概知道shelf
具体怎么组成的,然后如果没有应用的情况下,会怎么呈现。
具体参见:https://support.google.com/chromebook/answer/3113576?hl=en
大概了解之后,来跟一下调用栈
如果没有修复代码
bool ScrollableShelfView::ShouldCountActivatedInkDrop(
const views::View* sender) const {
bool should_count = false;
// When scrolling shelf by gestures, the shelf icon's ink drop ripple may be
// activated accidentally. So ignore the ink drop activity during animation.
if (during_scroll_animation_)
return should_count;
<---- 修复代码
if (first_tappable_app_index_ == -1 || last_tappable_app_index_ == -1) {
// Verify that `first_tappable_app_index_` and `last_tappable_app_index_`
// may be both illegal. In that case, return early.
DCHECK_EQ(first_tappable_app_index_, last_tappable_app_index_);
return false;
}
----->
// The ink drop needs to be clipped only if |sender| is the app at one of the
// corners of the shelf. This happens if it is either the first or the last
// tappable app and no arrow is showing on its side.
if (shelf_view_->view_model()->view_at(first_tappable_app_index_) == sender) {
should_count = !(layout_strategy_ == kShowButtons ||
layout_strategy_ == kShowLeftArrowButton);
} else if (shelf_view_->view_model()->view_at(last_tappable_app_index_) ==
sender) {
should_count = !(layout_strategy_ == kShowButtons ||
layout_strategy_ == kShowRightArrowButton);
正常调用栈应该是这样
ash/shelf/scrollable_shelf_view.cc
-> ScrollableShelfView::ShouldCountActivatedInkDrop
-> shelf_view_->view_model()->view_at(first_tappable_app_index_)
shelf_view
定义在
ash/shelf/scrollable_shelf_view.h
-> ShelfView* shelf_view_ = nullptr;
ShelfView
定义在
ash/shelf/shelf_view.h
-> const views::ViewModel* view_model() const { return view_model_.get(); }
-> std::unique_ptr<views::ViewModel> view_model_;
-> T* view_at(size_t index) const { return static_cast<T*>(ViewAtBase(index)); }
ViewAtBase
函数ui/views/view_model.h
View* ViewAtBase(size_t index) const {
check_index(index);
return entries_[index].view;
}
check_index
位于ui/views/view_model.h
void check_index(size_t index) const { DCHECK_LT(index, entries_.size()); }
其中Entries
using Entries = std::vector<Entry>;
到这里整个调用栈就清晰了,但是根据这个调用栈再加上自己的测试,感觉不会想clusterfuzz
那样成功触发崩溃,因为check_index
中-1
不会小于size()
,在这里就会返回。因为没有poc
没有办法具体分析,这个issue
大概就这样了。