Я разработал простую линейную дисциплину, использующую 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 в исходном коде. Если бы он работал так, как Цыварев предположил, что во время компиляции это намного облегчило бы отслеживание.
Простите мое невежество, но ... это правильно: 'unsigned char outbuffer [128] = {0};' Разве это не определяло бы 1-байтовый массив вместо 128-байтового массива? Не имеет значения, если вы не инициализируете этот массив и оставьте его определение только как 'unsigned char outbuffer [128];'? –
Я не думаю, что это имеет значение.Основываясь на вашем комментарии, я попробовал оба способа проверить свое здравомыслие, и он потерпел неудачу в обоих направлениях. – cigarman