LINUX.ORG.RU

Продемонстрирована возможность разработки частей Linux на Rust

 , ,


4

9

Французский программист написал статью, в которой рассмотрел возможность переписывания ядра Linux на Rust.

В статье отмечено, что данный язык хорошо подходит для системного программирования, будучи достаточно низкоуровневым и при этом лишённым многих недостатков C, и уже используется для написания новых ОС. Однако автор не считает создание ОС с нуля перспективным для серьёзного применения, и последовательный перенос отдельных частей Linux на Rust для решения различных проблем безопасности кажется ему более целесообразным.

В качестве «Proof of Concept» была приведена реализация системного вызова, содержащая вставки на Assembler внутри unsafe-блоков. Код компилируется в объектный файл, не связанный с библиотеками и интегрируемый в ядро во время сборки. Работа производилась на основе исходного кода Linux 4.8.17.

>>> Статья



Проверено: Shaman007 ()
Последнее исправление: sudopacman (всего исправлений: 5)
Ответ на: комментарий от lefsha

Столько текста, чтобы признаться, что не осилили ссылки?

RazrFalcon ★★★★★
()

При желании можно и на go писать модули ядра.

Можно даже ежа в жопу затолкать, но вот зачем?

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

Напоминает, как религиозные фанатики переворачивают всё с ног на голову.

В данном фрагменте используется unsafe, и это значит что программист уверен что он делает с памятью, и теперь уже это его задача — следить, чтобы в строку не накинули чего-нибудь плохого.

Без ансейфа этот сомнительный финт не провернуть потому что это не безопасно.
Вы придумали какой-то свой раст и недовольны им. Тотальная безопасность памяти гарантируется везде кроме ансейфа. Для того чтобы в логике не возникало всяких heartbleed, как выше заметили: проблема там была в обычном сишном коде, а не в ассемблерных трюках.

А ансейф на то и ансейф чтобы давать полную свободу работы на низком уровне, локализовать небезопасный код и явно его выделить.

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

А за тотальной безопасностью идите в языки с GC, где подобные вещи вообще не сделать никак, и которые не подходят для низкоуровнего программирования.

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

Да и что это за фукция main, которая ничего не возвращает? Каким образом это должно работать в ядре или с ядром? Программа обязана возвращать хотя бы 0, если написавший не в состоянии обыграть все случаи ошибок.

Из позитива, то что мне нравится тип данных, когда можно указывать разрядность. Правда в С сейчас такое тоже есть типа int32_t, но очень много программ с обычным int и потом мы удивляемся, когда все ломается при переходе от 32бит к 64бит.

Скрытие от программиста разрядности используемых данных это плохо. Тем более в низкоуровневом языке типа С.

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

Дополнительно в С плохо, что указатель после создания дин. массива может быть сдвинут и тем самым - усе пропало. Но если объявить его const то уже фиг присвоишь динамически. Вместо const должен быть freeze и unfreeze.

Но народу проще придумать еще 100 языков, вместо того чтобы починить один.

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

Ссылки и указатели по сути одно и то же, просто ссылки несут немного больше информации.

Если написано fn(a: *mut i32, b: *mut i32) то значит в эту функцию разрешается засунуть любые два указателя, в том числе и нулевые.

Если написано fn(b: &mut i32, a: &mut i32) то это те же самые два указателя плюс условия:

1) a != null

2) b != null

3) a != b

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

pftBest ★★★★
()
Последнее исправление: pftBest (всего исправлений: 1)
Ответ на: комментарий от mersinvald

Мне не нужна тотальная безопасность. По этому я выбираю С. Мне нужна простота написания программ и отсутствие запретов.

Rust не обеспечивает безопасность как Вы сами сказали, тогда нафига он нужен, если на низком уровне он слишком многословен. Причем без необходимости. Все эти mut, владения и передача прав от лукавого.

Этим должен заниматься компилятор и указывать какие проблемы он нашел. Все массивы могут быть помещены в аквариум - более крупный массив забитый определенными числами. Если во время отладки происходи обращение или запись вне внутреннего массива, то это легко отловить какая функция это сделала.

Сам же язык менять не нужно.

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

Скрытие от программиста разрядности используемых данных это плохо. Тем более в низкоуровневом языке типа С

Вот тут то ты и спалился. Размер инта зависит от ширины адреса в целевой архитектуре, потому что инт изначально использовался для адресной арифметики.
Странно топить за си и не знать такой элементарщины. В Rust для адресации используют isize и usize, которые так же зависят от ширины адреса.

Но народу проще придумать еще 100 языков, вместо того чтобы починить один.

Есть идеи как починить язык из 70х, нихрена не вписывающийся в современные реалии разработки, не поломав обратную совместимость?
Или ты из тех, кто считает, что пол века стагнации в области ЯП для системной разработки — это хорошо?

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

Я никогда не писал на ЯП, в которых есть NULL, уважаемый анонимный осилятор. А об упомянутых сужу по генерируемому ими контенту.

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

Все массивы могут быть помещены в аквариум - более крупный массив забитый определенными числами

Еще и тотальное непонимание работы аллокатора и оперативной памяти вообще. Со сферическими аквариумами в вакууме вам к машинам тьюринга с бесконечными лентами, а мы живем в убогом реальном мире.

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

Чтобы переопределить кусок памяти из строки в функцию Мы в одном случае берем указатель - выше, хотя казалось бы имя строки само по себе указатель. А во втором случае используем целую функцию для этого... Зачем?

Не совсем. В первом примере используется массив байт, в C это был бы const char code[] = "..."; и чтобы взять ссылку/указатель, действительно нужно использовать &это — «просто так, сами собой», как в C, массивы к указателям не преобразуются.

Во втором примере у нас стразу ссылка на срез (slice) байт, а мы хотим получить указатель — можно либо сделать code as *const u8, либо использовать метод .as_ptr(), который примерно так и реализован: https://doc.rust-lang.org/src/core/slice.rs.html#320-322

Снаружи мы это крестим, молимся, поливаем святой водой, чтобы работало и запускаем...

Прямо как в C.

При этом формально мы сами стреляем себе в ногу, потому как никто не знает что там в этой строке и содержание этой строки всегда можно изменить. В итоге от «безопасности» остался пшик, но за то мы научились молиться перед тем как идти стреляться.

Зачем все это?

Если язык напирает, что он безопасный, то он должен запрещать подобное. Если позволяет как С, то не должно быть никаких сложностей и танцев с бубном, чтобы достичь результата!

Он и запрещает подобное. Преобразовывать какие-то байты в строку — небезопасно, так что в safe Rust это сделать нельзя. В unsafe Rust — можно, поэтому вокруг каста mem::transmute() нужен блок unsafe.

Так где здесь сложности и танцы с бубном? В &?

Вот именно в этом проблема Rust!

Нет.

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

Я никогда не писал на ЯП, в которых есть NULL

Либо это ложь, либо у Вас, мягко говоря, сомнительный уровень экспертизы в вопросах срачей C-C++-Rust

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

Вот это радость... Ссылка (по русски псевдоним=кличка) вдруг стала указателем. Тогда как говорится нафига вводить новую сущность или новое название того, что уже есть?

И оказывается в случае ссылки нельзя передавать 2 одинаковых параметра в функцию... Т.е. если функция умножает 2 числа, то я не могу передать два одинаковых числа, чтобы получить квадрат?

Нафиг такой язык!!!

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

Ну ты блин совсем, ты или тролль или реально не знаешь где связь между «два числа» и «две ссылки на два числа»

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

Т.е. если функция умножает 2 числа, то я не могу передать два одинаковых числа, чтобы получить квадрат?

В примере показаны изменяемые ссылки. Изменять одну и ту же память по двум разным ссылкам — небезопасно потому что ты поломаешь память по a, получишь не пойми что и с b словишь легфолт или UB.

Если память менять не надо, а нужно вернуть квадрат (функция вообще чистая) — никаких проблем

fn mul(a: &i32, b: &i32) -> i32 {
    a * b
}

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 2)
Ответ на: комментарий от mersinvald

Это Вы не поняли. Речь была об отладке, а не нормальном режиме! В отладке можно использвать спец аллокатор с увеличенными границами нежели чем требуемые для массива.

Сама фраза - непонимание работы аллокатора это бред. Этих аллокаторов полномы полно. И каждый может написать свой. Тот же Гугл написал свой аллокатор и ничего ему не помешало.

Вы какой из сотни аллокаторов имеете ввиду?

Я бы Вам по советовал почитать или послушать доклад Андрея Александреску по поводу аллокаторов. А то у Вас выходит, что и он ничего не понимает.....

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

Вот тут то ты и спалился. Размер инта зависит от ширины адреса в целевой архитектуре, потому что инт изначально использовался для адресной арифметики.

Бгг. int - размер регистра, а не адреса. Потому в x86_64 int - 32бит, а адрес - 64.

Использование везде размерности, когда явно предполагается быстрая работа с небольшими числами — глупость. Потому и используют int, а не int_32t. Например, вы можете представить язык с количеством ключевых слов > 2*16 ? И зачем для цикла по ключевым словам использовать жесткую размерность счётчика? Чтобы тормозило на другой архитектуре, где это не оптимально?

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

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

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

Речь не о переводе! И слово reference тоже неверное слово в данном случае. Подразумевается всегда «второе имя той же самой переменной» Это псевдоним, кличка итд. Вообщем другое имя и больше ничто. Каждое имя указывает на один и тот же объект. Вы говорите Лондон, а другие говорят London. И очевидно, что Лондон это не reference для London.

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

Речь была об отладке, а не нормальном режиме!

О как, а тесты давно стали гарантировать отсутствие ошибок? Или вы в прод будете отладочные сборки запускать?

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

Подразумевается всегда «второе имя той же самой переменной»

Кем подразумевается?

Ссылка отличается от указателя семантически, на уровне ЯП, внизу это тот же указатель, и в C++ и в Rust.

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

Ну опять же, наличие тестов на отладочных сборках не гарантирует отсутствие ошибок. Значительно уменьшает их количество, да, но совершенно не спасает от неучтенных в тестах случаев.

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

Зачем встревать в срачи, в которых ничего не понимаешь?

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

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

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

Вы реально не понимаете. Функция вычисляет произведение 2х чисел. Квадрат будет только в одном единственном случае. Никто не меняет функцию из-за того, что ее входные данные могут быть одинаковыми или разными.

В общем из того, что Вы говорите Rust пользоваться нельзя. И вероятность, что он взлетит близка к 0.

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

Дык я согласен, просто уточнил про «аквариум» :)

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

И зачем для цикла по ключевым словам использовать жесткую размерность счётчика?

Для цикла по ключевым словам и не надо использовать, более того, это моветон.
Для циклов по данным с индексацией надо использовать size_t в си, usize в Rust, а лучше итераторы.

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

Вы реально не понимаете. Функция вычисляет произведение 2х чисел. Квадрат будет только в одном единственном случае. Никто не меняет функцию из-за того, что ее входные данные могут быть одинаковыми или разными.

Я тебе и привел пример функции «mul», в переводе «произведение», которая принимает две ссылки на два произвольных числа. Любых, даже если это одно и то же число. О каком частном случае ты говоришь?

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

из того, что Вы говорите

Нет, из того что Вы понимаете.
Срачи на ЛОРе — так себе источник знаний из-за недопонимания сторон. Чтобы сделать выводы, читайте документацию к Rust, там подробно описаны механизмы владения-одалживания, типы ссылок и какие ограничения где накладываются.

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

Вы сами попадаете в капкан. Если никто включая компилятор и отладчик не может узнать перейду ли я за пределы массива, то безопасные языки, которые не дают перейти _за_ пределы массива невозможны. По скольку они очевидно возможны, то легко узнать перешли ли границу массива или нет. Просто потому, что у массива есть адрес и есть макс. смещение. Если число вне этого диапазона, то мы явно за границей. проверять можно с помощью большего массива и его порчи специально для отладки. Послушайте Александреску...

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

А оригинальный пример с

fn mul(a: &mut i32, b: &mut i32) -> i32
О том, что если какой-то Вася Пупкин в своей библиотеке написал какую-то дичь, которая ломает в процессе работы функции значения по адресам a и b, то в Си вы об этой уязвимости узнаете только в рантайме, а в Rust Вы

  • увидите из сигнатуры, что функция меняет входные значения
  • компилятор не даст коду скомпилироваться, если это изменение может испортить Ваш код.

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

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

Только вот C это такой язык в котором не известен размер массива.

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

Тебе блин три раза уже сказали

Тут не могут быть одинаковые:

fn(a: &mut u32, b: &mut u32)

Тут могут быть одинаковые:

fn(a: & u32, b: & u32)

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

Послушайте Александреску...

Который написал D с итераторами, исключающими проблемы выхода за пределы массива и явными рантаймовыми проверками при индексации?
Ну да, в Rust то же самое. Послушайте Александреску...

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

Вот тут то ты и спалился. Размер инта зависит от ширины адреса в целевой архитектуре, потому что инт изначально использовался для адресной арифметики.

Вы вообще читать умеете? Я именно об этом и говорю, что различная разрядность для int и есть огромный недостаток C.

Тип данных должен зависеть ИСКЛЮЧИТЕЛЬНО от данных, а не от какой-то архитектуры. Это и есть проблема. Если я использую int32_t, то я знаю какое число могу туда положить. И если мне мало, я беру больше, а если много - меньше.

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

Вы вообще в курсе как много программ пришлось переделывать под 64 бит..? А все из-за таких идиотизмов.

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

О Боже. Вы сначала читайте, потом пытайтесь понять и только когда поняли - отвечайте....

Вы говорили, что я не знаю как работает один единственный аллокатор! Какой именно Вы не уточнили.

Я Вам посоветовал послушать доклад Александреску именно на тему аллокаторов и как оно должно работать!!!

Каким боком тут D?????

Для ленивых вот линк https://www.youtube.com/watch?v=LIb3L4vKZ7U

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

Да, но вот ведь незадача: эти идиотизмы нельзя убрать из Си из-за обратной совместимости.

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

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

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

Вы понимаете, что такой аллокатор, который вы описали, будет по меньшей мере неэффективен, будет страдать от фрагментации и потребует постояной упаковки, что убьет производительность напрочь, когда нужна работа с кучей?
И выключение его в релизных сборках НИКАК не скажется на безопасности в лучшую сторону, вот вообще никак.

Не говоря о том что для данных на стеке аллокатора нет от слова совсем, кто там будет следить в релизных сборках за buffer overrun?

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)
Ответ на: комментарий от lefsha

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

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

Я прекрасно понял, что где-то могут, а где-то нет. Из самой строчки это не (оче)видно. Значит это плохая концепция. Я не должен читать тонну документации, чтобы узнать о таком очень не очевидном поведении!

Зачем это было сделано? Какова польза? Чем тут мешает С?

В С гораздо проще:

int64_t mul(int32_t a, int32_t b);

void mul(int32_t* a, int32_t* b);

int64_t mul(const int32_t* a, const int32_t* b);

void mul(int32_t* const a, int32_t* const b);

int64_t mul(const int32_t* const a, const int32_t* const b);

Что нового и лучшего придумал Rust чем выше приведенные варианты.При этом тут нет бредового ограничения, что а не может быть равно b.

lefsha
()
Последнее исправление: lefsha (всего исправлений: 2)
Ответ на: комментарий от lefsha

Прекрасное подтверждение, что можно совершенно по разному написать такую простую вещь и в итоге совсем непонятно что все это такое.

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

В одном случае мы определяем функцию как принимающую указатели, а в другом - выше как ссылки. При этом в обоих случаях передаем указатели на переменные во время исполнения.

Ссылки — это указатели, для которых Rust даёт определённые гарантии. То, что в моём примере используются указатели, это всего лишь моя хотелка.

Чтобы переопределить кусок памяти из строки в функцию Мы в одном случае берем указатель - выше, хотя казалось бы имя строки само по себе указатель. А во втором случае используем целую функцию для этого... Зачем?

Во первых не строки, в обоих случаях массив байтов, а не строки. Если массив статический то можно взять ссылку. В моём случае универсальный вариант, подходит и для статического массива и для среза.

Снаружи мы это крестим, молимся, поливаем святой водой, чтобы работало и запускаем...

Правильно, программист, если пишет что-то в unsafe, то должен говорить компилятору и другим.

Парни, я знаю, что я делаю. Всё хорошо, я профи.

Что можно перевести на нормальный язык:

Парни, если что-то упадёт, то знайте, это из-за последней правки unsafe.

Вот так и живём.

При этом формально мы сами стреляем себе в ногу, потому как никто не знает что там в этой строке и содержание этой строки всегда можно изменить. В итоге от «безопасности» остался пшик, но за то мы научились молиться перед тем как идти стреляться.

Да, в Rust если возможность отстрелить себе ногу, для того, чтобы была возможность создавать zero-cost абстракции с безопасным интерфейсом.

Зачем все это?

За тем, что если в команде макаки, то для их крейта написано #[deny(unsafe_code)], что запрещает писать unsafe, а все профи могут писать с unsafe в другом крейте, который используют макаки.

Если язык напирает, что он безопасный, то он должен запрещать подобное. Если позволяет как С, то не должно быть никаких сложностей и танцев с бубном, чтобы достичь результата!

Вот именно в этом проблема Rust!

  • Язык напирает, что он существует в двух испостасях, безопасный и небезопасный.
  • Язык напирает, что в безопасном всё чётко и безопасно, но не всегда оптимально.
  • Язык напирает, что не оптимально можно обойти с помощью небезопасного.
anonymous
()
Ответ на: комментарий от mersinvald

Именно по этому я и сказал, что мне нравятся типы данных в Rust. Я не отрицаю, что у него есть хорошие стороны.

Проблема как всегда в том, что людям мало изменить немножко. Мало сделать пару штрихов. Как некоторым девушкам им надо наложить столько краски на лицо, что уже не красиво, а уродливо.

Это очень важный момент - где и как остановится. Я физически не люблю языки с GC. Это просто ужасно. Когда сложная программа, да еще связанная с железным оборудованием вдруг зависает на неопределенное время и сделать ничего нельзя.

Но в одном Go очень прав. Они сами себе во время сказали - стоп! Они оставили язык простым лаконичным и понятным. Изобретателям Rust нужна таблетка от жадности.

Чтобы их язык стал популярным он должен прежде всего обойти по популярности остальные языки. Я не вижу, что это произойдет. Я бы лично скорее выбрал Go, а там где нельзя С.

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

Из самой строчки это не (оче)видно. Значит это плохая концепция. Я не должен читать тонну документации, чтобы узнать о таком очень не очевидном поведении!

Это очевидно для того кто прочел хотя бы о базовых концепциях. Знания Си ты в молоком матери впитал, наверное, и никогда не учил язык, просто его знаешь и всё очевидно?

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)
Ответ на: комментарий от lefsha

Ну вот есть у вас структура

typedef struct {
char* d;
size_t len;
} string;

Есть у вас такой код.

char* a = s1->d + 15;//s1 = указатель на string
append(s1,s2)//Требует реаллокации. Или, возможно, требует, но только иногда, чтобы было веселее отлаживать
*a; 
Вы можете сколько угодно говорить, что такое допускают только кретины, а вот вы-то нормальный программист, но так все говорят, а идиотские ошибки с переполнением буфера или висячими ссылками как были, так и есть.

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

Что нового и лучшего придумал Rust

Rust гарантирует отсутсвие проблем с памятью. И эти строгие правила - это то что позволяет ему это сделать.

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

Rust гарантирует отсутсвие проблем с памятью.

Именно! Хм... О чём это я?

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

Вы понимаете, что такой аллокатор, который вы описали, будет по меньшей мере неэффективен, будет страдать от фрагментации и потребует постояной упаковки, что убьет производительность напрочь, когда нужна работа с кучей?

Вы видели, что я говорил об отладке программы??? Причем это нужно только если нельзя написать анализатор кода и поймать ошибку там. А анализаторов кода полно. Значит случаев когда придется работать со спец аллокатором немного. Иногда просто хватить valgrind.

И выключение его в релизных сборках НИКАК не скажется на безопасности в лучшую сторону, вот вообще никак.

В релизных сборках никаких отладочных аллокаторов быть не должно! Я не знаю на чем Вы работаете. Но мой Linux, написанный на С ни разу не падал из-за каких-то неведомых проблем. Есть хорошая поговорка - не чини работающую систему.

Мы установили, что на Rust принципиально возможно написать небезопасную программу. На этом весь спор можно завершить. С помощью Rust можно точно так же прекрасно выстрелить себе в ногу. Нужно лишь выучить еще один язык.... А смысл?

Не говоря о том что для данных на стеке аллокатора нет от слова совсем, кто там будет следить в релизных сборках за buffer overrun?

Если я чего-то в чем-то понимаю, то на стеке выделяется место для статичных данных. Кол-во выделенной памяти и размер стека можно сравнить как 2 числа. Это элементарно.

lefsha
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.