Проверката е някъде нагоре: защо Торвалдс изтри __get_user() и __put_user() от Linux 7.4
Две функции, които вярваха, че някой друг вече е проверил. И защо ядрото спира да им вярва.
На 21 октомври Линус Торвалдс merge-на собствен клон, който маха от ядрото две функции с дълга история: __get_user() и __put_user(). Промяната влиза в Linux 7.4.
Новината обиколи LinkedIn с хаштагове и щит в зелено. Повечето постове казват едно и също: "по-безопасен достъп до паметта", "модерни алтернативи", "Торвалдс ползва AI". На ниво заглавие всичко това е вярно. Но пропуска най-интересното - защо тези функции бяха опасни, без в тях да има нито един бъг.
Защото проблемът не беше в кода им. Проблемът беше в едно предположение.
Границата между твоята програма и ядрото
Всяка програма на Linux живее в user space - собствено парче памет, в което прави каквото си иска, без да пречи на другите. Ядрото живее отделно, в kernel space, и има достъп до всичко.
Когато програмата иска нещо от ядрото - да прочете файл, да отвори мрежова връзка, да смени настройка - тя прави syscall и често подава адрес: "ето ти данните, тук са". Ядрото трябва да отиде на този адрес и да ги вземе. Или да остави отговора там.
И тук е капанът. Адресът идва от програмата. А програмата може да лъже. Може да подаде адрес, който сочи не към собствената ѝ памет, а към паметта на ядрото. Ако ядрото послушно чете и пише там, атакуващият току-що е получил ключовете за сервиза.
Образ от работилницата: клиентът казва "частта е в багажника". Преди да бръкнеш, проверяваш, че багажникът е на неговата кола, а не на съседната.
Какво правеха двете функции
get_user() взима една проста променлива от user space и я пренася в ядрото. put_user() прави обратното - записва една стойност от ядрото към паметта на програмата. Преди да пипнат каквото и да е, двете проверяват, че адресът наистина принадлежи на user space. Тази проверка се казва access_ok().
Версиите с двойна долна черта правят същото, но без проверката. Те приемат, че някой вече я е направил.
Навремето това имаше смисъл. Проверката струваше измеримо време, а ако една функция чете десет полета от една и съща структура, защо да проверява десет пъти? Проверяваш веднъж в началото, после ползваш бързия вариант. Логично. Бързо. И с часовников механизъм вътре.
"Проверката е някъде нагоре"
Ето как изглежда класическият модел, опростен:
Code:
/* ред 40 */
if (!access_ok(uptr, sizeof(*uptr)))
return -EFAULT;
/* ... 80 реда по-късно ... */
if (__get_user(val, uptr)) /* разчита на проверката от ред 40 */
return -EFAULT;
Днес работи. После идва някой и прави рефакторинг. Мести проверката в друга функция. Добавя бърз път, който я прескача. Копира парчето от ред 120 в нов драйвер, без ред 40.
Кодът се компилира. Тестовете минават. С нормални данни всичко е наред. Чак когато някой подаде злонамерен адрес, се оказва, че проверката отдавна я няма. Компилаторът не предупреждава. Нищо не предупреждава - __get_user() няма как да знае дали някой изобщо е проверил.
Това не е бъг в функцията. Това е бъг, който функцията прави възможен. А в ядрото разликата между двете е академична - и двете завършват с дупка.
Торвалдс говори против тези функции от години. В merge съобщението ги нарича изцяло исторически и казва, че вече никога не са правилният избор.
Проверка, направена някъде другаде, е проверка, на която просто вярваш.
Какво идва на мястото им
Отговорът е скучен и точно затова е добър: get_user() и put_user(), с вградена проверка. Проверката и употребата са на едно и също място. Никакъв рефакторинг не може да ги раздели.
Аргументът със скоростта е мъртъв. Тук LinkedIn постовете бъркат посоката - старите функции бяха по-бързите, не новите. Просто днес разликата е пренебрежима: проверката на обхвата е сравнение на адрес с граница, наносекунди.
За кода, който наистина прави много достъпи подред, Томас Глайкснер миналата година добави scoped user access regions. Отваряш регион с проверка, правиш всички достъпи вътре, затваряш го. Проверката е структурно вързана за блока - не може да "остане някъде нагоре". Според Торвалдс точно тези helpers премахват всяко оправдание за старите функции.
Важно за мащаба: изчистени са генеричният код и x86. В някои други архитектури остават следи заради нетривиални случаи. При ARM64 тези функции и без това вече водят към безопасните версии. Тоест не е "проблемът е решен навсякъде". Това е голяма стъпка, не финал.
Торвалдс и AI - без хайпа
Да, вярно е. Торвалдс напоследък сам праща повече кръпки, частично с помощта на Claude Code, и при това премахване е имало AI участие.
Но погледни каква е работата. Да конвертираш стотици места от един модел към друг е механично, повтарящо се, и все пак изисква внимание на всяко място. Точно там AI инструментът е полезен - като добре заточен инструмент на тезгяха.
Решението какво да се махне, защо, и кои остатъци са приемливи засега - това е негово. Човекът, който трийсет години е най-известният критик на лош код в света, не дава волана на машината. Ползва я. Проверява я. Подписва се отдолу.
Моето правило е същото: не приемам код, който не мога да обясня на глас. Изглежда и в ядрото важи.
Едно честно уточнение: този текст е подготвен с помощта на Claude - същия модел, за който става дума. Затова тук държа на фактите от merge-а, а не на рекламата около него.
Какво значи това за теб
- Linux 7.4 още не е излязъл. Тече merge window-ът, целта е края на декември, но стабилното издание може да се изплъзне до средата на януари 2027.
- Debian 13 (trixie) е на ядро 6.12 LTS. Утре нищо не се променя на машината ти.
- Като обикновен потребител нямаш какво да правиш. Ползата е невидима: по-малко шанс бъдещ бъг в някой драйвер да стане дупка в сигурността.
- Ако компилираш out-of-tree модули (DKMS драйвери за Wi-Fi, VPN, видеокарти), стар код с тези функции най-вероятно няма да се компилира на x86 със 7.4. Провери изходния код на модула предварително.
Code:
# в директорията с изходния код на модула
grep -rnE "__(get|put)_user\(" ./
Ако излезе нещо - преди да се оплачеш на разработчика, провери дали няма по-нова версия. Сериозните проекти ще са го оправили преди 7.4 да стигне до дистрибуциите.
От гаража
Изпускателната система има десетки връзки. Фланци, скоби, заварки, муфи. Правилото е просто: всяка връзка, която си пипнал, се тества. Дори да си сигурен. Дори предната да е тествана.
Класическата грешка е друга. Сменяш един фланец и приемаш, че тестът от миналата седмица още важи. Нали тогава всичко беше наред. А газовете вече намират пътя си към купето. Въглеродният окис не мирише и не предупреждава.
Това е __get_user() в метал. Проверката е била направена. Някога. Някъде нагоре. За система, която вече не е същата.
Торвалдс не измисли нов тест. Той просто направи така, че тестът и връзката да не могат да се разделят. В ядрото това се казва добър дизайн. В гаража се казва занаят.
Доверието не се наследява. Проверява се там, където се използва.
Накратко
- __get_user() и __put_user() пропускаха проверката на адреса и разчитаха, че е направена по-рано.
- Опасността не е бъг в тях, а разстоянието между проверката и употребата.
- Заместват ги get_user()/put_user() и scoped user access regions - проверка и достъп на едно място.
- Изчистени са генеричният код и x86; в други архитектури има остатъци.
- AI е помогнал с механичната част. Решенията са на Торвалдс.
- За Debian 13 нищо не се променя сега. Ако ползваш DKMS модули - провери ги преди 7.4.
Ключови думи: Linux 7.4, Linux kernel, Линус Торвалдс, __get_user, __put_user, get_user, put_user, access_ok, user space, kernel space, scoped user access, сигурност на ядрото, Claude Code, AI в програмирането, DKMS
Източници: Phoronix - "Linus Torvalds Eliminates Unsafe __get_user() & __put_user() Functions In Linux 7.4" (21.10.2026), phoronix.com/news/Linus-Kills-Get-Put-User-Linux; commit e497310b4ffb "uaccess: Provide scoped user access regions"; LinuxCompatible - преглед на merge window-а за Linux 7.4.
toni@dtgarage:~$ whoami
Тони Ангелчовски | Ексклузивно за DTGaraGe
toni@dtgarage:~$ cat LICENSE
Копирането и препубликуването без разрешение не е позволено.
toni@dtgarage:~$ ./support.sh
Подкрепи DTGaraGe и независимото техническо съдържание
toni@dtgarage:~$ █
Тони Ангелчовски | Ексклузивно за DTGaraGe
toni@dtgarage:~$ cat LICENSE
toni@dtgarage:~$ ./support.sh
toni@dtgarage:~$ █