1.83K subscribers
3.29K photos
130 videos
15 files
3.57K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Forwarded from shitposting 3.0 [+ dragons]
🔥10😁7😱3🤯1
Так, блэт, почему уже три (если я не сбился со счёта) человека изъявили желание, чтобы я был их daddy? 🤔
21🌚3🤷2
#prog

Опыт работы с VSCode Live share
Forwarded from концлагерь "закат-13" [экстремист edition]
почему я его ненавижу. спешу отметить, что это мое личное мнение, вероятно, я дурачок, и некоторые из проблем возможно все таки можно решить.

итак, сам список:
• дерганная работа — в direct-режиме и даже если пинг между участниками низкий задержка при использовании live share может составлять несколько секунд. почему? черный ящик имени майкрософта.
десинк порой настолько сильный что у хоста вместо написанного мной кода — каша.

• невозможность поднять свой релей — issue закрыт как "not planned"

• кривая работа с кодом — тайпинги прогружаются через раз, Quick Fix работает, упаси дьявол, 1 раз из 10.
а если ты через тот же Quick Fix пытаешься импортировать какой-то файл — удачи, бро, велик шанс, что он не увидит, где можно импорт взять (даже если он в локальном файле а не в стороннем модуле)

• кривая работа с терминалом — он может тупо зависнуть, если происходит какая-то активная фигня (например — сборка докер имеджа). то какое решение?
либо переподключиться, либо попросить хоста (если это не ты) открыть другой термниал. очень удобно 👍

ну и для нотки лирики: за 2 часа парного кодинга мне пришлось переподключаться около 6-7 раз.
🤮1
😡
🤮4👍3🤬2👌2🔥1
#prog #ocaml #article

Oxidizing OCaml (1, 2, 3) — серия из трёх статей, описывающая дополнения в OCaml, позволяющие добавить в язык некоторые из присущих Rust концептов — владение, лайфтаймы и уникальные ссылки — и таким образом привнести довольно конкретные преимущества для написания корректных и более экономно использующих ресурсы программ. Всё это — через систему аннотаций режимов (mode), ортогональных системе типов и друг другу и потому не влияющих на разрешимость вывода типов (для авторов предложенных изменений это важно).

Настоятельно советую прочитать оригинальные статьи, но что-то я перескажу и тут.

Первая статья: Oxidizing OCaml: Locality. В ней рассказывается про режим локальности значений. В отличие от полиморфных ссылок Rust, этот режим поддерживает лишь два возможных варианта: global (вариант по умолчанию) и local.

Времена жизней глобальных (global) значений могут жить независимо от исполнения различных функций. Как следствие, компилятор вынужден все подобные значения класть в кучу и делегировать управление временем их жизни рантаймовому сборщику мусора. На использование локальных (local) значений накладываются ограничение: именно, подобные значения не могут пережить функцию, в которой они находятся. В терминах escape analysis это значения, которые не escape текущую функцию. Так как подобные ограничения излишне строги, язык предоставляет возможность явно выделить local значение во внешнем регионе вместо текущего. Так как escape analysis — это не какая-то новая вещь, определить локальные значения возможно статически во время компиляции.

Введение этого режима даёт два преимущества.

Во-первых, локальные значения можно выделять на стеке — и в случае OCaml это несколько более существенное преимущество, поскольку стековый аллокатор не обязан использовать под стек ту же память, что и стек потока управления. Это позволяет достигнуть более высокой производительности, поскольку использование стекового аллокатора не триггерит использование сборщика мусора, а также естественный LIFO паттерн доступа к стековой памяти приводит к лучшей утилизации кеша процессора. Для сравнения, в текущей реализации OCaml сборщик мусора поколенческий, и для младшего поколения используется кольцевой буфер. Малая сборка мусора относительно дешёвая, но каждый объект, который переживает её, уходит в кучу для старых объектов, обход которой дороже. Вдобавок, использование кольцевого буфера повышает шанс, что аллокация новых значений приведёт в вымыванию из кеша старых значений.

Во-вторых, локальность позволяет строить более корректные API. В качестве примера в статье приводится функция Core_unix.with_file. Эта функция принимает на вход имя файла и коллбек. Семантика достаточно проста: функция открывает файл с указанным именем, отдаёт дескриптор коллбеку, после чего закрывает файл и возвращает результат вызова коллбека. Всё бы ничего, но OCaml — не чистый язык, а потому переданный коллбек может захватить внешнее изменяемое состояние и сохранить файловый дескриптор в нём. Попытка воспользоваться этим дескриптором приведёт в лучшем случае к ошибке, а в худшем — к взаимодействию с некоторым другим файлом, если дескриптор будет переиспользован. Локальность позволяет решить эту проблему. Указав режим аргумента коллбека, как local, мы наложили на коллбек требование, что аргумент не утечёт. Таким образом, мы можем быть уверены, что после завершения коллбека аргумент существует лишь в with_file, и потому его можно передать в close, не опасаясь ошибок.

Как вы могли заметить, local лишь накладывает ограничения на использование, а потому, по идее, можно передать global значение туда, где ожидается local значение. И действительно, на вариантах режима локальности есть отношение субтипизации: global является подтипом local.

Автор отмечает, что локальность с успехом используется на практике.
👍92
Вторая статья: Oxidizing OCaml: Rust-Style Ownership. В ней рассказывается о режиме уникальности значений. У этого режима три возможных значения: shared (вариант по умолчанию), exclusive и unique.

Режим shared говорит о том, что на значение существует произвольное количество ссылок. Это не даёт никакой полезной информации, и такое значение вынужденно управляется сборщиком мусора. В OCaml есть такая возможность, как функциональное обновление записи (в Rust есть аналогичная фича с практически идентичным названием):

type 'a box = { x : 'a option }

let clear box =
{ box with x = None }
;;

(* clear имеет тип 'a box -> 'b box *)

Если значение, переданное в clear, является shared, то компилятор вынужден копировать все поля записи и выделять в куче новое значение поля (а также запись целиком). На поле (и на запись) могут существовать ссылки в иных местах программы, поэтому генерируемый код вынужден выделять память под новую структуру, чтобы другой код не мог заметить изменений.

Но что, если ссылка на box существует в единственном экземпляре? В этом случае можно избежать выделения новой структуры и непосредственно поменять значение поля на новое, что и эффективнее копирования самого по себе, и снижает нагрузку на сборщик мусора. Более того, значение может поменять тип (это называется strong function update) — и это не приведёт к проблемам, так как в силу единственности ссылки старый тип никто не увидит. Чтобы удостоверить единственность ссылки, можно пометить значение, как unique. Это аналогично перемещению в Rust, с той разницей, что режимы ортогональны типам.

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

type file

val open_ : path:string -> file @ unique
val close : file @ unique -> unit

Использование unique позволяет удостовериться, что к файлу нельзя обратиться после того, как он будет закрыт. Функция для чтения из файла при этом будет записана так:

val read : file @ local -> string

Режим local для аргумента существенен: если его опустить, то вот такой прямолинейный код:

let print_and_close (unique file) =
print_endline (read file);
close file
;;

приведёт к ошибке, поскольку file тут использован неуникальным образом. Функция read без local может потенциально сохранить file где-то ещё и инвалидировать инвариант единственности ссылки. Правильный код будет выглядеть так:

let print_and_close (unique file) =
print_endline (read &file);
close file
;;

, где синтаксис &file используется для снижения режима file до local shared (и называется заимствованием, как и в Rust).

Как можно заметить, понижение unique до shared валидно. И в этом есть смысл: unique означает информацию о наличии единственной ссылки. Эту информацию можно попросту забыть и получить shared. Действительно, unique является подтипом shared, и в частности, следующий код валиден:

let bar (unique x) =
x, x
;;

(* val bar : 'a @ unique -> 'a * 'a *)

Это аналогично тому, как &mut-ссылку в Rust можно привести к &-ссылке.
👍4❤‍🔥1
Тем не менее, такой уникальный доступ временами излишне ограничителен. Пример:

val write : file @ unique -> string -> unit

let write_and_close (unique file) =
write file "data";
close file
;;

Компилятор закономерно жалуется на то, что мы уже использовали file уникально и потому не можем использовать его снова:

5 | close file
^^^^
Error: file is used uniquely so cannot be used twice.

(аналогично растовому value used after move). Для исправления этого недостатка пришлось бы возвращать file из write — что было бы, мягко говоря, не очень удобно. Продолжая параллели с Rust, в Rust функция write принимала бы &mut-ссылку. И этому есть аналог среди режимов уникальности!

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

val write : file @ local exclusive -> string -> unit

В коде

let write_and_close (unique file) =
write &file "data";
close file
;;

значение file одалживается только одиножды, поэтому синтаксис &file снижает режим до local exclusive. Если бы аргумент был одолжен дважды, то вместо этого произошло бы понижение до local shared. Пример:

val write2 : file @ local exclusive -> file @ local exclusive -> string -> unit

let write_twice (unique x) =
write2 &x &x "data"
;;

Ошибка:

4 | write2 &x &x "data"
^^
Error: found a shared value where an exclusive value was expected.

В OCaml есть возможность явно помечать поля, как мутабельные. Расширение, добавляющее режимы уникальности, также позволяет помечать поля, как exclusive mutable. Это позволяет задействовать реальную мутабельность для functional update, но не позволяет использовать strong function update. В этом есть смысл: exclusive — это временно уникальная ссылка, а потому имеющиеся до её создания ссылки могли бы наблюдать изменение типа. Тут тоже можно провести параллели с Rust: &mut T инвариантна по T.
К сожалению, одной лишь уникальности без дополнительных изменений недостаточно, ибо уникальность позволяет написать unsound код:

type 'a box = { x : 'a option }

let wrap (unique box) =
fun () -> box
;;

let unsound (unique box) =
(* get захватывает box *)
let get = wrap box in
let a = get () in
let b = get () in
{ overwrite a with x = Some 0 }, { overwrite b with x = Some "string" }
;;

Функция unsound пытается перезаписать одно и то же место в памяти значениями двух различных типов, оставляя ссылки на них живыми. Это позволяет тривиально сломать type safety. Надо отметить, что небезопасным тут является не вызов get сам по себе, а вызов get дважды.

И потому необходимым является ввод третьего режима: линейности. Именно, нам нужно выразить тот факт, что get можно вызвать только один раз. И это именно то, что означает режим once (его можно применять и к не-функциям, но для этого он, как правило, менее полезен). Другим возможным значением (которое является умолчанием) является many, которое не накладывает ограничения на количество использования значения.

once
является более строгим ограничением, чем unique: написать с его помощью функцию для дупликации значения нельзя:

let baz (once x) =
x, x
;;

Ошибка:

2 | x, x
^
Error: found a once value where a many value was expected.


Компилятор знает, что вызов функции, захватывающей unique более одного раза, небезопасен, поскольку захваченное значение может быть использовано после первого вызова. Поэтому компилятор выводит режим wrap как once:

val wrap : 'a @ unique -> (unit -> 'a @ unique) @ once

Теперь код выше не компилируется:

let unsound (unique box) =
let get = wrap box in
let a = get () in
let b = get () in
{ overwrite a with x = Some 0 }, { overwrite b with x = Some "string" }
;;

Ошибка:

4 | let b = get () in
^^^
Error: found a once value where a many value was expected.

Уникальность и линейность семантически связаны. Именно, unique говорит, что на значение есть только одна ссылка, а once говорит, что на значение не будет создано новых ссылок. Продолжая параллели с Rust (упомянутые и в статье, между прочим!), можно сказать, что once-значение — это замыкание, реализующее трейт FnOnce.

Как и с другими режимами, once — это ограничение на использование. Ничто не мешает использовать только один раз значение, допускающее произвольное количество использований. Именно поэтому в Rust трейт FnOnce является (косвенно) супертрейтом трейта Fn, допускающим произвольное количество вызовов.

Одни лишь once и many, впрочем, недостаточно выразительны. Если у нас есть функция, которая захватывает некое заимствованное (и потому local) exclusive значение, то по умолчанию замыкание становится local, даже если заимствование идёт от global значения. Однако если пометить само замыкание, как global, то это значение будет перемещено в замыкание. И тут встаёт вопрос: какой режим у подобного замыкания?

С одной стороны, его явно должно быть можно вызвать более одного раза. С другой стороны, полностью свободным от ограничений оно не является. Именно, оно не является рентерабельным: нельзя вызвать его, пока не завершился предыдущий вызов, ибо это сломает инвариант эксклюзивности ссылки. Для выражения этого ограничения используется третий вариант линейности: separate. Он аналогичен FnMut в Rust (опять-таки, это прямо написано в статье).
👍6
У читателя может возникнуть резонный вопрос: стоит ли добавление режимов уникальности (и линейности) добавленной сложности языка, особенно с учётом того, что в OCaml и так есть изменяемые поля? Это совершенно справедливый вопрос, и до недавнего времени перевес был бы не в сторону дополнительных фич. Однако релиз Multicore OCaml добавил в язык параллельность, и как следствие — потенциальные гонки данных. Конкретно в случае с OCaml это несколько меньшая проблема, чем в, скажем, C(++) и Rust, поскольку в модели памяти OCaml гонки данных не приводят к неопределённому поведению. С другой стороны, они всё ещё могут с легкостью привести к логическим ошибкам.

Третья статья: Oxidizing OCaml: Data Race Freedom. Как следует из названия, в этой статье рассказывается про API, который позволяет статически убедиться в отсутствии гонок данных в программах на OCaml. К сожалению, эта статья заметно сложнее предыдущих, поэтому вместо своего пересказа я пробегусь по ней тезисно.

Для того, чтобы дать подобные гарантии, вводится новый режим, с возможными значениями sync и nosync. Значения с первым режимом допускают конкуретный доступ на запись и потому могут быть безопасно разделены между доменами (термин OCaml для обозначения независимого потока исполнения, который могут исполняться параллельно). Режимом sync, очевидно, обладают неизменяемые значения. Также им обладают exclusive значения в силу своего инварианта. Все остальные значения являются unsync.

Наиболее простой способ избавиться от гонок данных — обернуть данные в мьютекс. В представленном API конструктор мьютекса принимает unique аргумент, что логично: невозможно защитить доступ к данным при помощи мьютекса, если можно получить к ним доступ в обход мьютекса (в Rust Mutex::new принимает аргумент по значению). (А ещё конструктор требует, чтобы аргумент был sync, и мне не особо понятно, почему).

Для доступа к содержимому мьютекса используется функция with_lock, в том же bracket стиле, что и with_file. Она захватывает мьютекс, вызывает переданную функцию на охраняемом значении, отпускает мьютекс и возвращает результат вызванной функции. Для обеспечения корректности она требует, чтобы переданный коллбек принимал аргумент в режиме local exclusive (то есть не более одного доступа за раз и аргумент не утекает), а сам коллбек принимается, как local.

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

Этот тип используется для предоставления доступа к содержимому капсулы. Именно, доступ к содержимому капсулы происходит через связанный тип указателей (серьёзно, в статье так и называется Ptr). Для любых манипуляций с тем, на что этот указатель указывает, требуется связанный с капсулой ключ, и доступ к этому ключу является эксклюзивным. Так как каждая капсула получает свой собственный ключ, получить доступ к содержимому указателя капсулы можно лишь через ключ этой капсулы — иначе типы не совпадут. Потому вместо синхронизации доступа к графу объектов нужно синхронизировать лишь доступ к ключу. Так как ключ не хранит никаких данных, в рантайме он ничего не стоит, а защищающий его мьютекс вырождается в атомарное булево значение.

Этот тип синхронизации не имеет прямого аналога в Rust. Наиболее близкий API — это GhostCell, но в OCaml он гораздо удобнее. И ещё: тип Rc<T> не является SendSync) в Rust, поскольку передача копии Rc в другой поток привела бы к возможности неатомарного доступа к счётчикам ссылок из разных потоков. С другой стороны, это не было бы проблемой, если бы весь связный граф из Rc можно было бы передать целиком из одного потока в другой — и, похоже, именно это в какой-то мере и можно сделать с предложенным в статье API.
Ну давайте дорогие читатели расскажите за что меня ненавидите
Блог* pinned a photo
Forwarded from Cybred
This media is not supported in your browser
VIEW IN TELEGRAM
https://github.blog/2023-10-09-coordinated-disclosure-1-click-rce-on-gnome-cve-2023-43641/

CVE-2023-43641
PoC

RCE по клику в библиотеке libcue, которую использует один из компонентов Gnome. Переполнение, как и в Looney Tunables
#prog #itsec #article

How I made a heap overflow in curl

Даниел сам признаёт, что этот баг не удалось бы сделать в memory safe языке. И, как видите, используемые статические анализаторы не помогли.

Ещё примечательно, что баг был допущен при ручной конвертации из блокирующего кода в неблокирующий с использованием стейт-машины. Так что это может быть доводом в пользу того, чтобы иметь async в языке
🥰7👍1
И ещё #itsec
> 1996
> 1988

Стабильность, которой позавидует Microsoft

На фото запечетлён момент, когда менеджмент ставит задачи разработчику по исправлению этих багов.

CVE-2023-43785 - выход за границы буфера в коде libX11, проявляющийся при обработке ответа от X-сервера с числом символов, не соответствующих ранее отправленному запросу XkbGetMap. Уязвимость вызвана ошибкой в X11R6.1, существующей с 1996 года. Уязвимость может быть эксплуатирована при подключении приложения, использующего libX11, к вредоносному X-серверу или промежуточному прокси, контролируемому злоумышленником.

CVE-2023-43786 - исчерпание стека в результате бесконечной рекурсии в функции PutSubImage() в libX11, возникающей при обработке специально оформленных данных в формате XPM. Уязвимость существует с момента выхода X11R2 в феврале 1988 года.

CVE-2023-43787 - целочисленное переполнение в функции XCreateImage() в libX11, приводящее к переполнению кучи из-за ошибки в вычислении размера, не соответствующего фактическому размеру данных. Проблемная функция XCreateImage() вызывается из функции XpmReadFileToPixmap(), что позволяет эксплуатировать уязвимость при обработке специально оформленного файла в формате XPM. Уязвимость также существует со времён X11R2 (1988 год).


Уязвимости в библиотеках X.Org, две из которых присутствуют с 1988 года
https://www.opennet.ru/opennews/art.shtml?num=59906
5😁5🤯1