Pull to refresh
4
0.1
Кривенков Роман @qwerty19106

Embedded

Send message

Эта конкретная опция по умолчанию выключена. Зато тысячи потенциально-опасных мест по-умолчанию не вызывают UB, в отличие от С++.

P.S. Кстати, я перепутал опцию, о чем чуть ниже написали. Обращение за границы массива всегда вызывает панику. А overflow-checks включает панику при проверке переполнения целых чисел. Но и без нее UB не будет!

А отключить проверку границ можно, как в safe через итераторы, так и в unsafe через unchecked* методы.

То что этот конкретный proposal не добавляет в стандарт понятие pointer provenance, не значит что ничего не меняется. Там 1,5 страницы изменений стандарта.

Моя претензия не к самому pointer provenance, а к тому, что поведение компиляторов меняют на ходу, и оно не соответствует стандарту. А потом, поигравшись, пытаются уже поменять в стандарте. Обычно же экспериментальные вещи включаются ключем компилятора, и по умолчанию они выключены.

Из этого и возникло что "уже все сломано", и proposal как будто пытается починить. Но он лишь фиксирует ситуацию.

Тогда что он делает в proposal к стандарту?

переиспользования старого указателя после вызова realloc() - в общем то, что и так запрещено в стандарте давным-давно.

В С89 это разрешено стандартом, и тем не менее pointer provenance всё ломает. Кстати он ломает не только realloc, просто это самый наглядный и легко воспроизводимый случай.

Феерический набор клише и недостоверной информации.

Например, в Rust невозможно без unsafe передать функции неинициализированный буфер.

MaybeUninit в стабильной версии компилятора уже более 3 лет.

Где гарантии, что в этих тысячах unsafe блоков меньше критичных ошибок, чем в вылизанных десятилетиями стандартных библиотеках C++?

Внутри может быть сколько угодно unsafe кода, но наружу торчит безопасное API, т.к. инварианты для unsafe соблюдены. В отличие от С++, где на каждый чих нужно лезть в документацию и стандарт на предмет UB.

При этом до сих пор существуют возможности даже без unsafe блоков добиться, для примера, use-after-free, пусть и не тривиальным путем.

Это признано багом и будет исправлено. Причем это единственный такой баг, а сколько таких висит в компиляторах С++ и никогда не будет исправлено, т.к. "это не баг а фича"?

Так же Rust не контролирует обращение за границы массива в релизной сборке.

[profile.release]
overflow-checks = true

Это опция существует ~ 8 лет, но вы продолжайте распространять..

UB в Rust тоже имеют место быть.

Для этого нужно написать unsafe код, и это выглядит сложнее и гораздо многословнее, чем safe код. В отличие от С++, где в std короткие и простые методы вызывают UB, и это сделано специально.

Вот в этом очень длинном комментарии неплохо описаны проблемы современного С++ https://habr.com/ru/articles/813645/comments/#comment_26815569

От себя добавлю:

  • pointer provenance, которого нет в стандарте, и неизвестно когда будет. Но его добавили во все компиляторы задним числом, даже для режима С89, и он ломает старый, вылизанный десятилетиями код, и усложняет написание нового.

  • Неочевидные UB, про которые никто не знает, не ловятся санитайзерами и статическим анализом. Например integer promotion https://godbolt.org/z/GWsaGo

  • Главная (на мой взгляд) проблема С++ - код с UB написать проще, чем без UB, и он выглядит гораздо короче. В Rust же наоборот.

Или какие-то глобальные изменения в С произошли?

Раньше компиляторы С и С++ реализовывали стандарт, а теперь стандарт переписывают по желанию разработчиков компиляторов.

В Rust с безопасностью гораздо лучше , но есть не очевидное следствие: в типах содержится информация, которую программист на С++ держит в уме и комментариях. Из-за этого большой порог входа и некоторые структуры гораздо сложнее написать (например графы).

Классический код на Rust не "ориентирован" на объекты и их взаимодействие

Еще как ориентирован. В С++ наследование, а в Rust композиция. Это 2 разных подхода ООП, и у каждого есть свои плюсы и минусы.

С++ в совершенно обычном коде вызывает UB, чего не происходит в других языках. Посмотрите например последний пример по ссылке, связанный с Integer Promotion:
https://github.com/Nekrolm/ubbook/blob/master/numeric/overflow.md

constexpr std::uint16_t IntegerPromotionUB(std::uint16_t x) {
    x *= x;
    return x;
}

// 65535 * 65535 mod 1<<16 = 1

static_assert(IntegerPromotionUB(65535) == 1); // won't compile

Хотя казалось бы в unsigned типах всё надежно..

Во-втором примере я не вижу, куда спряталась информация о типе возврата функции, указатель на которую передаётся.

Я чуть выше уже про это ответил.

И как в этой нотации записать не указатель на функцию, а указатель на указатель на функцию, или указатель на указатель на указатель на функцию?

Точно также, как в С, но читается лучше:

op_ref_ref_ref: &mut &mut &mut fn(int, int): int

Разумеется могут возникнуть проблемы с Borrow Checker в такой схеме, так что лучше

op_ref_ref_ref: &mut Box<Box<Box<fn(int, int): int>>>

Хотя я не могу даже представить, зачем нужно передавать "указатель на указатель на указатель на функцию".

Вы правы, тип я забыл указать. Должно быть так:
fn operation(op: fn(int, int): int, a: int, b: int): int { return op(a, b); }

Или можно передать с любым, главное чтобы аргументы совпадали?

Зависит от задачи. Если тип не указан, то ничего не возвращается (аналогично void в С и С++)

Передача аргументом указателя на функцию в С и С++ - громоздкая и архаичная. Например это очень сложно читать:
int operation(int(*op)(int,int), int a, int b) { return op(a, b); }

А вот это сразу очевидно:
fn operation(op: fn(int, int), a: int, b: int): int { return op(a, b); }

Не вижу в этом никакой проблемы.
1) Используйте внутри блока ссылку, тогда перемещение не произойдет.
2) В коде, который возможно не исполнится, вызывайте `.clone()`, если вам нужно владение.
Такой подход будет оптимальным и с точки зрения быстродействия, и читаемости кода.

Какое решение?

Перечитайте раздел с "maybe(async)"

Я так и не понял в чем мораль?

На мой взгляд, лучше разделять библиотеку на 3 части: общая, async и blocking. Изначально немного больше работы, но потом гораздо проще поддерживать.

Я исхожу из своего опыта работы со Stackless Python3, где те же проблемы. Особенно вот эта:

Но, с добавлением этих функций в язык связана одна проблема: они идентичны СИНИМ функциям, поэтому при вызове их невозможно отличить! При работе нужно опираться на документацию и держать в голове все ЗЕЛЁНЫЕ функции, чтобы ненароком не вызвать их из КРАСНОЙ.

Переписываем одинаковый код из года в год и тянем среду выполнения С.

Что же вы предлагаете? Отказ от FFI будет стоить языку жизни, т.к. придется переписать все популярные библиотеки.

Посмотрел подробнее примеры в конце статьи, поля регистров всё-таки есть (тут я был не прав).

А запутали меня строки:
GPIOA->ODR &= GPIO_ODR::ODR[NUM_4] | GPIO_ODR::ODR[NUM_10];

Из которых не следует что поля регистров вообще где-то описаны, или генерятся из SVD файлов.

Думаю это просто неудачный пример, т.к. регистры GPIO не подразумевают нормальных имен полей. Предлагаю эту часть статьи переписать, взяв например регистры и поля RCC. Тогда плюсы автогенерации из SVD будут всем очевидны.

P.S. Теперь ясно, что оно гораздо лучше CMSIS, при условии что вы сами поправите/напишите SVD файлы нужной перифирии.

Библиотека автора пока еще далека от CMSIS даже, т.к. нет работы с полями регистров (имена, R/W и R/O режимы, битовые маски). И совсем не сравнить с библиотеками на Rust, где для полей регистров на уровне типов указывается, есть ли вторичные эффекты при чтении/записи поля.

Но главное, что вендоры пока даже не пытаются исправлять SVD файлы, т.к. ими никто не пользуется. Перепутанные имена регистров, отсутствующая периферия, или наоборот несуществующая, RO поля, помеченные как RW и т.д.

Мало кто готов спуститься до этого уровня и поправить SVD файлы для нужного чипа. А вендоры не готовы принимать исправления. И пока это не поменяется, не возможно сделать автогенерацию кода регистров, и 99% разработчиков будут продолжать писать на С библиотеках вендоров.

В Rust кстати эту проблему решили тем что написали свой стандарт SVD. Ну и пишут с 0 SVD файлы, что довольно трудоемко, хотя они и выходят раз в 8 меньше файлов вендоров.

А я вам написал, что эти фичи eigen делают невозможным выбор оптимального варианта SIMD. И это в С++, где производительность - объект поклонения каждого программиста.

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

Но вы упорно отказываетесь обсуждать суть проблемы. Кто же из нас демагог?

Я вам развернуто написал что на мой взгляд в eigen хорошо сделано, а что плохо. И чего не хватает в nalgebra и Rust.

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

По этому возвращаю вам обратно:

Я тоже могу сказать, что embassy не выразим в С++.

И прочитал еще раз. И все-равно мы там обсуждали "другие языки" и GC последние 6 сообщений как минимум.

Пробелмы поставки библиотек в бинарном формате это отдельная проблема. То что в расте это сделать нельзя говорит лишь об ограниченности раста и что он неприменим для вендоров блобов. Хотя скорее всего можно за сишным интерфейсом спрятать реализацию на расте, тогда возвращаемся к той же проблеме.

Разумеется можно:

crate-type = ["dylib", "rlib"]

Будет .so библиотека с кодом Rust без всякого C-API. И для вендоров всё хорошо.

Давайте начнём с того как вендор создаст dll на раст и что вы будете делать с ней?

В dll от вендора на практически любом языке не получится добавить санитайзеры, исходного когда тоже нет.
Так что придется поверить вендору что с ней всё хорошо. И полагаться на гарантии компилятора.
Например для CSharp я уверен что dll не портит память и не вызовет UB. Для Rust аналогично, если вендор мне ответит что там нет unsafe (разумеется если он не врет).

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

Я так понимаю ошибок в 3rd-party на других языках ни когда не бывает? или к чему это?

утечка памяти и ресурсов в целом в языка с GC вроде как не уникальное явление

Перечитал. Не согласен.

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

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

И все же что вы будете делать с dll от вендора?

Вы спрашивали:

а что собственно страшного в переполнении signed int и в чем принципиальное отличие от того что в других языках оно defined behavior хотя и делает тоже самое?

А теперь заявляете:

меня мало интересуют синтетические примеры

Похоже вы не читали другие примеры по ссылке. Вычисление хэша строки тоже синтетический пример?

Ну а в реальной жизни полно уязвимостей, вызванных UB переполнения int, раздел Observed Examples
https://cwe.mitre.org/data/definitions/190.html

А бесконечные циклы и без переполнения знаковых могут случится и не только в С++

В других языках эти ошибки будет легко воспроизвести и поправить код. А в C и С++ UB приведет к гейзенбагу, который будет наблюдаться раз в год при определенном положении Марса в созведии Козерога.

Линуса значит, вот только то что он согласился на поддержку раста как языка для драйверов ни какого отношения к "не безопасности" C++ не имеет, и его претензии к плюсам в целом мне понятны и в большинстве своём обоснованы , но они опять же ни как не связаны с "не бесопасным кодом"

Речь вообще не про Rust. Его претензии к С++ и С привели к появлению опций в gcc, чтобы можно было отключить некоторые UB ключем компиляции.
Ну а про Microsoft и Google вы совершенно случайно забыли ответить.

ты же знаешь что glibc в андроиде на C++ написан? и то что не проблема писать на С++ и иметь при этом внешний апи на C?

Если вот эта, то там от С++ одно название.
https://ru.wikipedia.org/wiki/Bionic_(библиотека)

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

Очнитесь и выгляньте наконец в реальный мир. Он совсем не такой как в 2000 году:

Cloudflare целиком на Rust переписались, в исходниках Android его уже столько же, сколько C, AWS несколько сервисов целиком переписали и не останавливаются (и сделали Firecracker целиком на нем, опенсорс менеджер микровиртуалок, на котором работают Lambda и Fargate), с голенга многие тоже переписываются. Я могу дать полный список известных мне проектов, там от MESA до Figma, Vercel и части бэка npm.js репозитория

Компилятор переносит умножение на 0x20000001 на строку выше. Результат умножения оказывается больше чем int max, и он выкидывает условие остановки цикла.

Information

Rating
4,157-th
Location
Ижевск, Удмуртия, Россия
Date of birth
Registered
Activity