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'