2015-12-14 2 views
3

Я разработал простую линейную дисциплину, использующую v4.0.5 ядра Linux, работающую под управлением Mint Linux.Linux Kernel Line Discipline copy_from_user

Структура tty_ldisc_ops выглядит следующим образом:

static struct tty_ldisc_ops my_ldisc = { 
    .owner   = THIS_MODULE, 
    .magic   = TTY_LDISC_MAGIC, 
    .name   = "my_ldisc", 
    .open   = my_open, 
    .close   = my_close, 
    .read   = my_read, 
    .write   = my_write, 
    .ioctl   = my_ioctl, 
    .poll   = my_poll, 
    .receive_buf = my_receive, 
    .write_wakeup = my_wakeup, 
}; 

Модуль добавляется через insmod my_lkm.ko. Я знаю, что он добавлен правильно, поскольку я использовал printk для его указания и могу видеть сообщение через dmesg. Кроме того, при запуске приложение mypace использует ioctl, и я также проверил, что работает через printk.

Проблема в том, что в my_write copy_from_user всегда возвращает ненулевое значение, указывающее, что оно каким-то образом сработало.

Вот my_write():

static ssize_t my_write(struct tty_struct *tty, 
         struct file *file, 
         const unsigned char *buf, 
         size_t nr) 
{ 
    int error = 0; 
    unsigned char data[MAX]; //MAX is 256 

    if(!my_tty) { 
     return -EIO; 
    } 
    if(nr > MAX) {  //too big  
     return -ENOMEM; 
    } 
    error = copy_from_user(data,buf,nr);  
    printk("copy_from_user returned %d\n",error); 
    //here, error is always equal to nr 
    //(which is 12 in my example application) 
    if(error==0) { 
     printk("success\n"); //never get here 
     return nr; 
    } 
    return error; 
} 

Из того, что я исследовал, copy_from_user в конечном итоге вызывает pa_memcpy, который делает проверку указателей используется. Эта проверка не работает, но я не могу понять, почему. Я не знаю, как * buf и данные перекрываются или могут вызвать ошибку.

Выход из uname -a: Linux mint-linux 4.0.5-040005-generiC#201506061639 SMP Sat Jun 6 16:40:45 UTC 2015 UTC x86_64 x86_64 x86_64 GNU/Linux

Фрагмента приложения является пользовательским пространством:

#define OPEN_FLAGS  (O_RDWR|O_NONBLOCK) 
int main(int argc, char **argv) 
{ 
    int fd=-1; 
    int bytes_written= 0; 
    char device="/dev/ttyUSB0"; 
    unsigned char outbuffer[128]={0}; 
    fd=open(device,OPEN_FLAGS); 
    //set baud rate, etc., switch to my_ldisc (using N_MOUSE) 
    outbuffer[0]=0x01; 
    outbuffer[1]=0x02; 
    outbuffer[2]=0x03; 
    outbuffer[3]=0x04; 
    outbuffer[4]=0x05; 
    outbuffer[5]=0x06; 
    outbuffer[6]=0x07; 
    outbuffer[7]=0x08; 
    outbuffer[8]=0x09; 
    outbuffer[9]=0x0A; 
    outbuffer[10]=0x0B; 
    outbuffer[11]=0x0C; 
    bytes_written=write(fd,outbuffer,12); 
    while(true) { 
     //... 
     sleep(1); 
    } 
} 

Кроме того, любой доступ BUF в my_write приводит к нестабильности в VM. Даже следуя примеру драйвера терминала в O'Reilly Linux драйвера книги, как это:

printk(KERN_DEBUG "%s - ", __FUNCTION__); 
for(i=0;i<nr;i++) 
{  
    printk("%02x ",buf[i]); 
} 
printk("\n"); 

Следуя совету Цыварев, я напечатал указатель в пространстве приложения пользователя и модуль ядра. Они были разными, что означало, что я должен напрямую обращаться к входящему буферизу. Я использовал printf("%p\n",outbuffer);, чтобы сделать это в пространстве пользователя и эквивалентном printk в пространстве ядра.

Итак, замедление и тестирование модуля по строкам помогли мне исправить исходную проблему, которая, как оказалось, была ошибкой в ​​приложении пользовательского пространства.

FWIW, компилятор никогда не предупреждал об использовании __user в исходном коде. Если бы он работал так, как Цыварев предположил, что во время компиляции это намного облегчило бы отслеживание.

+0

Простите мое невежество, но ... это правильно: 'unsigned char outbuffer [128] = {0};' Разве это не определяло бы 1-байтовый массив вместо 128-байтового массива? Не имеет значения, если вы не инициализируете этот массив и оставьте его определение только как 'unsigned char outbuffer [128];'? –

+0

Я не думаю, что это имеет значение.Основываясь на вашем комментарии, я попробовал оба способа проверить свое здравомыслие, и он потерпел неудачу в обоих направлениях. – cigarman

ответ

5

В отличие .write к методу struct file_operations, который принимает указатель на пользователя данных, .write метод struct tty_operations принимает указатель на данные ядра, и эти данные shold быть доступны через обычные методы, такие как memcpy или даже непосредственно.

Современное ядро ​​использует атрибут __user для отметки данных пользовательского пространства, и этот атрибут проверяется (во время компиляции) при доступе к данным. Таким образом, включение предупреждений компилятора покажет использование данных с неправильным происхождением.

+0

Благодарим вас за ответ. Однако любой доступ к переменной buf не выполняется. Попытка получить доступ к buf напрямую заставляет машину блокироваться. Еще одно замечание: этот же модуль, используя декларацию __user для buf, отлично работает с помощью copy_from_user в v3.11. – cigarman

+0

См. Мои правки. Добавление или удаление __user не имеет значения, и любое объявление не создает предупреждений компилятора. – cigarman

+0

Возможно, в других частях вашего модуля есть ошибки. Отсутствие предупреждений компилятора кажется подозрительным: ваша функция 'my_write' не возвращает значение в конце, и компилятор должен обязательно предупредить об этом. Вы устанавливаете флаги компилятора через 'ccflags-y: = -Wall' или другим способом? – Tsyvarev