2023.09.04
CVE-2021-39632 | A-202159709 | EoP | 高 | 11、12 |
---|
patch
Do not write past end of inotify event structure.

分析
//
static int inotify_cb(int fd, __unused uint32_t epevents) {
if (g_saved_input_cb == nullptr) return -1;
// The inotify will put one or several complete events.
// Should not read part of one event.
int event_len_int;
int ret = ioctl(fd, FIONREAD, &event_len_int); // 获取event长度
if (ret != 0) return -1;
if (event_len_int < 0) return -1;
size_t event_len = event_len_int;
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
if (!dir) {
return -1;
}
std::vector<int8_t> buf(event_len);
// 从inotify文件描述符中读取 event 数据到 buf 中
ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len));
if (r != event_len) {
return -1;
}
size_t offset = 0; // 【1】无符号数 offset = 0
// 循环处理inotify events
while (offset < event_len) {
// 【2】buf里存储的是events,根据偏移offset来访问每个event,一开始指向buf.data()开头
struct inotify_event* pevent = reinterpret_cast<struct inotify_event*>(buf.data() + offset);
// 【3】继续往下看之前,先看看inotify_event结构体:
// 【3.1】name字段是inotify_event结构体最后一个字段,char型数组,也就是该event的名字
// 【3.2】len字段是inotify_event结构体倒数第二个字段,无符号32位,含义为name数组的长度
// 【5】offset表示之前已经处理过的event的长度,sizeof(inotify_event) + pevent->len表示此次要处理的event的长度
// 如果此次要处理的event的长度加上之前处理过的长度的结果,大于总长度,则error
if (offset + sizeof(inotify_event) + pevent->len > event_len) {
// The pevent->len is too large and buffer will over flow.
// In general, should not happen, just make more stable.
return -1;
}
// 【4】每次增加的偏移offset的含义是结构体inotify_event的长度 + name的长度
offset += sizeof(inotify_event) + pevent->len;
// 【6】将name字段末尾设置为\0。当pevent->len等于buf剩余长度时,会越界写一个\0。举例如下:
// 假设name为event,即长度为5,那么:
// 4B 4B 4B 4B 5B
// | wd | mask | cookie | len | name |
// 0 3 4 7 8 11 12 15 16 20 21
// pevent->name[pevent->len] => pevent->name[5] => pevent[21], 越界写一个\0
// 所以patch写的是不要在inotify event 结构体之后再写数据(Do not write past end of inotify event structure.)
pevent->name[pevent->len] = '\0';
if (strncmp(pevent->name, "event", 5)) {
continue;
}
android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY));
if (dfd == -1) {
break;
}
if (!should_add_input_device(dfd, g_allow_touch_inputs)) {
continue;
}
// Only add, we assume the user will not plug out and plug in USB device again and again :)
ev_add_fd(std::move(dfd), g_saved_input_cb);
}
return 0;
}
/*
* struct inotify_event - structure read from the inotify device for each event
*
* When you are watching a directory, you will receive the filename for events
* such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.
*/
struct inotify_event {
__s32 wd; /* watch descriptor */
__u32 mask; /* watch mask */
__u32 cookie; /* cookie to synchronize two events */
__u32 len; /* length (including nulls) of name */
char name[]; /* stub for possible name */
};
小结
以后注意这种在结构体之后写数据的代码:pevent->name[pevent->len] = '\0'
。