
GNU IFUNC は CVE-2024-3094 の真の原因です
[オレンジ色のウェブサイト][hn]のジョーカーどもが俺を困らせているようだな。以下にいくつか厳選した返答を用意したが、まずは挑戦状を叩きつけておく:ifuncを使わずに この攻撃を実証できた最初の人物に、自腹で500ドルを送る。純粋に興味があるし、啓蒙のためなら喜んで金を払う。このリポジトリをフォークして、動作するPoCをPRで送ってくれ。勝者には個別に郵送先を聞く。では返答だ:
これは見当違いも甚だしい。
坊や、俺は見当違いの中に住んでいるんだよ、誰にでも吠えられる。
エクスプロイトに必須ではなかった。
お前こそエクスプロイトに必須じゃねえよ!
rootとして実行される任意のコードに対する保護を追加したいなら、selinuxが常にある。
一度ロードされれば、この攻撃はそれ以上syscall境界を越える必要がなかった。だからそう、我々は「おまけ」のrootセッションを制限できたかもしれないが、それでもマシンには招かれざる客がいただろう!
なんだこれは、ビルボの誕生日会か??パーティー用の用事以外では入れないのか!!
- IFUNCはmainの前にコードを実行する唯一の方法では決してない。
しかし、メモリ保護が設定される前にコードを実行する不必要な方法ではある。
- 彼らが提示する代替案は、プロセスの生存期間中ずっと関数ポインタが書き込み可能なままになるため、おそらく安全性は低い。
mprotectで improvisation できる!LD_PRELOADの変更サブセクションの上の最後の文を見てくれ。
ええ、このブログは誤導されている。
失礼、このブログは誘導されていない。これらの悪戯は全部自分でやったんだ!誰にもこんなバカになるよう騙されていない。
IFUNCは[クライアント]ソフトウェア自体で実装されるべきだ、
@CountWSS 💯 その通りだぜ
Githubメンテナーによる一連の露骨なプロセス失敗から...
これが唯一真剣に返答する点だ:
これを彼の側のミスから始まったと考えるのは、xz-utilsのメンテナーに対して極めて不公平であり、コミュニティにとってかなり危険だと思う。それは誰もこのプロジェクトの維持を手伝おうとしなかったことから始まった。攻撃者はifuncを技術的脆弱性として、そして我々のxz-utilsに対する集団的怠慢を社会的脆弱性として利用した。Collin氏の行動を、英雄的で何年にもわたるコミュニティサービスへの献身以外の何かと見るのは恥ずべきことだと思う。
あとBruce Schneierも俺に同意しているから...残念だったな、お前の議論は終わりだ。
言葉は必要以上に厳しかったかもしれない
友達が最初にどれだけこれをマイルドにさせたか、信じられないだろうな。
Linuxディストリビューションは、OpenBSDが自分たちの混乱に適合し適応することを期待するほど、自分たちを高く評価すべきではない
@debazel!!! Me gusta。
なんてこった、完全にデタラメだ。
ああ、その部分は正確だ。
xz-utilsを[CVE-2024-3094][nvd]のせいにするのをやめるべき理由。あと私のETSA Talkもチェックしてね!

CVE-2024-3094、一般には「xz-utilsバックドア」として知られるこれは、世界のサイバーセキュリティにとって危機一髪だった。[Andres Freund][freund]が間一髪で発見していなければ、地球上のほとんどのSSHサーバーが、この攻撃の背後にいる者にrootアクセスを許可し始めていただろう。
残念ながら、あまりにも多くの分析が[悪意あるコード][JiaT75]がどのようにxz-utilsリポジトリに入り込んだかに焦点を当てている。代わりに私は、重要なオープンソースソフトウェアにおける2つの長年の設計上の決定、すなわち[OpenSSHをSystemDにリンクすること][biebl]と、[GNU IFUNC][sourceware]の存在が、この攻撃を可能にしたと主張したい。
始める前に:この議論の多くはLinuxにおける動的リンクの複雑さを扱っている。復習が必要なら、dynamic_linking.mdをチェックしてほしい。
xz-utilsバックドアの高レベルな詳細を概説した優れた記事は山ほどある。例えばDan Goodinの[What we know about the xz Utils backdoor that almost infected the world][goodin1]や、Sam Jamesの[FAQ on the xz-utils backdoor (CVE-2024-3094)][thesamesam] gistなどだ。ここでそれをすべて蒸し返す必要はないので、この記事の目的のために、非常に粗い振り返りを以下に示す:
## なぜLinuxディストリビューションはOpenSSHを改変するのか?
短い答えは、彼らがそうせざるを得ないからです。OpenSSHは
OpenBSDコミュニティによって、OpenBSDコミュニティのために開発されており、彼らは
Linuxなど気にも留めていません。[Portable OpenSSH][mindrot]プロジェクトは、
OpenBSD固有のコンポーネントを汎用POSIXコンポーネントに置き換え、該当する場合は
プラットフォーム固有のコードを加える、ベストエフォートのパッチ集です。
SSHのソフトウェアサプライチェーンは、実際には次のようなものになります:```mermaid
flowchart TD
subgraph OpenBSD Folks
A[OpenBSD]
B[OpenSSH]
H[improvements]
end
B-->A
A-->H
H-->B
B-->C
C[Portable OpenSSH]
subgraph Debian Folks
D[Debian SSH]
G[improvements]
end
C-->D
D-->G
G-->C
subgraph Fedora Folks
J[Fedora SSH]
K[improvements]
end
C-->J
J-->K
K-->C
OpenBSD版のOpenSSHは他のすべての上流に位置し、その改善のほとんどはOpenBSDコミュニティ内からもたらされます。これらの変更は下流のPortable OpenSSHプロジェクトに流れ込み、OpenBSDに固有ではない方法で新機能を再実装しようとします。これにより、SSHはLinux、macOS、FreeBSD、さらにはWindowsといったプラットフォームで動作できるようになります。
しかし、それだけにとどまりません。一部のオペレーティングシステムは、Portable OpenSSHが提供する以上のカスタマイズをさらに適用しています。たとえば、AppleはmacOSのパスワードマネージャーと統合するために、ssh-addに[--apple-use-keychain][keith]フラグを追加しています。
CVE-2024-3094の場合、FedoraとDebianはOpenSSHのフォークに対して独自の[SystemDパッチ][biebl]を維持し、[sshdの再起動時の競合状態][schmidt]を修正しました。そのため、SSHの実際のサプライチェーンは次のようになり始めました:```mermaid
flowchart TD
A[OpenSSH]
B[Portable OpenSSH]
C[Debian SSH]
D[Fedora SSH]
A-->B
B-->C
B-->D
C<-->|SystemD Patches|D
これらのパッチがPortable OpenSSHに取り込まれることはなかった。Portable OpenSSHの人々は[「libsystemdへの依存を受け入れることに興味がなかった」][djmdjm]からだ。そして上流のOpenSSHにも取り込まれることはなかった。OpenBSDにはSystemDをサポートする必要がまったくなかったからだ。
### 「関心の分離」に関する懸念
これは一見無害に見えるが、オープンソース、特にLinuxにおけるはるかに大きな問題の一例である。オペレーティングシステムの重要なコンポーネントが、互いを知らず、互いに話をしない人々によって開発されているのだ。
* SystemD向けにOpenSSHへパッチを当てた人々は、libsystemdがxz-utilsに依存していることを知っていた(あるいは気にかけていた)だろうか?
* SystemDの人々は、xz-utilsがifuncを使い始めたことを知っていた(あるいは気にかけていた)だろうか?
* OpenSSHの人々は、ifuncというものが存在することを知っていた(あるいは気にかけていた)だろうか?OpenBSDでは間違いなく存在しないものだ。
ある意味で、このコミュニケーションの断絶はオープンソースの特徴である。私はあなたにわずらわされることなく、あなたの成果を自分のニーズに合わせて適応させることができる。しかしそれはまた、重要な設計上の前提(従来の動的リンクのプロセスなど)が維持されることを妨げる、ある種の間接性をもたらすこともある。
[コンウェイの法則][conway]の明らかな系として、あなたが自組織の組織図を出荷しているなら、組織図の隙間に潜むバグもまた出荷しているということになる。ここで実際に間違いを犯した個人やチームは存在しないが、後知恵で見れば、攻撃者たちがDebian/FedoraのSSHの左手がxz-utilsの右手が何をしているかを知らないと認識していたことは明らかだ。
## GNU IFUNCは*本来*何をするものなのか?
これは、ある関数のどのバージョンを使いたいかを実行時に決定できるようにするものである。これは、リンカがシンボルを解決する方法に影響を与えるために**任意のコード**を実行する機会を与えることで実現される。

### CPU機能の検出
幅広い種類のx86 CPUで動作しなければならないアプリケーションがあるとしよう。現在のCPUの特定の機能に応じて、同じタスクに対して異なるアルゴリズムを使いたいかもしれない。IFUNCの背後にある元々のアイデアは、関数が最初に呼び出されたときにCPU機能をチェックし、その後はそのCPUに最も適した実装を使えるようにすることだった。
[`cpu_demo.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/cpu_demo.c)を見てみよう:```c
void print_cpu_info() __attribute__((ifunc ("resolve_cpu_info")));
void print_avx2() { printf("AVX2 is present.\n"); }
void print_nope() { printf("AVX2 is missing.\n"); }
static void* resolve_cpu_info(void) {
__builtin_cpu_init();
if (__builtin_cpu_supports("avx2")) {
return print_avx2;
} else {
return print_nope;
}
}
int main() {
print_cpu_info();
return 0;
}
このプログラムは、IFUNCの最も一般的な使用方法を示しています。CPUが特定の機能をサポートしているかどうかを問い合わせ、サポートされている機能に応じて関数の異なる実装を提供します。この場合、私たちの関数 print_cpu_info は、あなたのCPUがどれほど古いかに応じて、「AVX2 is present」または「AVX2 is missing」のいずれかを出力することになります。
IFUNCはCPU機能のプロービングを目的としていますが、リゾルバ内でより複雑なコードを実行することを妨げるものは何もありません。例えば、tty_demo.c は、STDOUTがファイルであるかターミナルであるかに応じて、異なる関数実装をロードする方法を示しています:```c
// Print Green text to the Terminal
void print_to_tty(const char *message) {
const char *green_start = "\033[32m";
const char *color_reset = "\033[0m";
printf("%sTTY: %s%s\n", green_start, message, color_reset);
}
// Print plain text to a file void print_to_file(const char *message) { printf("FILE: %s\n", message); }
void print_message(const char *message)
attribute((ifunc("resolve_print_function")));
void (*resolve_print_function(void))(const char *) { struct termios term;
// Ask the kernel whether stdout is a file or a tty
int result = ioctl(STDOUT_FILENO, TCGETS, &term);
if (result == 0) {
// stdout is a terminal
return print_to_tty;
} else {
// stdout is not a terminal
return print_to_file;
}
}
int main() { print_message("Hello, World!"); return 0; }
これは実際にはIFUNCの意図された使用方法ではありませんが、何が可能かを示しています。宣言したIFUNCを使用する任意のプログラムで、`main`の前に任意のコードを実行できます。
## IFUNCはおそらく悪いアイデアである

GNU IFUNCは実装が難しく、正しく使用するのが困難であり、(性能ツールと謳われているわりに)代替手段よりもそれほど高速ではありません。CVE-2024-3094で見られたように、ソフトウェアサプライチェーン攻撃のための非常に強力なツールでもあります。
IFUNCはGNU C Library内で広く使用されており、それはおそらく問題ありません。彼らはIFUNCが元々開発された対象であり、IFUNCを実際に実装しているコンパイラおよびリンカーチームと密接に連携しています。彼らはトレードオフを理解するのに最適な立場にあり、CPU固有の実装から恩恵を受けるlibc関数は多数存在します。IFUNCはglibcの内部インターフェースと見なし、他のアプリケーションでの使用は避けるべきだと考えます。
### 安全に使用するには混乱しすぎている
ifuncは使用するのがあまりにも困難です。[コーナーケース][nagy]が多すぎ、[公式ドキュメント][gnu-cfa]は[不十分][sourceware]です。これにより、ifuncの採用が簡単であるという誤解をユーザーに与えています。
ifuncが利用可能になってから数年経っても、宣伝されているインターフェースは[機能しませんでした][agner]。GCCの開発者はそれを[間違い][odonell]と呼び、IFUNCの脆弱性を補うための警告の追加を検討しています:
> IFUNCを堅牢にするために必要なglibcのソリューションは整っておらず、壊れる可能性があることをユーザーに警告するためにできることをすべきです。
IFUNCだけではありません。Apple Mach-Oには`.symbol_resolver`と呼ばれる類似の機能があり、彼らはそれを[「追加したことを後悔している」][rjmccall]と述べています。
### RELROを損なう
Global Offset Tableがまだ書き込み可能な間に任意のコードを実行できるようにすることで、[RELRO](https://github.com/robertdfrench/ifuncd-up/blob/trunk/dynamic_linking.md#relro)によって提供される保護は[無意味になります][binarly-io]。
これは注目に値します。なぜなら、RELROは動的にロードされるシンボルの整合性を保護する方法として宣伝されているからです。ユーザーの観点から見ると(コンパイラとリンカーのユーザーであるあなたにとって)、これは[最小驚愕の原則][pola]に違反します:*動的ライブラリをロードする*ことが*動的ライブラリを保護する*ために設計された安全機能を損なうと期待する合理的な人はいません。

### 常に必要なわけではない
この状況に対処する方法は他にも複数あります。それぞれ異なるトレードオフがありますが、すべてIFUNCよりもはるかにシンプルです。これらはすべてIFUNCよりも移植性が高く、理解しやすく、悪用が困難です。
> 「Ifuncは、実行時のマイクロアーキテクチャ固有のコード選択を行うためのまったく愚かな方法にすぎません。」
>
> -- [Rich Felker](https://hachyderm.io/@dalias/112952237145378821)、
> [musl](https://musl.libc.org)のメンテナー。
#### グローバル関数ポインタ
IFUNCが魅力的なのは、開発者が関数選択を*命令的に*ではなく*宣言的に*表現できるからです。しかし、これを命令的に行うのは実際にはそれほど難しくありません。[`static_pointer.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/static_pointer.c)を考えてみましょう。これは実行時にグローバル関数ポインタを解決します:```c
static int (*triple)(int) = 0;
int triple_sse42(int n) { return 3 * n; }
int triple_plain(int n) { return n + n + n; }
void print_fifteen() {
int fifteen = triple(5);
printf("%d\n", fifteen);
}
int main() {
__builtin_cpu_init();
if (__builtin_cpu_supports("sse4.2")) {
triple = triple_sse42;
} else {
triple = triple_plain;
}
print_fifteen();
return 0;
}
これは、リンカに特別な仕掛けを入れてまで避ける必要があるほど悪いことなのでしょうか?
このアプローチの欠点の1つは、関数ポインタ triple が実行時に書き換え可能であるのに対し、IFUNC+RELRO であれば、GOT 内の ifunc アドレスが一度解決された後に不変であることが保証される点です。しかし、少し追加の作業を行えば、[mprotect(2)][mprotect] を使ってそのようなポインタを読み取り専用にマークできます。
LD_PRELOAD の変更コードが必要とする CPU 機能を把握しており、各ケース用に動的ライブラリの別コピーを持っているなら、次のように $LD_PRELOAD で適切なライブラリを指定することで同じことを実現できます:```bash
#!/bin/bash
if (cat /proc/cpuinfo | grep flags | grep avx2 > /dev/null); then
LD_PRELOAD=./myfunc_avx2.so ./my_app
else
LD_PRELOAD=./myfunc_normal.so ./my_app
fi
(`LD_PRELOAD`に馴染みがない場合は、catonmatの["A Simple
`LD_PRELOAD` Tutorial"][catonmat]を参照してください。)
#### 機能の組み合わせごとにバイナリを分ける
実際にサポートする必要がある固有のCPU機能の組み合わせはいくつあるでしょうか。
そもそもいくつ存在するのでしょうか。
一見すると、これは組み合わせ爆発のように見えます。amd64 ISAには、
多数の異なるベクトル演算、仮想化、セキュリティ拡張が存在します。しかし、
これらの機能は実環境で独立して出現するわけではありません。たとえば、
AVX-512を備えていながらSSE4.2やAES-NIを欠くCPUは存在しません。
アプリケーションが必要とするCPU機能と、それらが実際のチップ上で
どのように共存するかを把握しておけば、出荷すべき個別バイナリの数を
判断する助けになります。思ったほど多くはないかもしれません。ほとんどの
パッケージマネージャではインストール時にスクリプトを実行できます。
単一のrpmやdebファイルに複数のバイナリを同梱し、インストール時の
ロジックでホストCPUに最適なものを選ぶことができます。
### 代替手段よりそれほど速くはない
> 長い間明らかだったのは、たとえifuncに性能上の利点があったとしても、
> それは関数呼び出し全体が非常に短く、呼び出しのオーバーヘッドが
> 全体の時間のかなりの部分を占めうる場合に限られるということです。
>
> -- [Rich Felker](https://hachyderm.io/@dalias/113074264762553873)、
> [musl](https://musl.libc.org)のメンテナ。
ifuncの通常の正当化理由が性能に関連していることを踏まえ、
*ifunc自体*がどれほどのオーバーヘッドを引き起こすのかを見てみたくなりました。
結局のところ、最適化する価値のある関数はおそらく頻繁に呼び出されるので、
関数呼び出しのオーバーヘッドは認識しておく価値があります。
これを明らかにするために、*動的に解決される*関数をタイトなループで
繰り返し呼び出す実験を設計しました。
[`speed_demo/ifunc`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/ifunc/main.c)と
[`speed_demo/pointer`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/pointer/main.c)を見てください。
これらのプログラムはどちらも同じ処理(静的カウンタのインクリメント)を
行いますが、インクリメンタ関数の解決方法が異なります。前者はGNU IFUNCを
利用し、後者は昔ながらの関数ポインタに依存しています。
全体のロジックは次のとおりです。
1. リゾルバ関数を呼び出して、どのインクリメンタを使うかを決定する。
1. この結果をどこかに記録する(GOT内、または関数ポインタとして)。
1. このインクリメンタ関数を数十億回呼び出して、そのコストを見積もる。
対照実験として、
[`speed_demo/fixed`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/fixed/main.c)もあり、これは同じ
インクリメンタ処理を行いますが、動的に解決される関数を一切使いません。
これは、実行時間のうちどれだけが関数呼び出しに費やされ、どれだけが
単なる加算に費やされているかを見積もる助けになります。
Makefileのターゲット`rigorous_speed_demo`は、これらの各プログラムを
複数回実行し、その性能に関する簡単な統計を生成します。これらの数値は
もちろんハードウェアによって変わりますが、`fixed`テストが比較のための
ベースラインとして機能します。
| *結果* | LOW | HIGH | AVG |
|-----------|------|------|-------|
| fixed | 2.93 | 4.20 | 3.477 |
| ifunc | 9.50 | 10.56| 9.986 |
| pointer | 6.23 | 7.44 | 6.791 |
ここでわかるのは、ifuncが昔ながらの関数ポインタを使う場合と比べて
無視できないオーバーヘッドを持つということです。平均すると、私の
ハードウェアでは、ifunc関数を20億回呼び出すのに、関数ポインタを
20億回呼び出す場合の約2倍の時間がかかります。
これは実生活で問題になるでしょうか。まったくそうではありません。
最適化する価値のある関数は、ここで分析している「1ずつインクリメントする」
関数よりもはるかに高コストです。これが興味深いのは、GNU IFUNCが性能の
ための恩恵であると主張しているにもかかわらず、関数ポインタよりも多くの
コストを負担しているように見えるからにすぎません。
#### 他の手法の性能
ifuncよりも遅い他の手法もあります。`super_rigorous_speed_demo`を見てください。
これはさらに2つの実験を組み込んでいます。
[`speed_demo/upfront`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/upfront/main.c)と
[`speed_demo/always`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/always/main.c)です。
`speed_demo/upfront`は`speed_demo/pointer`と同様に動作しますが、
関数ポインタを保持する代わりに、CPU機能チェックの結果をグローバル変数に
保存します。これには依然として、これらのグローバル変数の値に基づいて
どの実装を使うかを決定する「リゾルバ」関数を最初に実行する必要があります。
この手法はifuncよりも遅いことがわかりますが、関数ポインタを保存するよりも
安全でもあります。関数ポインタは任意の値に設定できますが、ブールフラグは
そうではありません。したがって、これらの変数を変更できる攻撃者は、
プログラムを*遅くする*ことはできても、プログラムを*異なる動作*に
することはできません。
`speed_demo/always`は最も遅い手法になるように設計されています。実装が
必要になるたびに必要なCPU機能をすべてチェックし、その場で1つを選びます。
興味深いことに、この手法は他のどの手法よりも著しく遅いわけではありません。
チェックするCPU機能が1つだけの場合、ifuncよりもわずかに遅いだけです。
| TEST | LOW | HIGH | AVG |
|---------|------|-------|---------|
| fixed | 5.02 | 5.70 | 5.37 |
| pointer | 6.40 | 7.02 | 6.66 |
| ifunc | 8.56 | 11.11 | 9.64 |
| upfront | 9.24 | 9.41 | 9.33333 |
| always | 10.07| 10.56 | 10.2333 |
## 結論
GNU IFUNCはgcc/ld.soのニッチな機能であり、CVE-2024-3094で使われるまで
ほとんど誰も知りませんでした。自明でない落とし穴があり、ドキュメントも
不十分です。リンカが`main`の前に、プロセスイメージの重要な部分が
初期化され保護される前に、任意のコードを実行できるようにすることで、
プログラミングの最も基本的な前提の1つを損なっています。それは、
ライブラリをロードするという行為そのものが本質的にプログラムを
*変えてはならない*という前提です。
IFUNCの性能上の利点は実在しますが、代替手段よりも意味のあるほど
優れているわけではありません。複数のCPUに最適化された単一のバイナリを
配布するというシンプルさは非常に魅力的ですが、それはより単純な手法
(関数ポインタなど)で実現できます。
私はIFUNCはgccでデフォルトで無効にすべきだと考えます。有効にするには、
`--enable-cve-2024-3094`のような怖そうなフラグを必要とすべきです。
libc以外でこれを使う人は、代替手段が適切でないことを示す厳密で
よく調査された論拠を提示することが期待されるべきです。

<!-- REFERENCES -->
[aes-ni]: https://en.wikipedia.org/wiki/AES_instruction_set
[agner]: https://www.agner.org/optimize/blog/read.php?i=167
[biebl]: https://salsa.debian.org/ssh-team/openssh/-/commit/818791ef8edf087481bd49eb32335c8d7e1953d6
[binarly-io]: https://github.com/binarly-io/binary-risk-intelligence/tree/master/xz-backdoor
[catonmat]: https://catonmat.net/simple-ld-preload-tutorial
[conway]: https://en.wikipedia.org/wiki/Conway%27s_law
[djmdjm]: https://github.com/openssh/openssh-portable/pull/251#issuecomment-2027935208
[fr0gger]: https://infosec.exchange/@fr0gger/112189232773640259
[freund]: https://www.openwall.com/lists/oss-security/2024/03/29/4
[gnu-cfa]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-ifunc-function-attribute
[goodin1]: https://arstechnica.com/security/2024/04/what-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world/
[hn]: https://news.ycombinator.com/item?id=48056749
[jasoncc]: https://jasoncc.github.io/gnu_gcc_glibc/gnu-ifunc.html#relocations-and-pic
[JiaT75]: https://github.com/tukaani-project/xz/commit/cf44e4b7f5dfdbf8c78aef377c10f71e274f63c0
[keith]: https://keith.github.io/xcode-man-pages/ssh-add.1.html#apple-use-keychain
[mindrot]: https://anongit.mindrot.org/openssh.git
[mprotect]: https://www.man7.org/linux/man-pages/man2/mprotect.2.html
[musl]: https://musl.libc.org
[nagy]: https://sourceware.org/legacy-ml/libc-alpha/2015-11/msg00108.html
[nvd]: https://nvd.nist.gov/vuln/detail/CVE-2024-3094
[odonell]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70082#c0
[OpenSSH9.8p1]: https://www.openssh.com/releasenotes.html#9.8p1
[openssh-unix-dev]: https://marc.info/?l=openssh-unix-dev&m=171288895109872&w=2
[pola]: https://en.wikipedia.org/wiki/Principle_of_least_astonishment
[rjmccall]: https://reviews.llvm.org/D139163#3993795
[schmidt]: https://bugzilla.redhat.com/show_bug.cgi?id=1381997#c4
[sourceware]: https://sourceware.org/glibc/wiki/GNU_IFUNC
[thesamesam]: https://gist.github.com/thesamesam/223949d5a074ebc3dce9ee78baad9e27#design