LINUX.ORG.RU

В Rust borrow checker обнаружены ошибки, существовавшие 11 лет и позволяющие обращаться к освобождённой памяти

 , ,


1

6

Проблема заключается в том, что требования borrow checker к времени жизни структуры можно обойти с помощью присвоения ковариантных полей.

В качестве примера кода, демонстрирующего ошибку, приведён (ссылка на Rust playground; для запуска нужно нажать кнопку “Run”):

struct Q<T, F: FnMut(&T)>(Option<T>, F);

impl<T, F: FnMut(&T)> Drop for Q<T, F> {
    fn drop(&mut self) {
        self.1(self.0.as_ref().unwrap())
    }
}

fn main() {
    let mut out: Option<&'static _> = None;
    {
        let l = vec![vec![1]];
        let mut q = Q(None, |s| {
            out = Some(*s);
        });
        q.0 = Some(&l);
    }
    println!("{:?}", out);
}

Обнаружение unsoundness (ошибок, приводящих к нарушению гарантий компилятора) в borrow checker’е Rust — не самое необычное явление. Но эта ошибка примечательна тем, насколько простым может быть код, вызывающий её. Для других ошибок приходилось сильно изворачиваться, чтобы избежать проверок компилятора.

Ссылка на issue: https://github.com/rust-lang/rust/issues/164250

★★

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

Не защищаю этот код, но вот вполне рабочий код на C++:

void *$(){[&](...){$:[](){({$:;_:[](){},""[+-(&&$<&&_)];});}();}([=](){});}

Я, конечно, разобрался что он делает, но после появления лямбд (и прочих новомодных плюшек) C++ сложно назвать простым и понятным языком.

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

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

Например, свой вклад в катастрофу Суперджета в Шереметьево внёс код на Обероне, который не позволяет при одновременной перезагрузке всех трёх вычислителей перейти в режим полёта минуя режим взлёта самолёта. Т.е. нельзя восстановить прежнее состояние, можно только начать программу полёта с чистого листа. Разработчики уверяли, что это ради безопасности и гарантированности поведения системы. С академической точки зрения оно так и есть. Но есть нюанс.

Bircoph ★★
()
Ответ на: комментарий от html-kernel

С не дырявые. Дырявые руки, которые на них пишут. Сам по себе язык (как стандарт) и компилятор (как его реализация) не гарантируют безопасности произвольных операций с памятью.

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

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

Утечка памяти не классифицируется, как проблема с безопасностью памяти.

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

И было показано, что Rust не может выполнить взятых на себя обязательств: https://habr.com/ru/articles/348344/ https://github.com/rust-lang/rust/issues/24292

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

Текущая история помягче, но всё же показывает, как в safe code получить use-after-free.

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

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

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

5 1/3 как в radix50

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

5 1/3 как в radix50

у Бабиджа и не его плюмяницы вроде как 5 хватит всем

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

всёж исходно Сяшка «не была запроектированна» - как заточенный по мере использования промежуточное представление - отличная вешь

как массовый кактус для прикладных программистов - это даже не саботаж

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

С не дырявые. Дырявые руки, которые на них пишут.

Ну не знаю, я тут узнал, что банальное a+b может быть UB. Дырявые, не дырявые, но нормальным язык после такого не назовешь. И на руки удобно списывать.

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

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

Я в курсе.

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

А как надо было? Чудес не бывает. Проблема с утечкой принципиально не исправляется, насколько я понимаю.

Текущая история помягче, но всё же показывает, как в safe code получить use-after-free.

Текущая история показывает баги компилятора. Их исправят и всё. В любом компиляторе багов хватает.

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

А как надо было? Чудес не бывает. Проблема с утечкой принципиально не исправляется, насколько я понимаю.

Раст реализует принцип: «Взял память - положи на место!» автоматически. С таким подходом не должно быть утечек памяти. В теории. На практике люди такие выдумщики, что смогли этот подход хакнуть. Я прекрасно понимаю, почему говорили, что с растом не возможны утечки памяти и почему оказалось, что возможны.

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

стоит просто помнить что именно ub от a+b было следствием удачного трэйдоффа Сяшки vs Паскаль_направление - ибо Сяшка отпимальна для микрикомпов с некотороым количестом сотен килобайт памяти озу - будь с памятью все 80ые так же трудно как и в70ые форт бы укоренился - покрай ней мере шитый код (который и в бспл был) стал бы достаточно массовым что бы застрять навсегда подобно sh

т.е. именно «скорость» a+b гарантирует ub

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

Что-то такое я себе и представлял: язык языком, а защита должна быть многоуровневой. Помню, Хаскель форсили со словами: там все настолько декларативно, что ошибки на нём невозможны. Потом оказалось, что возможны «ошибки другого рода».

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

А как надо было?

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

Нет же, полезли в ультимативные высказывания в духе «не возможно». Опыт показал, что возможно и покажет ещё не один раз. И даже на Leakpocalypse просто поменяли определение ошибки работы с памятью вместо признания ошибочности ультимативного подхода.

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

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

Option<&’static _>

Да это как раз читаемо ещё. А вот некоторый реальный C++20, где лямбда на лямбде и думаешь WTF?

Bircoph ★★
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария