ядро Linux, буферы пространства пользователя, access_ok и wait создают условия гонки? - PullRequest
2 голосов
/ 31 июля 2011

В следующем коде (реализация read для драйвера char) возможно ли изменение записей TLB MMU во время wait_event_interruptible, так что __put_user вызывает исключение, даже если access_ok выполнено успешно?

Можно ли заблокировать пользовательский буфер таким образом, чтобы он оставался действительным на время запроса?

Повторила бы проверка access_ok после возврата wait_event_interruptible, чтобы сделать это безопасным?

ssize_t mydriver_pkt_read( struct file* filp, char __user* const buff, size_t count, loff_t* offp )
{
  struct mydriver_pkt_private* priv;
  volatile unsigned short* iobase;
  unsigned next;
  char __user* p = buff;

  if (count <= 0) return -EINVAL;
  if (!access_ok(VERIFY_WRITE, buff, count)) return -EFAULT;

  priv = (struct mydriver_pkt_private*)filp->private_data;
  iobase = priv->iobase;

  next = priv->retained;
  if ((next & PKTBUF_FLAG_NOTEMPTY) == 0) {
    next = ioread16(iobase);
    if ((next & PKTBUF_FLAG_NOTEMPTY) == 0) { // no data, start blocking read
      iowrite16(1, iobase); // enable interrupts
      if (wait_event_interruptible(priv->wait_for_ringbuffer, (priv->retained & PKTBUF_FLAG_NOTEMPTY)))
          return -ERESTARTSYS;
      next = priv->retained;
    }
  }

  while (count > 0) {
    __put_user( (char)next, p );
    p++;
    count--;
    next = ioread16(iobase);
    if ((next & PKTBUF_FLAG_STARTPKT) || !(next & PKTBUF_FLAG_NOTEMPTY)) {
      priv->retained = next;
      return (p - buff);
    }
  }

  /* discard remainder of packet */
  do {
    next = ioread16(iobase);
  } while ((next & PKTBUF_FLAG_NOTEMPTY) && !(next & PKTBUF_FLAG_STARTPKT));
  priv->retained = next;
  return (p - buff);
}

Эксклюзивный открытый код:

int mydriver_pkt_open( struct inode* inode, struct file* filp )
{
  struct mydriver_pkt_private* priv;

  priv = container_of(inode->i_cdev, struct mydriver_pkt_private, cdevnode);

  if (atomic_cmpxchg(&priv->inuse, 0, 1))
    return -EBUSY;

  nonseekable_open(inode, filp);

  filp->private_data = priv;
  return 0;
}

1 Ответ

3 голосов
/ 31 июля 2011

Если у вас не хранится семафор mm_sem, таблицы страниц могут изменяться в любое время (другими потоками того же процесса, отображающими страницы из другого процессора, или из-за исключений из процессов восстановления страниц). Вам даже не нужно спать; это может произойти, даже если у вас отключено приоритетное прерывание, до тех пор, пока может появиться прерывание перемотки TLB. И это может произойти, даже если прерывания отключены, если у вас SMP-машина, как вы можете иногда видеть обновления таблицы страниц, отображаемые даже без явного сброса TLB.

access_ok() только проверяет, что диапазон адресов не перекрывается с пространством ядра . Так что он ничего не говорит о том, разрешают ли доступ к записям таблицы страниц, но его результат также не меняется, даже если вы блокируете. Если доступ запрещен, __put_user() вернет -EFAULT, что должно быть передано в пространство пользователя (т. Е. Ошибка здесь с -EFAULT).

Обратите внимание, что единственная разница между put_user() и __put_user() заключается в том, что put_user() также выполняет проверку access_ok(). Так что, если вы используете его в цикле, то, вероятно, сделайте один access_ok() раньше времени и используйте __put_user().

...