LINUX.ORG.RU

Атака на пакет arrayref Rust

 , , ,


0

5

Атака на цепочку поставок: вредоносный код в crates.io через пакет arrayref

20 августа 2026 года Команда безопасности Rust (Rust Security Response Team) сообщила об обнаружении вредоносных пакетов в реестре crates.io, связанных с популярной библиотекой arrayref.

Что произошло

20 августа 2026 года в 7:15 UTC команда получила сообщение о том, что пакет proc-macro1 является вредоносным. После проверки выяснилось, что его build-скрипт загружал вредоносное ПО.

Пакет proc-macro1, а также связанные с ним proc-macro-en, aovine, arone, aronenao и tinymember были удалены из реестра.

Дальнейшее расследование показало, что широко используемый пакет arrayref был недавно перевыпущен с добавленной зависимостью от вредоносного proc-macro1, при этом последние версии были помечены как yanked. Команда удалила вредоносную версию и восстановила ранее ошибочно отозванные версии.

Аналогичным образом пострадали другие пакеты того же автора — internment и append-only-vec. По ним были приняты те же меры. Аккаунт автора заблокирован в качестве меры предосторожности. По имеющимся данным, сам автор arrayref не действовал злонамеренно — вероятнее всего, были скомпрометированы его компьютер или учётные данные. Команда пытается связаться с ним.

Что нужно сделать пользователям

Рекомендуется проверить локальные зависимости на предмет использования следующих вредоносных версий, удалённых с crates.io:

  • append-only-vec@0.1.9
  • arrayref@0.3.10
  • internment@0.8.7
  • proc-macro1, proc-macro-en, aovine, arone, aronenao, tinymember (любые версии)

Проверить наличие этих пакетов в локальном кэше можно следующей командой:

find ~/.cargo/registry/cache -type f \( \
  -name 'append-only-vec-0.1.9.crate' -o \
  -name 'arrayref-0.3.10.crate' -o \
  -name 'internment-0.8.7.crate' -o \
  -name 'proc-macro1-*.crate' -o \
  -name 'proc-macro-en-*.crate' -o \
  -name 'aovine-*.crate' -o \
  -name 'arone-*.crate' -o \
  -name 'aronenao-*.crate' -o \
  -name 'tinymember-*.crate' \
\) -print

Благодарности

Команда Rust поблагодарила исследователей Nextron Systems GmbH за первоначальное обнаружение проблемы и сообщение о ней, а также сотрудников, участвовавших в устранении инцидента.

>>> Источник



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

Даже простейший hello world невозможен без unsafe.

Не, ну уж не надо так сильно преувеличивать.

А я и не преувеличиваю, println вызывает io::write, который пишет в stdout, внутри которого unsafe

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

Просто существуют, разрабатываются и исследуются другие подходы к безопасности памяти, отличные от раста, например тот же вале и другие. Может быть и не надо спешить внедрять модель раста в с++, если она далека от идеала. В общем, надеюсь, есть люди поумнее, которые на этом собаку съели, и именно они будут предлагать стандарт safe c++ :)

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

Просто существуют, разрабатываются и исследуются другие подходы к безопасности памяти…

Почему не используются в мейнстриме тогда?

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

Кто-то этим вообще занимается? Кажется никому этого не нужно, уже есть раст, все потихоньку забивают на сишку.

В общем, надеюсь, есть люди поумнее, которые на этом собаку съели, и именно они будут предлагать стандарт safe c++ :)

Нужно только подождать, ага!

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

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

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

Список же потому и выбирают

Не обязательно, а с такой реализацией вообще не выбирают. Это пример возможностей системы типов, одна из… Можем и дальше поупарываться. Например, можно прицепить итератор к списку за счёт утяжеления:

struct DIter<T> {
    node: NodePtr<T>,
    prev: NodePtr<T>,
    next: NodePtr<T>
}

prev, next, берутся из ноды, а в самой ноде, один из них будет сигнализировать (если null) что на ноде висит итератор(-ы), а второй на на этот итератор указывать. Так сильно сэкономим на и так дорогих интрузивных указателях - размер нод останется такой же, а итераторов должно быть, по идее, сильно меньше чем нод. И при отработке общего деструктора во всех итераторах занулится поле node. Теперь безопасный интерфейс итератора может выглядеть так:

impl<T> DIter<T> {
    pub fn go_next(&mut self) -> Option<()> {
        // чехарда с указателями
    }
    pub fn go_prev(&mut self) -> Option<()> {
        // чехарда с указателями
    }
    pub fn modify_val(&self, fun: impl FnOnce(&mut T))  -> Option<()> {
        if self.node.is_null() { return None }
        unsafe { fun( &mut (*self.node).val ) }
        Some(())
    }
    pub fn remove_node(self) {
        // чехарда с указателями
    }
}

Лайфтайм теперь не привязан к списку, можем и итерироваться и менять ноды независимо. modify_val(&self, fun: impl FnOnce(&mut T)) - это позволяет сейфово и эффективно(растовые ссылки самый быстрый вид косвенности) работать с val и гарантировать(совместно с однопоточностью) уникальность &mut ссылки. FnOnce позволяет принять и функцию и замыкание любого типа, с одинаковой и максимально эффективной оптимизацией. В плюсах же с обычными ф-ми - как повезёт.

remove_node(self) - а вот это (мув самого себя в никуда) - в плюсах нельзя повторить. Методы go_next, go_prev могут вызвать произвольный убшный бабах после удаления ноды. Раст статически и зерокостно не даст после вызова remove_node(self) вызывать какие-либо методы на этом итераторе. В плюсах можно «защититься» от этого только дополнительной рантаймовой проверкой в этих методах.

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

Кто-то этим вообще занимается? Кажется никому этого не нужно, уже есть раст, все потихоньку забивают на сишку.

Комитет же

Нужно только подождать, ага!

да, как ни странно, просто подождать

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

Я не совсем понимаю, что ты хочешь получить сейчас. Если список, подобный с++, но на safe rust, то у тебя же внутри уже есть unsafe. Ссылка на вариант, написанный полностью без unsafe была выше. Там немного замысловатый интерфейс и список разделяет владение элементами, но вариант вполне рабочий.

А в твоём варианте, что будет если два итератора указывают на один и тот же элемент и оба одновременно вызовут modify_val?

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

… в СВЯЗНОМ СПИСКЕ.

Подсказка: Нода либо принадлежит списку, либо освобождена.

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

Ссылка на вариант, написанный полностью без unsafe была выше.

Нет, в RC есть, как минимум память без ансейфа не выделить, но и без этого его там полно. Вообще это не важно и хейтерская шиза, в смысле, факт наличия unsafe кода или высчитывание процентов ансейф кода. Это не какая-то сифа, а нормальная и полноценная часть системы обеспечения корректности кода. Наличие unsafe с соответствуещей системой типов означает что в языке есть safe, т.е. можно сделать типы/интерфейсы с сейфовостью не отличимой от встроенной, что бы это не значило. Формально: ансейф код + доказатеьство и обеспечение сейфовости строго равно сейф, плюсы так вот не умеют, и мало кто вообще умеет. И ещё, это ортогонально производительности, в обе стороны влияет.

что будет если два итератора указывают на один и тот же элемент и оба одновременно вызовут modify_val?

Нормально будет. Времена жизни передаваемых аргументом ссылок ограничены вызываемой fun и, следовательно, в однопоточном коде не пересекутся, и с деструктором списка тоже. Из-за которого и нельзя просто выдать(как в первой версии) куда-то ссылку на неопределённый срок, может отработать деструктор при всё ещё живой ссылке и она повиснет, а в расте это прям жёсткое УБ

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

Нет, в RC есть, как минимум память без ансейфа не выделит

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

что будет если два итератора указывают на один и тот же элемент и оба одновременно вызовут modify_val?

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

А если один modify_val вызвать из другого?

a.modify_val(|x| {
    *x += 1;

    b.modify_val(|y| {
        *y += 1;
        *x += 1;
    });
});

Если a и b указывают на один элемент, то первая fun ещё не закончилась, когда вызывается вторая. Получается, в этот момент одновременно живы две mutable-ссылки на один T?

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

И мне не надо доверять unsafe коду, который написал ты. И твой код с опасным unsafe это ярко иллюстрирует.

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

А если один modify_val вызвать из другого?

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

struct DIter<T> {
    node: NodePtr<T>,
    prev: NodePtr<T>,
    next: NodePtr<T>
}

подразумевает что на ноде может сидеть только один итератор, и методы go_next, go_prev должны обеспечить это или с остановкой выдавая «занято» или, в другом варианте, перескочив через занятую ноду на следующую (вполне обычная логика для списков - найти «свободную» ноду). И в определении «fn modify_val(&self,», естественно, должно быть «&mut self» (тут я зевнул), и вызвать modify_val внутри другого modify_val на одном и том же итераторе не получится. И ещё у итератора должен быть деструктор который вернёт указатели prev, next обратно в ноду, на случай если remove_node() не вызывался, а скоуп в котором сидел итератор кончился.

И мне не надо доверять unsafe коду, который написал ты

Тут важно что unsafe-ый контракт не получиться забыть или, что даже важнее, не поставить по капризу вида - «да у меня стаж в сишке ого-го и я ошибок не делаю это всё какие-то другие неправильные сишники говнокодят» - что выводит вопрос в чисто формализованную технологическую плоскость.

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

Это отражение хотелки - «а запилите мне «простой» список на укозателях чтоб лоб в лоб как в сишке, и с повторением всех дурных сишных привычек, и раст это должен каким-то чудом вывезти».

Хотелка была - список на safe rust, если что. Ну да ладно, допустим ты хочешь сделать как на с++, просто с safe API, пусть с unsafe внутри. Но тогда вот это твоё ограничение «на ноде может сидеть только один итератор» всё ломает. В std::list может хоть сто итераторов указывать на одну ноду. Если уж ты хочешь добиться чего-то похожего на std::list, то надо это разрешить, как это позволяет, например, rc-dlist-deque. В нём сколько угодно итераторов могут ссылаться на одну ноду без проблем. И только для получения эксклюзивного mut& вступают в игру ограничения.

И мне не надо доверять unsafe коду, который написал ты

Тут важно что unsafe-ый контракт не получиться забыть или, что даже важнее, не поставить по капризу вида - «да у меня стаж в сишке ого-го и я ошибок не делаю это всё какие-то другие неправильные сишники говнокодят» - что выводит вопрос в чисто формализованную технологическую плоскость.

Так ты же буквально только что продемонстрировал обратное :) В первой версии modify_val(&self, …) ты как раз забыл часть unsafe-контракта, и у тебя через полностью safe API можно было получить UB. То есть unsafe хорошо локализован и заметен, но корректность его контракта компилятор за тебя уже не проверяет. Поэтому, собственно, я и прошу пример без unsafe. Ну да ладно, это уже лирика.

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

Одно время я слушал стримы Евгения Спицына (историк наш), пока однажды, когда он в очередной раз сыпал по памяти десятками имён каких-то хренов третьего ранга принятия решений из начала прошлого века, не ужаснулся до корней волос: он сделал своей профессией копание в чужих жизнях вместо проживания собственной.

А сама фраза – бред собачий. Точно так же можно сказать про «premature architecture»: даёшь agile во все дыры! Лично я никогда не занимался оптимизацией целенаправленно, я просто чистил нагромождения говнокода и говно-архитектуры; ускорение на 2-3 порядка получалось при этом само собой. (Хотя вру: многие вещи у меня настолько давно уже в рефлексах, типа например prepared statements, что я их даже не замечаю.) А если заранее не видишь в общем и целом, что хочешь получить на выходе, то вляпаешься в проблемы в любом случае.

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

он сделал своей профессией копание в чужих жизнях вместо проживания собственной

Почему обязательно вместо, а не вместе? А условных программист, который делает своей профессией копание в куче UB и особенностей реализаций, или кучу плохо совместимых между собой ассемблеров, или куче стандартов C++, тоже делает это вместо проживания своей жизни? А условный физик, делающий своей профессией копание в квантах и кварках, да спинах с зарядами?

Профессия и знания, в неё входящие не заменяет жизнь, а является её составной частью. У нормальных людей не единственной.


Что касается фразы… Да тут как обычно со всеми подобными — в ней есть немалая доля мудрости, но при этом с дуру можно и… много чего сломать… Применение подобных афоризмом ко всем до единой возможным ситуациям без критического осмысления ничуть не лучше тех ошибок, от которых они предостерегают. А порой и хуже. Но это не значит, что они не несут в себе и некоторой мудрости, тем не менее. Когда есть свой мозг, а не только желание слепо следовать избранным догматам, человек как-то определяется сам для себя, где что и насколько применимо…

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

Обсуждаемая фраза – «the root of all evil» – максимально категорична и не предполагает использования мозгов.

Куча UB и стандартов, а точнее, куча мелких нюансов, увеличивающаяся с каждым новым стандартом – это на самом деле про то, что C++ откровенно говёный язык. Только маньяки буду просирать свою жизнь, выискивая разные виды глистов в этом продукте чужой жизнедеятельности, или (как я) воюя с ним из нежелания отказываться от высокоуровневости и низкоуровневости в одном флаконе. А кто поумнее – возьмут что-нибудь попроще и субъективно поудобнее, и будут больше времени проводить с детьми.

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

Обсуждаемая фраза – «the root of all evil» – максимально категорична

Да.

и не предполагает использования мозгов.

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

Куча UB и стандартов, а точнее, куча мелких нюансов, увеличивающаяся с каждым новым стандартом – это на самом деле про то, что C++ откровенно говёный язык.

Не спорю.

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

Нет, не только маньяки. Есть множество других расстройств, ведущих, к похожим результатам, но не являющихся манией ;)

А кто поумнее – возьмут что-нибудь попроще и субъективно поудобнее, и будут больше времени проводить с детьми.

Возможно. Хотя и часть из тех, кто поглупее, тоже ;)

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

Обсуждаемая фраза – «the root of all evil» – максимально категорична

Да.

и не предполагает использования мозгов.

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

В логику умеют не только лишь все.

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

Обсуждаемая фраза имеет контекст, как в виде статьи, в которой написана, так и в виде своей компьютерной эпохи.

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

Неверно - ни синтаксически (не вижу в тексте самой фразы никаких отсылок ни к каким контекстам), ни эмпирически (её цитируют по делу и нет, безо всякого контекста, и без мозгов). Хорошая формулировка должна быть самодостаточна, как 2+2=4, как теорема, без необходимости объяснять «что имел в виду автор». А эта фраза, повторюсь, – бред собачий.

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 1)
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.