Bug #5429

64ビットなFreeBSDのioctlでビット31が1なリクエストの時の不具合

Added by Makoto Kishimoto over 2 years ago. Updated 5 months ago.

[ruby-dev:44589]
Status:Closed
Priority:Normal
Assignee:Makoto Kishimoto
Category:-
Target version:-
ruby -v:- Backport:

Description

64ビットなFreeBSD 8において、ioctl(2)のプロトタイプ宣言は以下のように
なっていて、

int
ioctl(int d, unsigned long request, ...);

ビット31(32ビットであればMSB)が1なリクエストでも、上位ビットがゼロ拡張
された64ビット値を期待しています。

それに対しrubyのtrunkではIO#ioctlの引数の値の範囲は符号付き32ビットに
なっていて、最終的にio.cのnogvliocntlに、intを引数としたioctlの
呼び出しがあり、ビット31が1なリクエストは符号拡張されて、ioctlを呼ぶことに
なり、

その結果システムメッセージに、(SNDCTLDSPSPEED の例)
WARNING pid 82043 (initial thread): ioctl sign-extension ioctl ffffffffc0045002
というようなウォーニングが出ます(一応目的の動作はしている)。

回避する修正は (unsigned) とキャストを入れればいいように思うのですが、
他のプラットフォームでの問題や、IO#ioctlの受け入れるべき引数の値の範囲の
問題などがあるので、とりあえずチケットのみ登録します。


Related issues

Related to Backport93 - Backport #6127: IO#ioctl range error in 1.9.3 Closed 03/10/2012
Duplicated by ruby-trunk - Bug #6427: ruby ioctl: Error integer 2148012656 too big to convert t... Rejected 05/12/2012

Associated revisions

Revision 33716
Added by Motohiro KOSAKI over 2 years ago

  • io.c (ioctlreqt): Type of req argument of ioctl() depend on platform. Moreover almost all linux ioctl can't be represented by 32bit integer (i.e. MSB is 1). We need wrap ioctl argument type. [Bug #5429]
  • io.c (struct ioctl_arg): ditto.
  • io.c (rb_ioctl): ditto.
  • test/ruby/testio.rb (testioctl_linux): add a testcase for ioctl

History

#1 Updated by Yui NARUSE over 2 years ago

ざっと調べてみましたが、

== POSIX
int ioctl(int fildes, int request, ... /* arg */);
http://pubs.opengroup.org/onlinepubs/9699919799/functions/ioctl.html

== AIX
int ioctl (fd, request, .../arg/)
int fd;
int request;
int .../arg/;
http://publib.boulder.ibm.com/infocenter/aix/v6r1/topic/com.ibm.aix.commtechref/doc/commtrf2/ioctl.htm

== Solaris
int ioctl(int fildes, int request, /* arg */ ...);
http://download.oracle.com/docs/cd/E19963-01/html/821-1463/ioctl-2.html

== HP-UX
int ioctl(int fildes, int request, ... /* arg */);
http://nixdoc.net/man-pages/HP-UX/man2/ioctl.2.html

== Tru64
int ioctl(
int fildes,
int request,
... /* arg */ );
http://nixdoc.net/man-pages/Tru64/man2/ioctl.2.html

== IRIX
int ioctl (int fildes, int request, ...);
http://nixdoc.net/man-pages/irix/man2/ioctl.2.html

== Linux
extern int ioctl (int _fd, unsigned long int _request, ...) __THROW;
manpageは以下の通りだが、実際のヘッダは異なる。
int ioctl(int d, int request, ...);
https://www.kernel.org/doc/man-pages/online/pages/man2/ioctl.2.html

== NetBSD
int ioctl(int d, unsigned long request, ...);
http://netbsd.gw.com/cgi-bin/man-cgi?ioctl+2+NetBSD-current

== OpenBSD
int ioctl(int d, unsigned long request, ...);
http://www.openbsd.org/cgi-bin/man.cgi?query=ioctl&sektion=2&format=html

== Darwin
int ioctl(int fildes, unsigned long request, ...);
http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/ioctl.2.html

いやー、やっぱり Linux はダメですね!ってのはおいといて、
商用 UNIX は int、それ以外は unsigned long っぽいですね。

#2 Updated by Motohiro KOSAKI over 2 years ago

  • ruby -v changed from ruby 1.9.4dev (2011-10-09 trunk 33439) [x86_64-freebsd8.2] to -

== Linux
extern int ioctl (int _fd, unsigned long int _request, ...) __THROW;
manpageは以下の通りだが、実際のヘッダは異なる。
int ioctl(int d, int request, ...);
https://www.kernel.org/doc/man-pages/online/pages/man2/ioctl.2.html

いやー、やっぱり Linux はダメですね!ってのはおいといて、
商用 UNIX は int、それ以外は unsigned long っぽいですね。

うう・・すいません
Linuxのmanはこちらであずかります。man pageメンテナが夜逃げ中なので
ちょっと時間かかるかも

#3 Updated by Motohiro KOSAKI over 2 years ago

途中までやってみましたが、ioctlはunsigned longでもfcntl はintなので、もうちょっと工夫がいりますね。

#4 Updated by Motohiro KOSAKI over 2 years ago

fcntl は全プラットフォーム int のようである。めでたい

== Linux
int fcntl(int fd, int cmd, ... /* arg */ );
https://www.kernel.org/doc/man-pages/online/pages/man2/fcntl.2.html

== FreeBSD
int fcntl(int fd, int cmd, ...);
http://fuse4bsd.creo.hu/localcgi/man-cgi.cgi?fcntl+2

== NetBSD
int fcntl(int fd, int cmd, ...);
http://www.daemon-systems.org/man/fcntl.2.html

== OpenBSD
int fcntl(int fd, int cmd, ...);
http://nixdoc.net/man-pages/OpenBSD/fcntl.2.html

== AIX
int fcntl(int descriptor,
int command,
...)

http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Fapis%2Ffcntl.htm

== Solaris
int fcntl(int fildes, int cmd, /* arg */ ...);
http://download.oracle.com/docs/cd/E19963-01/html/821-1463/fcntl-2.html

== HP-UX
int fcntl(int fildes, int cmd, ... /* arg */);
http://nixdoc.net/man-pages/HP-UX/man2/fcntl.2.html

== Tru64
int fcntl(
int filedes,
int request [,
int argument |,
struct flock *argument |,
advfs_opT argument] ); int dup(
int filedes ); int dup2(
int old,
int new );
http://nixdoc.net/man-pages/Tru64/man2/fcntl.2.html

== IRIX
int fcntl (int fildes, int cmd, ... /* arg */);
http://nixdoc.net/man-pages/irix/man2/fcntl.2.html

== Darwin
int fcntl(int fildes, int cmd, ...);
http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/fcntl.2.html

#5 Updated by Motohiro KOSAKI over 2 years ago

よく見たら、fcntlのときに IOCPARMLEN を呼んでるのはバグですね。ioctlのcmdしか IOCPARMLEN でデコードできません。これが動くのはfcntlの引数にstring渡す人が一人もいなかったからでしょうね。

#6 Updated by Motohiro KOSAKI over 2 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r33716.
Makoto, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • io.c (ioctlreqt): Type of req argument of ioctl() depend on platform. Moreover almost all linux ioctl can't be represented by 32bit integer (i.e. MSB is 1). We need wrap ioctl argument type. [Bug #5429]
  • io.c (struct ioctl_arg): ditto.
  • io.c (rb_ioctl): ditto.
  • test/ruby/testio.rb (testioctl_linux): add a testcase for ioctl

#7 Updated by Motohiro KOSAKI over 2 years ago

  • Tracker changed from Bug to Backport
  • Project changed from ruby-trunk to Backport93
  • Category deleted (core)
  • Status changed from Closed to Assigned
  • Assignee set to Yuki Sonoda
  • Target version deleted (2.0.0)

r33711 - r33721 のバックポートを依頼します

#8 Updated by Motohiro KOSAKI over 2 years ago

なかださんがミスを直してくれました。
r33724, r33727 も追加で。

#9 Updated by Motohiro KOSAKI over 2 years ago

Chikanaga さんのレビューコメントを反映し、r33728 をコミットしました。

#10 Updated by Ayumu AIZAWA over 2 years ago

あいざわです

r33720で追加されたテストケースが Mac OSX SnowLeopardで失敗します。
以下のパッチ動くようにはなりますが、これってTempfile#openのバグなんでしょうか?


git diff --no-prefix
diff --git test/ruby/testio.rb test/ruby/testio.rb
index 6382cd7..e1ec389 100644
--- test/ruby/testio.rb
+++ test/ruby/test
io.rb
@@ -1919,7 +1919,8 @@ End
end

def test_fcntl_dupfd
  • Tempfile.open(self.class.name) do |f|
  • Tempfile.new(self.class.name) do |f|
  •  f.open
    fd = f.fcntl(Fcntl::F_DUPFD, 500)
    begin
      assert_equal(fd, 500)
    

    = パッチあてるまえの実行結果
    make test-all TESTS='test/ruby/testio.rb'
    ./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext
    -- --disable-gems "./test/runner.rb" --ruby="./miniruby -I./lib -I.
    -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems"
    test/ruby/test
    io.rb
    Run options: "--ruby=./miniruby -I./lib -I. -I.ext/common
    ./tool/runruby.rb --extout=.ext -- --disable-gems"

    Running tests:

    ...........................................E...................................................................

    Finished tests in 4.617944s, 24.0367 tests/s, 122.3488 assertions/s.

    1) Error:
    testfcntldupfd(TestIO):
    Errno::EINVAL: Invalid argument -
    /var/folders/uN/uN7thnp5EriPwEKPnKdfvk+++TI/-Tmp-/TestIO20111114-57558-itj964
    /users/ayumin/github/ruby/test/ruby/testio.rb:1923:in `block in
    test
    fcntl_dupfd'

    111 tests, 565 assertions, 0 failures, 1 errors, 0 skips

    ruby -v: ruby 2.0.0dev (2011-11-14 trunk 33748) [x86_64-darwin10.8.0]
    make: *** [yes-test-all] Error 1

#11 Updated by Tomoyuki Chikanaga over 2 years ago

あいざわさんのパッチだと、Tempfile.new がブロックを yield しないので、assert を通っていないだけのようです。

SnowLeopard の man fcntl によると

 [EINVAL]           Cmd is F_DUPFD and arg is negative or greater than the maximum allowable number (see getdtablesize(2)).

で getdtablesize() は 256 を返すのでとりあえず 500 -> 255 にすると通るようになりました。
500 というのは他の open 済みの fd と重複しない程度に大きな数字ということだと思うので、とりあえず 255 にしてしまってもいいでしょうか。ちゃんと空き fd を確保してそこに dup するようにしたほうがいいのかもしれませんが。

#12 Updated by Motohiro KOSAKI over 2 years ago

2011年11月14日11:25 Tomoyuki Chikanaga nagachika00@gmail.com:

Issue #5429 has been updated by Tomoyuki Chikanaga.

あいざわさんのパッチだと、Tempfile.new がブロックを yield しないので、assert を通っていないだけのようです。

SnowLeopard の man fcntl によると

[EINVAL]           Cmd is F_DUPFD and arg is negative or greater than the maximum allowable number (see getdtablesize(2)).

で getdtablesize() は 256 を返すのでとりあえず 500 -> 255 にすると通るようになりました。
500 というのは他の open 済みの fd と重複しない程度に大きな数字ということだと思うので、とりあえず 255 にしてしまってもいいでしょうか。ちゃんと空き fd を確保してそこに dup するようにしたほうがいいのかもしれませんが。

うーん、あんまり凝ったことをするとまた別のOSの制限にひっかかるかもしれないので
255でお願いできるとうれしいなあ。
fcntlのテストが全然ないから追加したんだけど、ポータブルに動きそうなコマンドって
難しいのよねえ・・

#13 Updated by Motohiro KOSAKI over 2 years ago

これでバックポート対象に r33752, r33753 が追加ですね

#14 Updated by Ayumu AIZAWA over 2 years ago

近永さんありがとうございます。
r33753 でなおしておきました。

2011年11月15日1:41 KOSAKI Motohiro kosaki.motohiro@gmail.com:

2011年11月14日11:25 Tomoyuki Chikanaga nagachika00@gmail.com:

Issue #5429 has been updated by Tomoyuki Chikanaga.

あいざわさんのパッチだと、Tempfile.new がブロックを yield しないので、assert を通っていないだけのようです。

SnowLeopard の man fcntl によると

[EINVAL]           Cmd is F_DUPFD and arg is negative or greater than the maximum allowable number (see getdtablesize(2)).

で getdtablesize() は 256 を返すのでとりあえず 500 -> 255 にすると通るようになりました。
500 というのは他の open 済みの fd と重複しない程度に大きな数字ということだと思うので、とりあえず 255 にしてしまってもいいでしょうか。ちゃんと空き fd を確保してそこに dup するようにしたほうがいいのかもしれませんが。

うーん、あんまり凝ったことをするとまた別のOSの制限にひっかかるかもしれないので
255でお願いできるとうれしいなあ。
fcntlのテストが全然ないから追加したんだけど、ポータブルに動きそうなコマンドって
難しいのよねえ・・

#15 Updated by Akira Tanaka over 2 years ago

2011年11月15日1:41 KOSAKI Motohiro kosaki.motohiro@gmail.com:

うーん、あんまり凝ったことをするとまた別のOSの制限にひっかかるかもしれないので
255でお願いできるとうれしいなあ。

たしか OpenBSD のデフォルトの file descriptor limit はもっと少なかったような。

http://www.openbsd.org/cgi-bin/cvsweb/src/etc/login.conf.in?rev=1.5
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/mklogin.conf?rev=1.6

によると 128 ですかね。
(履歴を見ると以前は 64 だった?)

それより小さくしといた方が面倒がないんじゃないでしょうか。
--
[田中 哲][たなか あきら][Tanaka Akira]

#16 Updated by Motohiro KOSAKI over 2 years ago

うーん、あんまり凝ったことをするとまた別のOSの制限にひっかかるかもしれないので
255でお願いできるとうれしいなあ。

たしか OpenBSD のデフォルトの file descriptor limit はもっと少なかったような。

http://www.openbsd.org/cgi-bin/cvsweb/src/etc/login.conf.in?rev=1.5
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/mklogin.conf?rev=1.6

によると 128 ですかね。
(履歴を見ると以前は 64 だった?)

それより小さくしといた方が面倒がないんじゃないでしょうか。

そのようにしました

#17 Updated by Motohiro KOSAKI about 2 years ago

思ったよりもパッチが大きくなったのと元報告者のKishimoto さんからFreeBSDでのフィードバックが得られていないので1.9.3ではrejectにしようかと考えています。ご意見お聞かせください

#18 Updated by Yui NARUSE about 2 years ago

  • Assignee changed from Yuki Sonoda to Makoto Kishimoto
  • Priority changed from Low to Normal

#19 Updated by Makoto Kishimoto about 2 years ago

とりあえず今気づきましたというACKだけ。
特に何か「これは困る」というものではないので、
1.9.3へのバックポートは、なしで、私はかまいません。

trunkで問題が起きないことを確認すればいいんですよね?
(さて問題のスクリプトはハードディスクのどこだっけ……)

#20 Updated by Makoto Kishimoto about 2 years ago

確認しました。
最初の報告にあるような、ウォーニングシステムメッセージは出なくなっています。
クローズお願いします。

#21 Updated by Yui NARUSE about 2 years ago

  • Tracker changed from Backport to Bug
  • Project changed from Backport93 to ruby-trunk
  • Status changed from Assigned to Closed

Makoto Kishimoto wrote:

とりあえず今気づきましたというACKだけ。
特に何か「これは困る」というものではないので、
1.9.3へのバックポートは、なしで、私はかまいません。

じゃあ、なしで。

trunkで問題が起きないことを確認すればいいんですよね?
(さて問題のスクリプトはハードディスクのどこだっけ……)

問題のスクリプトとやらをこのチケットに貼っておいて頂けますか。

#22 Updated by Makoto Kishimoto about 2 years ago

たいして長くないので添付じゃなくインラインにしますが、

SNDCTLDSPSPEED = 0xc0045002 - 0x100000000
SNDCTLDSPSTEREO = 0xc0045003 - 0x100000000
SNDCTLDSPSETFMT = 0xc0045005 - 0x100000000

AFMTS16LE = 0x00000010

open("/dev/dsp", "w:ASCII-8BIT"){|dsp|
ioarg = [AFMTS16LE].pack "i!"
dsp.ioctl SNDCTLDSPSETFMT, ioarg
ioarg = [44100].pack "i!"
dsp.ioctl SNDCTLDSPSPEED, ioarg
ioarg = [1].pack "i!"
dsp.ioctl SNDCTLDSPSTEREO, ioarg

loop {
buf = STDIN.read 4
break unless buf
buf.force_encoding "ASCII-8BIT"
dsp.write buf
}
}

こんな感じです。CDからリップした、

track01.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz

こんな感じのwavを標準入力から食わせると音が出ます。

#23 Updated by Yui NARUSE 5 months ago

  • ruby -v set to -

メモ: Linux の ioctl の manpage がなおったらしい
Linux's bug
http://mkosaki.blog46.fc2.com/blog-entry-1246.html

Also available in: Atom PDF