LINUX.ORG.RU

[Kernel] Oops при memcpy


0

1

В своём модуле, пытаюсь модифицировать данные.
Модифицируемые данные находятся по адресу 0xf640b898.
При попытке любой записи в эту память вылетает Oops.
С надписью, ошибка в такой-то функции(которая меняла память)
BUG: unable to handle kernel paging request at 0xf640b898 Oops: 0003 [#1] SMP

Раньше всегда помогало,
mm_segment_t fs=get_fs(); set_fs(KERNEL_DS);
А сейчас чтото, при таких действиях этот упс всегда выскакивает...как мне это дело поправить???

Deleted

При этом замечу, память в которую пытаюсь произвести запись.... kmap ..
И данные оттуда читаются.

Deleted ()
Ответ на: комментарий от Deleted

Втыкни кусок кода и дамп полностью. Так не понятно.

sn1ln ()
Ответ на: комментарий от Deleted

>При этом замечу, память в которую пытаюсь произвести запись.... kmap

(0xf640b898-0xC0000000)/1024/1024 ~ 868Mb

ttnl ★★★★★ ()
Ответ на: комментарий от Deleted

> В своём модуле, пытаюсь модифицировать данные.

как?

ниже упоминается set_fs(), это наврдит на подозрения...

Модифицируемые данные находятся по адресу 0xf640b898.


ты хотя бы архитектуру упомянул, иначе этот адрес не
очень интересен.

BUG: unable to handle kernel paging request at 0xf640b898 Oops: 0003 [#1] SMP


ну вот не может оно именно так выглядеть ;)

ладно, я могу ошибиться, но это похоже на fault_in_kernel_space().
тогда, судя по address, это 32bit.

error_code == 0x3 == PF_PROT | PF_WRITE. пишешь в read only
memory?

еще раз, могу ошибаться, внимательно не смотрел.

idle ★★★★★ ()
Ответ на: комментарий от idle

Архитектура intel x86 32bit
Модуль тестирую на VirtualBox

Насчет read only memory не знаю, возможно, как это посмотреть? + если всётаки она only read то как ,где проставить, возможность запись в неё?

Deleted ()
Ответ на: комментарий от Deleted

Только что снова посмотрел вывод проги.
Сейчас выполнение моего модуля пошло чуть по другому пути.чем раньше.
При первой ветке программы, когда я начинал данную тему мой модуль делал kmap страницы памяти, и работал с ней. И далее выскакивала описанная мной ошибка.
Теперь модуль выполняется по другой ветке.. без явного kmap и выскакивает OOps всё на тойже функции.
Вот её код.

inline bool my_func(char *data_ptr, char *inject_data_ptr, unsigned short inject_data_len)
{
char *ptr = data_ptr;
unsigned short len = inject_data_len;


/* Производим модификацию */
printk(«inject_data_ptr = %p\n», inject_data_ptr);
printk(«inject_data_len = %u\n», inject_data_len);
printk(«data_ptr = %p\n», data_ptr);
print_data(data_ptr, inject_data_len);
print_data(inject_data_ptr, inject_data_len);

//return ERR; // если вернуть управление тут, то упса не будет
printk(«Перед модификацией памяти\n»);
memcpy(data_ptr, «B», 1); // вызывает упс, и дальше прога не идёт

memcpy(data_ptr, inject_data_ptr, inject_data_len);// вызывает упс, и дальше прога не идёт


memset; // тоже вызывает упс


return OK;
};



Вот код print_data

void print_data(char *data, unsigned long len)
{
if(data == NULL || len == 0) return;

printk(«>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n»);
int ii=0;
printk(«\n»);
for(ii=0; ii< len; ii++)
{
printk(«%c», *(data+ii));
}

printk(«\n\n»);

printk(«<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n»);

return;
};


А вот выводимая инфа в логе..

inject_data_ptr = f4b8fc38
inject_data_len = 8
data_ptr = b782f01c


AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<



New data
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Перед модификацией памяти
BUG: unable to handle kernel paging request at b782f01c
IP: [<f7fc2648>] my_func+0x1d8/0x1e0 [MY_module]
*pde = 34a91067 *pte = 443e9025
Oops: 0003 [#1] SMP


Как уже понятно AAA...AAA Это содержимое памяти.
New data - новые данные которые пробую записать поверх ААА..

Deleted ()
Ответ на: комментарий от Deleted

> *pde = 34a91067 *pte = 443e9025

вот, с этого надо было начинать. я же говорил, что оно
не может выглядеть так, как было в начальном посте.

из чего видно, что _PAGE_RW в pte нет, read only mapping.

смущает меня PAGE_USER... что-то недоброе ты делаешь.

и ты не ответил, при чем здесь set_fs().

idle ★★★★★ ()
Ответ на: комментарий от idle

idle
Очень сильно боюсь показаться глупым..но как по *pde = 34a91067 *pte = 443e9025 Вы определили, что «что _PAGE_RW в pte нет» и что память read only ?

Как мне модифицировать данную память?


Насчёт set_fs, если не ошибаюсь, оно убирает проверки на модификацию памяти...или я не прав?

Насчёт vdso/vsyscall ничего не могу сказать.. потому-что не знаю что это.
Хотя могу сказать, что осуществляется модификация памяти приложения работающего в USER_SPACE...
Опятьже, если не ошибаюсь... то по идее из модулей, можно делать всё что угодно, и иметь доступ к всему оборудованию системы..хоть к чипу bios... И раз в системе 4гб то в этот диапазон пишется инфа различных процессов, ну и знаю адрес страницы , куда пишет какой-то процесс, я могу спокойно в неё ..писать..всё что угодно... Или я не прав?(кстати, это я сейчас и пытаюсь сделать)

Задаю часто глупые вопросы..т.к. самоучка, и всё узнавал методом проб и ошибок.

Deleted ()
Ответ на: комментарий от sn1ln

скорее всего тебе надо получить физическую память и ее править. И скорее всего снять mprotect() какой-нибудь..

sn1ln ()
Ответ на: комментарий от Deleted

>BUG: unable to handle kernel paging request at b782f01c

b7... - это вроде как стек или даже ниже? Надо как минимум copy_to_user использовать, а не memcpy.

Murr ★★ ()
Ответ на: комментарий от Murr

Murr при вызове copy_to_user ничего не происходит, память не модифицируется...и упса нет.

sn1ln

скорее всего тебе надо получить физическую память и ее править. И скорее всего снять mprotect() какой-нибудь..


Как это сделать?

Deleted ()
Ответ на: комментарий от Deleted

>Murr при вызове copy_to_user ничего не происходит, память не модифицируется...и упса нет.

Ну правильно. Указатель ведь кривой, по нему ничего не запишешь. Но так хоть не падает. :-)

А откуда вообще в модуль попал сей замечательный указатель? И в правильном ли вы контексте (того же процесса, из которого получен указатель) вы пытаетесь записать туда данные?

Murr ★★ ()
Ответ на: комментарий от Deleted

> Насчёт set_fs, если не ошибаюсь, оно убирает проверки на модификацию памяти.

Если не знаешь - смотри исходный код и читай документацию

ну и знаю адрес страницы ,

Откуда адрес знаешь? Он тебе в сне приснился?

я могу спокойно в неё ..писать..всё что угодно.

Когда, куда именно и откуда ты собираешься «куда-то» писать?

anonymous ()
Ответ на: комментарий от Murr

В модуль он попал через установленный мной перехватчик на системной функции inet_sendpage из которой был взят указатель на страницу памяти, и смещение.. далее через kmap был получен адрес страницы, затем прибавлено смещение внутри страницы..и вооля) получен указатель на данные.
Далее этот указатель доходит до функции в которой происходит сбой.
Приложение которое вызвало inet_sendpage не догадывается о том, что данные модифицируются.

Deleted ()
Ответ на: комментарий от anonymous

anonymous
«Когда, куда именно и откуда ты собираешься „куда-то“ писать? »
Задача проста, в любой момент времени..писать данные из модуля, в любую страницу памяти.

Deleted ()
Ответ на: комментарий от Deleted

Очень сильно боюсь показаться глупым..но как по *pde = 34a91067 *pte = 443e9025 Вы определили, что «что _PAGE_RW в pte нет» и что память read only ?


pte = 0x443e9025
_PAGE_RW = 0x2

pte & _PAGE_RW = 0x0

ttnl ★★★★★ ()
Ответ на: комментарий от Deleted

>далее через kmap был получен адрес страницы

То есть все-таки это kmap выдал вам адрес 0xb7... < TASK_SIZE=0xc00..., по которому вы пытаетесь модифицировать данные? Я бы на вашем месте перепроверил код. Не может kmap выдавать адреса в диапазоне userspace (разве что вы используете какую-нибудь экзотику а-ля 4/4 и это не userspace, но тогда адрес слишком большой).

Murr ★★ ()
Ответ на: комментарий от Murr

Почему kmap не может выдавать адрес в userspace ?
Модифицируемые данные как раз должны находиться именно там.
Если коротко то приложение работающее на прикладном уровне, -> выделяет память под свои данные, они находятся в userspace , далее эти данные, приложение отправляет в ядро.. мой пункт назначения inet_sendpage , в данную функцию. ядро передаёт адрес страницы памяти с данными и смещение внутри страницы.
Т.к. вместо этой функции срабатывает мой перехватчик, то я получаю все данные направляемые в inet_sendpage.
И получаю адрес данных приложения, которое выполнило запрос к данной функции. А они лежат в пользовательском пространстве.
Данные там, где им и место)

Deleted ()
Ответ на: комментарий от Deleted

featurelles, что-то ты какую-то фигню делаешь. Как kmap() вызываешь? Мне кажется так тебе никто не поможет, пока ты нормальный код не приведешь, а не одни printk() которые не понятно зачем ты написал. IHMO.

sn1ln ()
Ответ на: комментарий от Deleted

>Почему kmap не может выдавать адрес в userspace ?

На это есть много причин. Но в вашем случае важен тот факт, что kmap может выдать либо постоянный виртуальный адрес страницы в пространстве памяти ядра, либо временный адрес страницы в пространстве памяти ядра (в том случае, если страница принадлежит к т.н. highmem zone). Существование виртуального адреса в пространстве памяти текущего процесса для запрошенной страницы не имеет значения для kmap. Теоретически, в определенных обстоятельствах этим можно было бы воспользоваться, но вообще это будет намного накладнее, чем создать временную проекцию для страницы в памяти ядра.

Murr ★★ ()
Ответ на: комментарий от sn1ln

>Мне кажется так тебе никто не поможет, пока ты нормальный код не приведешь

Присоединяюсь к этому мнению. :-)

Murr ★★ ()
Ответ на: комментарий от sn1ln

sn1ln Пока вам расписывал код, заметил такую фигню. Если записать в память в самом перехватчике..то никаких упсов вообще не будет, и память будет модифицирована! (Кстати, при модификации записываемые мной данные в память, записались(а точнее затёрли) в файл который передавался в функцию send_page ). Конечно не файл передавался функции, а адрес страницы памяти. Но смысл в том, что после модификации данных в PAGE , данные затёрли собой содержимое файла, и сохранились на жёстком диске.
Смысл в том, что я сейчас проверил поэтапно модуль вплоть до функции перед my_func (в которой всегда упс выскакивает) она большая и буду её смотреть завтра...видимо косяк в ней. Буду завтра его искать, и отпишу что за трабла была.

Кстати, поясните мне, почему при модификации PAGE данные записываются на диск? Тоесть когда пишу в память, система помечает страницу памяти как «грязную» или «модифицированную» и сохраняет данных на хард??

Deleted ()
Ответ на: комментарий от idle

> в том-то и дело, что нет. address > TASK_SIZE,

и это как раз странно.


ааааа блин. я невнимательно читал второй oops, там адрес
другой и он < TASK_SIZE. я почему-то решил, что это он про
все тот же полный oops говорит.

тогда PAGE_USER понятен.

idle ★★★★★ ()
Ответ на: комментарий от Deleted

> Насчёт set_fs, если не ошибаюсь, оно убирает проверки

на модификацию памяти


разумеется, нет.

т.к. самоучка


я тоже.

всё узнавал методом проб и ошибок.


вот плохой это метод. что это значит вообще? ты, просто
перебираешь варианты, пока оно не заработает. точнее,
пока тебе не покажется, что оно работает.

я вот вспомнил... это же я тебе сказал про set_fs() когда
у тебя были проблемы c copy_*_user(kernel_addr). вместо
того, чтобы _разобраться_, почему set_fs() нужен в данном
случае, ты просто «методом проб и ошибок» пришел к выводу,
что set_fs() нужен всегда для записи в память.

без обид ;) но так нельзя.

idle ★★★★★ ()
Ответ на: комментарий от Deleted

> В модуль он попал через установленный мной перехватчик

на системной функции inet_sendpage из которой был взят

указатель на страницу памяти



ох, ну все понятно.

этот вот page*, это же не просто in-kernel копия данных.
это и _есть_ данные. тебе еще повезло, было бы хуже,
если бы ты смог их изменить, результат (вообще говоря)
мог бы потом и в файл сохраниться.

посмотри sys_sendfile(). в частности,
__generic_file_splice_read(). оно же работает непосредственно
со страницами в page cache, а ты туда писать собрался, очень
мило.

я очень давно не смотрел в этот код, мог и ошибиться где-то.
но одно точно: НЕ НАДО пытаться модифицировать содержимое
страницы в inet_sendpage(), хуже будет.

idle ★★★★★ ()
Ответ на: комментарий от idle

>этот вот page*, это же не просто in-kernel копия данных. это и _есть_ данные. тебе еще повезло, было бы хуже, если бы ты смог их изменить, результат (вообще говоря) мог бы потом и в файл сохраниться.

Хорошее замечание.

Murr ★★ ()
Ответ на: комментарий от idle

idle __generic_file_splice_read() - уже смотрел, читал, изменял)))

Как уже выше отписал, в page могу свободно писать)..это радует.

Насчёт set_fs , когда вы мне как раз про copy_to_user говорили, я поискал на форуме что именно set_fs делает, кажется тогда в одной теме прочитал..а может мне и в тойже теме ответили, что она убирает проверку того, куда копируются данные, тоесть чтоб можно было копировать данные из и в пользовательское пространство.
Так у меня к вам вопрос... что именно делает set_fs и для чего ( в исходниках смотрел, но не понятно )

Deleted ()
Ответ на: комментарий от Deleted

>тоесть чтоб можно было копировать данные из и в пользовательское пространство.

Нет. Эта функция нужна для того, чтобы можно было передать __kernel указатель туда, где ожидают __user указатель. Функции ядра обычно используют copy_from_user/copy_to_user для копирования данных userspace<->kernelspace. Эти функции проверяют, чтобы пользовательский процесс по ошибке или злонамеренно не передал ядерный указатель.

Но в некоторых ситуациях данные, которые ожидаются из userspace, формируются самим ядром. В этой ситуации set_fs может отключить проверку параметров copy_to/from_user на принадлежность userspace.

Murr ★★ ()
Ответ на: комментарий от Murr
 use case 1)

threadA:
int a = 1;
syscall_inc(&a)

kernel:
do_syscall_inc(int *a) { 
        int v;
        rc = copy_from_user(&v, a, sizeof(int)); // получили 
        if (rc) return -EFAULT; 
        v++; // увеличили 
        rc = copy_to_user(a, &v, sizeof(int)); // сохранили 
        return rc ? -EFAULT : 0;
}

use case 2)

threadA: 
int *a = 0xfffffff0; 
syscall_inc(a)

kernel:
do_syscall_inc(int *a) { 
        int v; 
        rc = copy_from_user(&v, a, sizeof(int)); // ошибка - указатель в памяти ядра 
        if (rc) return -EFAULT; 
        v++; 
        rc = copy_to_user(a, &v, sizeof(int)); 
        return rc ? -EFAULT : 0; 
}

use case 3)

kernelthreadA: 
int a; 
syscall_inc(&a) 

kernel: do_syscall_inc(int *a) { 
        int v;
        rc = copy_from_user(&v, a, sizeof(int)); // ошибка - указатель в памяти ядра 
        if (rc) return -EFAULT; 
        v++; 
        rc = copy_to_user(a, &v, sizeof(int)); 
        return rc ? -EFAULT : 0; 
}

use case 4)

kernelthreadA: 
int a; 
set_fs(KERNEL_DS); 
syscall_inc(&a);
 set_fs(USER_DS); 

kernel: 
do_syscall_inc(int *a) { 
        int v;
        rc = copy_from_user(&v, a, sizeof(int)); // проверка отключена, скопировали
        if (rc) return -EFAULT; 
        v++; // увеличили 
        rc = copy_to_user(a, &v, sizeof(int)); // проверка отключена, сохранили
        return rc ? -EFAULT : 0;
} 
Murr ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.