Linuxでは ioctlは
/usr/include/sys/ioctl.h:
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
というプロトタイプになっているんだけど、なぜか manでは
int ioctl(int d, int request, ...);
と書いてあり、一致していないという問題があった。
実装がBSD互換で、manがOpenGroupのSUS準拠だったわけだね。
わたしがmanの問題だとおもって2012年1月にmanにチケットきったんだけどほうちされてて、
https://bugzilla.kernel.org/show_bug.cgi?id=42705
2012年7月にはLinusが glibcの実装の方を直すべきと主張して、glibcのほうにチケットを発行、
http://sourceware.org/bugzilla/show_bug.cgi?id=14362
どうなるのかやきもきしていたが、Michael 的にはmanは実装と一致すべきと思ったのだろう、
manを変更したとの連絡がとどいた。
これで使用法誤解がへるといいな。
以下、技術的および歴史的な状況の解説
・当初、ioctlはPOSIXに含まれていなかったのでBSD系は引数がunsigned long, SYSV系がintと分裂してた。
・glibcは、すごく昔の段階で多数派のBSDに日和った
・STREAMSをPOSIXに含めるときにioctlがPOSIXに追加された。しかし、それは(規格上は)STREAMS専用インタフェースで他の用途はしらんがな状態。かつ最新の規格ではSTREAMSはすでにobsolete
・世間の大多数のソフトは引数をintだと決め打っている。Macに至ってはカーネルが(BSD互換なので)unsigned longなのに、システムコンポーネントが int だと思って処理しているような箇所があり、
int action = (level ? TIOCSBRK : TIOCCBRK);
...
ioctl(fd, action, ...);
というコードが符号拡張で0xFFFFFFFF.... となってしまって、常に ENOTTY になってしまう有り様だった(Linusいわく)
・Linuxでは、そういうアホなユーザーランドへの対策として、64bitでも上位32bitは無視しているのでこの問題はおきない。たとえ、同様のミスをユーザーランドがしたとしても
・これをもって、Linusはカーネルはintでいいようになっていると主張していたが、manのほうのチケットでDavidが指摘したように、実際には最上位ビットも使っているので unsigned int 相当の動きをしている(Linusは暗黙の変換でうまくいくと主張していたが、まあ、そこはそれ)
・Linus的には 「So please fix the ioctl() declaration. "unsigned long" is misleading and actively incorrect and can cause bugs on non-Linux operating systems.」とglibcが直したほうが、よそのOSでのバグがなくなるんだと主張していたが、glibc関係者からは「Macにアホなバグがるから、glibcを直せってのはロジックが無茶苦茶だ」という反発がじゃっかんあったりなかったり。
とかなんとか迷走していた。
なお余談だが、Rubyもまったく同じバグが最近まであったのだが、1.9.3でわたしが直した。
備忘録なので、このエントリに特に結論はない。
/usr/include/sys/ioctl.h:
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
というプロトタイプになっているんだけど、なぜか manでは
int ioctl(int d, int request, ...);
と書いてあり、一致していないという問題があった。
実装がBSD互換で、manがOpenGroupのSUS準拠だったわけだね。
わたしがmanの問題だとおもって2012年1月にmanにチケットきったんだけどほうちされてて、
https://bugzilla.kernel.org/show_bug.cgi?id=42705
2012年7月にはLinusが glibcの実装の方を直すべきと主張して、glibcのほうにチケットを発行、
http://sourceware.org/bugzilla/show_bug.cgi?id=14362
どうなるのかやきもきしていたが、Michael 的にはmanは実装と一致すべきと思ったのだろう、
manを変更したとの連絡がとどいた。
これで使用法誤解がへるといいな。
以下、技術的および歴史的な状況の解説
・当初、ioctlはPOSIXに含まれていなかったのでBSD系は引数がunsigned long, SYSV系がintと分裂してた。
・glibcは、すごく昔の段階で多数派のBSDに日和った
・STREAMSをPOSIXに含めるときにioctlがPOSIXに追加された。しかし、それは(規格上は)STREAMS専用インタフェースで他の用途はしらんがな状態。かつ最新の規格ではSTREAMSはすでにobsolete
・世間の大多数のソフトは引数をintだと決め打っている。Macに至ってはカーネルが(BSD互換なので)unsigned longなのに、システムコンポーネントが int だと思って処理しているような箇所があり、
int action = (level ? TIOCSBRK : TIOCCBRK);
...
ioctl(fd, action, ...);
というコードが符号拡張で0xFFFFFFFF.... となってしまって、常に ENOTTY になってしまう有り様だった(Linusいわく)
・Linuxでは、そういうアホなユーザーランドへの対策として、64bitでも上位32bitは無視しているのでこの問題はおきない。たとえ、同様のミスをユーザーランドがしたとしても
・これをもって、Linusはカーネルはintでいいようになっていると主張していたが、manのほうのチケットでDavidが指摘したように、実際には最上位ビットも使っているので unsigned int 相当の動きをしている(Linusは暗黙の変換でうまくいくと主張していたが、まあ、そこはそれ)
・Linus的には 「So please fix the ioctl() declaration. "unsigned long" is misleading and actively incorrect and can cause bugs on non-Linux operating systems.」とglibcが直したほうが、よそのOSでのバグがなくなるんだと主張していたが、glibc関係者からは「Macにアホなバグがるから、glibcを直せってのはロジックが無茶苦茶だ」という反発がじゃっかんあったりなかったり。
とかなんとか迷走していた。
なお余談だが、Rubyもまったく同じバグが最近まであったのだが、1.9.3でわたしが直した。
備忘録なので、このエントリに特に結論はない。
コメント