seccomp_notify_receive()
の実装では、次のものを使用します。
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0)
return -ECANCELED;
errno
のエラーに関係なく、 -ECANCELED
を返します。
straceを使用してデバッグしているときに、シグナルSIGURGが原因でioctlがEINTRを返していることがわかりました。
1448850 ioctl(7, SECCOMP_IOCTL_NOTIF_RECV <unfinished ...>
1448230 getpid() = 1448217
1448230 tgkill(1448217, 1448850, SIGURG) = 0
1448850 <... ioctl resumed>, 0x7fc65d49ecd0) = -1 EINTR (Interrupted system call)
シグナルSIGURLはGolangランタイムによって生成されます。詳細についてはhttps://go-review.googlesource.com/c/go/+/232862/を参照してください。
そのレポートによると、EINTRを取得するときにsyscallを再発行する必要があります。
理想的には、libseccompがそれを処理する必要があるため、ユーザーに対して透過的です。
回避策として、プログラムでseccomp_notify_receive()を呼び出してシステムコールを再発行し、同じパターンであるためseccomp_notify_respond()
でも同じことを実行しようとしました。
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0)
return -ECANCELED;
しかし、時々私は戻り値errno = ENOENTを取得します。
1497880 ioctl(12, SECCOMP_IOCTL_NOTIF_SEND, 0x7f718fd28e10) = -1 ENOENT (No such file or directory)
libseccompでは、ENOENT(ターゲットプロセスがSECCOMP_IOCTL_NOTIF_SEND
を呼び出す直前に中断された)とEINTR(seccompエージェントがSIGURG信号によって中断された)を区別できません。
さらに、Golangからerrno
を読み取ることができないため、回避策がありません。
こんにちは@alban。
seccomp_attr_set(3)
のマンページでSCMP_FLTATR_API_SYSRAWRC
を見てください。
libseccompがデフォルトの-ECANCELEDの代わりにシステムエラーコードを呼び出し元に返すかどうかを指定するフラグ。 デフォルトはオフです(値== 0)。
この場合、異なるカーネル/ libcバージョン間でlibseccompAPIの安定したリターンコードの約束を確実にするために、 -ECANCELED
を強制します。 実際のkernel / libcリターンコードを見たい発信者にはSCMP_FLTATR_API_SYSRAWRC
を提供しますが、その属性を有効にした場合、安定したリターンコードは保証されないことに注意してください。
この問題はNOTABUGとして締めくくりますが、さらに質問がある場合は、ここで引き続き議論してください。 対処する必要のある実際の問題があると思われる場合は、再開できます。
こんにちは@pcmoore 、迅速な返信をありがとう。
実際のカーネル/ libcリターンコードを引き続き確認したい発信者向けに、SCMP_FLTATR_API_SYSRAWRCを提供しています。
しかし、新しい機能
_rc_filter_sys()
_rc_filter()
を使用しているので、私が間違っていなければ、 SCMP_FLTATR_API_SYSRAWRC
は効果がないはずです。
しかし、新しい関数は... _rc_filter_sys()ではなく_rc_filter()を使用しています...
あぁ…ごめんなさい。 実際には、APIレベルよりも深く、「system.c」を調べてください。libseccompシステムレベルの呼び出しは、APIレベルの関数までそれらを伝播しないことがわかります。 マンページを見ると、通知APIのSCMP_FLTATR_API_SYSRAWRC
については何も主張していないようです(反例についてはseccomp_load(3)
を参照してください)。
現在、この動作については何も主張していないので、先に進んでこれをRFEとして再開します。
@drakenclimber v2.5.1のリリースを遅くしたくないので、これはv2.6.x(おそらくv2.5.2)の素材だと思います。 そうでなければ私に知らせてください。
ありがとう!
記録のために、私はGolang側で回避策を見つけて、次のコードパターンでENOENTとEINTRを区別できるようにします。
- if retCode := C.seccomp_notify_respond(C.int(fd), resp); retCode != 0 {
- return errRc(retCode)
+ for {
+ retCode, errno := C.seccomp_notify_respond(C.int(fd), resp)
+ if errno == syscall.EINTR {
+ continue
+ }
+ if errno == syscall.ENOENT {
+ return errno
+ }
+ if retCode != 0 {
+ return errRc(retCode)
+ }
+ break
}
これにより、libseccomp-2.5.0で正常に動作するようです。
最も参考になるコメント
あぁ…ごめんなさい。 実際には、APIレベルよりも深く、「system.c」を調べてください。libseccompシステムレベルの呼び出しは、APIレベルの関数までそれらを伝播しないことがわかります。 マンページを見ると、通知APIの
SCMP_FLTATR_API_SYSRAWRC
については何も主張していないようです(反例についてはseccomp_load(3)
を参照してください)。現在、この動作については何も主張していないので、先に進んでこれをRFEとして再開します。
@drakenclimber v2.5.1のリリースを遅くしたくないので、これはv2.6.x(おそらくv2.5.2)の素材だと思います。 そうでなければ私に知らせてください。