почему не работает access_ok для этого ioctl

РЕДАКТИРОВАТЬ: У меня пока нет хорошего ответа на вопрос, почему я получаю здесь ошибку... Итак, позвольте мне немного перефразировать это. Нужна ли мне вообще проверка verify_area()? В чем смысл этого? Я проверил тот факт, что моя структура успешно передается этому ioctl, я думаю просто удалить ошибочную проверку, но я не на 100% знаю, что там нужно делать. Мысли? ЗАВЕРШИТЬ РЕДАКТИРОВАНИЕ

Я работаю над обновлением некоторых старых драйверов ядра Linux, и при тестировании одного из них я получаю ошибку, которая кажется мне странной. Вот так:

У меня есть простой вызов ioctl в пользовательском пространстве:

Config_par_t    cfg;
int ret;
cfg.target = CONF_TIMING;
cfg.val1   = nBaud;
ret = ioctl(fd, CAN_CONFIG, &cfg);

Config_par_t определен в файле can4linux.h (это драйвер CAN, поставляемый с uCLinux):

typedef struct Command_par {
  int cmd;          /**< special driver command */
  int target;           /**< special configuration target */
  unsigned long val1;       /**< 1. parameter for the target */
  unsigned long val2;       /**< 2. parameter for the target */
  int error;            /**< return value */
  unsigned long retval; /**< return value */
} Command_par_t ;

Что касается ядра, функция ioctl вызывает verify_area, которая является процедурой сбоя:

long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    void *argp;
    long retval = -EIO;
    Message_par_t Message;
    Command_par_t Command;
    struct inode *inode = file->f_path.dentry->d_inode;
    argp = &Message;

    Can_errno = 0;

    switch(cmd) {
      case CONFIG:
        if( verify_area(VERIFY_READ, (void *) arg, sizeof(Command_par_t))) {
          return(retval); 
        }

Теперь я знаю, что verify_area() больше не используется, поэтому я обновил его в файле заголовка с помощью этого макроса на access_ok:

#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
#define verify_area(type, addr, size) access_ok(type, addr, size)
#endif

Я работаю на платформе x86, поэтому я уверен, что фактически вызываемый макрос access_ok() находится в /usr/src/linux/arch/x86/include/asm/uaccess.h, как определено здесь:

#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))

#define __range_not_ok(addr, size)                  \
({                                  \
    unsigned long flag, roksum;                 \
    __chk_user_ptr(addr);                       \
    asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"     \
      : "=&r" (flag), "=r" (roksum)             \
      : "1" (addr), "g" ((long)(size)),             \
        "rm" (current_thread_info()->addr_limit.seg));      \
   flag;                                \
})

Я думаю, мне кажется, что это должно работать. Любые идеи, почему я получаю 1 возврат из этой verify_area, если проверка? Или какие-либо идеи о том, как я могу сузить проблему?

if( verify_area(VERIFY_READ, (void *) arg, sizeof(Command_par_t))) {

person Mike    schedule 07.09.2012    source источник


Ответы (1)


Макрос access_ok возвращает 0, если блок недействителен, и ненулевое значение, если он может быть действительным. Итак, в вашем тесте, если блок действителен, вы немедленно возвращаете -EIO. Судя по тому, как все выглядит, вы можете отрицать результат access_ok, что-то вроде:

if (!access_ok(...))
person cnicutar    schedule 07.09.2012
comment
э, извините, мое последнее утверждение было неверным. Результат access_ok: 1... небольшая опечатка, которая имеет большое значение... Я обновлю свой вопрос. - person Mike; 10.09.2012
comment
Хорошо, я смог найти ответ, который помог прояснить это для себя, и да, вы правы, все работает. Этот патч-файл помог мне понять: -if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || против нового: +if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||. Обратка перевернулась. - person Mike; 10.09.2012