Подробности

[В начало]

Проблема в реализации № L0011

Краткое описание

drivers/hid/hidraw.c: Двойной mutex_lock

Подробное описание

В драйвере drivers/hid/hidraw.c в функции hidraw_read может быть двойной mutex_lock: Путь:

  • Строка 50: начало первой итерации цикла "while(ret==0)"
  • Строка 52: первый вызов mutex_lock
  • Внутренний цикл "while (list->head == list->tail)" не изменяет состояния mutex.
  • Если начинается вторая итерация цикла "while(ret == 0)" в строке 50, то второй раз вызывается mutex_lock в строке 52.
  • Вторая итерация цикла "while(ret==0)" возможна, если локальная переменная ret не меняется в строке 94: ret+=len - т.е. len==0; Переменная len может быть равна нулю, если hidraw_read вызывается с параметром count==0 или list->buffer[list->tail].len == 0.
      45        struct hidraw_list *list = file->private_data;
      46        int ret = 0, len;
      47        char *report;
      48        DECLARE_WAITQUEUE(wait, current);
      49
      50        while (ret == 0) {
      51
      52                mutex_lock(&list->read_mutex);
      53
      54                if (list->head == list->tail) {
      55                        add_wait_queue(&list->hidraw->wait, &wait);
      56                        set_current_state(TASK_INTERRUPTIBLE);
      57
      58                        while (list->head == list->tail) {
      59                                if (file->f_flags & O_NONBLOCK) {
      60                                        ret = -EAGAIN;
      61                                        break;
      62                                }
      63                                if (signal_pending(current)) {
      64                                        ret = -ERESTARTSYS;
      65                                        break;
      66                                }
      67                                if (!list->hidraw->exist) {
      68                                        ret = -EIO;
      69                                        break;
      70                                }
      71
      72                                /* allow O_NONBLOCK to work well from other threads */
      73                                mutex_unlock(&list->read_mutex);
      74                                schedule();
      75                                mutex_lock(&list->read_mutex);
      76                                set_current_state(TASK_INTERRUPTIBLE);
      77                        }
      78
      79                        set_current_state(TASK_RUNNING);
      80                        remove_wait_queue(&list->hidraw->wait, &wait);
      81                }
      82
      83                if (ret)
      84                        goto out;
      85
      86                report = list->buffer[list->tail].value;
      87                len = list->buffer[list->tail].len > count ?
      88                        count : list->buffer[list->tail].len;
      89
      90                if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
      91                        ret = -EFAULT;
      92                        goto out;
      93                }
      94                ret += len;
      95
      96                kfree(list->buffer[list->tail].value);
      97                list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
      98        }
      99out:
     100        mutex_unlock(&list->read_mutex);
     101        return ret;
    

    Компонент

    linux-kernel 2.6.31

    Принято

    http://lkml.org/lkml/2009/10/12/101
    commit

    Статус

    Исправлено в ядре 2.6.35

    [В начало]