Libseccomp: RFE:より正確に定義されたエラー戻り値

作成日 2017年10月07日  ·  24コメント  ·  ソース: seccomp/libseccomp

システムコールに関するlibseccompエラーの戻り値を見ると、次の理由のどれがエラーの原因であるかを判別できません。

  1. 一部のアーチにsyscallが存在しません
  2. 一部のアーチではsyscallを一致させることができません(多重化されているため、socket / socketcallを考えてください)
  3. その他のエラーケース

seccompフィルターを作成するとき、呼び出し元はこれらの理由のいくつかを致命的と見なす可能性がありますが、他の理由は考慮しない可能性があるため、より詳細なエラー情報( -EINVALよりも幅広いエラー値のセット)が必要になります。

systemd PR6952も参照してください。

enhancement prioritmedium

最も参考になるコメント

まことにありがとうございます。 中途半端なパッチセットを持っています。仕上げて、レビュー用のPRとして提出します。

全てのコメント24件

こんにちは@topimiettinen 、申し訳ありませんが、これに到達するのに非常に時間がかかりましたが、これを修正する時が来たと思います。

@drakenclimberこれはやっかいなことになります。 すべてのlibseccompAPIには、この時点でマンページが必要です(必要がない場合は、問題を作成する必要があります)。すべてのマンページには、RETURNVALUEセクションに手で波打つ「エラー時の負の値」コメントがあります。 私たちは次のことをする必要があると思います:

  • 各API呼び出しを手動で監査して、可能な戻り値のリストを生成します
  • これらの戻り値が意味をなすかどうかを判断し、意味がない場合はコードを変更します
  • エラーコードが示す内容の簡単な説明とともに、関連するマンページに考えられる各戻り値を文書化します

考え?

こんにちは@topimiettinen 、申し訳ありませんが、これに到達するのに非常に時間がかかりましたが、これを修正する時が来たと思います。

@drakenclimberこれはやっかいなことになります。 すべてのlibseccompAPIには、この時点でマンページが必要です(必要がない場合は、問題を作成する必要があります)。すべてのマンページには、RETURNVALUEセクションに手で波打つ「エラー時の負の値」コメントがあります。 私たちは次のことをする必要があると思います:

* manually audit each API call to generate a list of possible return values

* decide if these return values make sense, modify the code if they don't

* document each possible return value in the associated manpage with a brief explanation of what the error code indicates

考え?

ダン....私はあなたが上に書いたすべて、特に必要な努力に惜しみなく同意します:)。 リターンコードを正しく取得し、それらを文書化することは、非常に大きな作業になります。

はい、魅力的な仕事ではありませんが、それは重要だと思います。 私はいくつかのcgroupに取り組んできましたが、最近、cgroupの機能を完全に誤解しているコンテナーの実装に遭遇しました...しかし、カーネルやその他の場所に適切にガイドするための明確なドキュメントがなかったため、最高の状態で機能しました。彼らはできた。

この問題に関する活動を見るのは素晴らしいことです! 私は徹底的なレビューに反対していませんが、元の要求は、ある程度直交しているさまざまな障害モードを区別できるようにするためだけに制限されていました。 レビューは確かに役立つでしょう、私が思う程度の前提条件でさえ。

この問題に関する活動を見るのは素晴らしいことです! 私は徹底的なレビューに反対していませんが、元の要求は、ある程度直交しているさまざまな障害モードを区別できるようにするためだけに制限されていました。 レビューは確かに役立つでしょう、私が思う程度の前提条件でさえ。

ある時点でレビューを行う必要がありますが、今もそうかもしれません。 保留が長ければ長いほど、呼び出し元にとってエラーコードの有用性は低くなります。libseccompの全体的なポイントは、このようなものを使いやすくすることです:)

ダン....私はあなたが上に書いたすべて、特に必要な努力に惜しみなく同意します:)。 リターンコードを正しく取得し、それらを文書化することは、非常に大きな作業になります。

ええ、これがこの問題が長い間続いている理由の1つですが、私はそれを十分に延期してきました(少なくとも、 @ drakenclimberは1年未満延期していると言えます!)。 今日の後半(明日?)、これをチャンク/複数の問題(いくつかの提案を含む)に分割して、少しずつ簡単に取り組むことができるようにします。

少し前向きなことですが、libseccompは実際には9つの一意のerrno値のみを使用しているように見えます(非常に大雑把なチェックによると)。

# grep -e "-E[A-Z0-9]\+" src/*.{h,c} | sed 's/.*-\(E[A-Z0-9]\+\).*/\1/' | sort -u
EACCES
EDOM
EEXIST
EFAULT
EINVAL
ENOMEM
EOPNOTSUPP
EPERM
ESRCH

...これは、問題領域を少し縮小するのに役立つはずです。特に、ライブラリ全体の各エラーコードに共通のセマンティック値に同意できる場合はそうです(これは間違いなく行う必要があります)。

これは意図したものより少し遅れていますが、libseccompAPIを構成する関数の完全なリストは次のとおりです。

const struct scmp_version *seccomp_version(void)
unsigned int seccomp_api_get(void)
int seccomp_api_set(unsigned int level)
scmp_filter_ctx seccomp_init(uint32_t def_action)
int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action)
void seccomp_release(scmp_filter_ctx ctx)
int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
uint32_t seccomp_arch_resolve_name(const char *arch_name)
uint32_t seccomp_arch_native(void)
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
char *seccomp_syscall_resolve_num_arch(uint32_t arch_token, int num)
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
int seccomp_syscall_priority(scmp_filter_ctx ctx, int syscall, uint8_t priority)
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_notify_alloc(struct seccomp_notif **req, struct seccomp_notif_resp **resp)
void seccomp_notify_free(struct seccomp_notif *req, struct seccomp_notif_resp *resp)
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

...これらの関数のうち、「int」を返す関数についてのみ心配する必要があります。

同様のコードパスと戻り値を持つ必要がある関数の可能なグループ。

  • グループA
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
  • グループB
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
  • グループC
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
  • グループD
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
  • グループE
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
  • グループF
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

...関数が上記のグループのいずれにも属していない場合は、コードパスや戻り値が一意である可能性があります。

グループCは__NR_SCMP_ERRORのみを返します。

グループDは、EINVAL、EPERM、EOPNOTSUPP、ENOMEM、EDOM、EFAULT、EEXISTのいずれかを返すことができます。

seccomp_load()は、prctl()(PR_SET_NO_NEW_PRIVSおよびPR_SET_SECCOMPのEACCES、EFAULT、EINVALのみ)およびseccomp()(EACCES、EFAULT、EINVAL、ENOMEM、ESRCH)システムコールからEINVAL、ENOMEM、ESRCHおよびerrno値を返すことができます。ページ。

libseccompがsyscallエラーを呼び出し元に返す場合(prctl()やseccomp()など)、libseccompが影響を受けないように、単一のerrno値(おそらくENOSYS?)の背後にそれらを隠す必要があると思います。カーネルの変更(またはABIの違い)。

これがデバッグの問題になる場合は、errno値を呼び出し元に直接返す新しいattrを導入できます。

可能性はありますが、libseccompのユーザーがすでに特定のerrno値を期待している場合は、ABI / APIの破損がないことを確認すると便利です。 元の問題は、一部のシステムコールが一部のアーキテクチャ(x86_32のみのugetrlimitなど)にのみ存在することでしたが、これはタイプミスのシステムコール名と区別できなかったため、異なる(おそらく合成の)エラーコードが必要になります。

さて、前に述べたように、私たちは現在、特定のerrno値を実際に保証するのではなく、単に「失敗時の負の値」であるため、特定のerrno値について現在仮定しているユーザーを破ることは残念ですが、状況を変えると思います将来、カーネルバージョンとABI全体で強力なerrno保証を提供することは、価値のあるトレードオフです。

さて、前に述べたように、私たちは現在、特定のerrno値を実際に保証するのではなく、単に「失敗時の負の値」であるため、特定のerrno値について現在仮定しているユーザーを破ることは残念ですが、状況を変えると思います将来、カーネルバージョンとABI全体で強力なerrno保証を提供することは、価値のあるトレードオフです。

同意します。

この評価が完了し、失敗時にerrno値を更新すると、返されるerrno値を何らかの形で保証できるようになります。

グループ化のアイデアはおそらく最善ではなかったので、これをすべて追跡するためにリストを開始しましょう_(進行するにつれてこれを更新し続けます)_:

  • [x] seccomp_reset
    現在、EINVAL、ENOMEMが返されます。

  • [x] seccomp_merge
    現在返されるのは、EINVAL、EDOM、EEXIST、ENOMEMです。

  • [x] seccomp_arch_exist
    現在、EINVAL、EEXISTが返されます。

  • [x] seccomp_arch_add
    現在返されるのは、EINVAL、EEXIST、ENOMEM、EDOMです。

  • [x] seccomp_arch_remove
    現在、EINVAL、EEXISTが返されます。

  • [x] seccomp_load
    現在、EINVAL、ENOMEM、ESRCH、ECANCELEDが返されます。

  • [x] seccomp_attr_get
    現在、EINVAL、EEXISTが返されます。

  • [x] seccomp_attr_set
    現在の返品:EINVAL、EACCES、EOPNOTSUPP、EEXIST。

  • [x] seccomp_syscall_resolve_name_arch
    すでに明確に定義されており、失敗時にsyscall値または__NR_SCMP_ERRORを返します。

  • [x] seccomp_syscall_resolve_name_rewrite
    すでに明確に定義されており、失敗時にsyscall値または__NR_SCMP_ERRORを返します。

  • [x] seccomp_syscall_resolve_name
    すでに明確に定義されており、失敗時にsyscall値または__NR_SCMP_ERRORを返します。

  • [x] seccomp_syscall_priority
    現在、EINVAL、EDOM、EFAULT、ENOMEMが返されます。

  • [x] seccomp_rule_add_array
    現在返されるのは、EINVAL、EOPNOTSUPP、ENOMEM、EDOM、EFAULT、EEXISTです。

  • [x] seccomp_rule_add
    現在返されるのは、EINVAL、EOPNOTSUPP、ENOMEM、EDOM、EFAULT、EEXISTです。

  • [x] seccomp_rule_add_exact_array
    現在返されるのは、EINVAL、EOPNOTSUPP、ENOMEM、EDOM、EFAULT、EEXISTです。

  • [x] seccomp_rule_add_exact
    現在返されるのは、EINVAL、EOPNOTSUPP、ENOMEM、EDOM、EFAULT、EEXISTです。

  • [x] seccomp_notify_alloc
    現在の返品:EOPNOTSUPP、ENOMEM、EFAULT、ECANCELED。 マンページでは、エラー時に-1がすでに指定されています。これは、seccomp()errnoのみを参照している可能性があります。

  • [x] seccomp_notify_receive
    現在の返品:EOPNOTSUPPおよびECANCELED。 マンページでは、エラー時に-1がすでに指定されています。これは、seccomp()errnoのみを参照している可能性があります。

  • [x] seccomp_notify_respond
    現在の返品:EOPNOTSUPPおよびECANCELED。 マンページでは、エラー時に-1がすでに指定されています。これは、seccomp()errnoのみを参照している可能性があります。

  • [x] seccomp_notify_id_valid
    現在の返品:EOPNOTSUPPおよびECANCELED。 マンページには、エラー時の-ENOENT(無効なID)がすでに指定されています。これは、seccomp()errnoのみを参照している可能性があります。

  • [x] seccomp_notify_fd
    すでに明確に定義されており、通知fdを返します。

  • [x] seccomp_export_pfc
    現在、EINVALおよびECANCELEDが返されます。

  • [x] seccomp_export_bpf
    現在、EINVAL、ENOMEM、およびECANCELEDが返されます。

これで、どの関数がどのエラーコードを返すかのリストができたので、特にエラーコードの使用方法とかなり一貫しているので、これについて少し気分が良くなりました。 その最後のビットは非常に役立つはずです。

PRを開始して、修正と変更に関するフィードバックの収集を開始できるようにします。すぐにここに投稿します。

ENOSYSのかなり「特別な」性質を考えると、カーネル/ libcのキャッチオールとしてerrnoを使用することについては留保しています。 私は他の価値観を見なければなりません、誰かがこれについて強い感情/考えを持っていますか?

まだ多くの欠落があり、ほとんどがマンページの編集とコードコメント(テストは言うまでもなく)ですが、次のブランチを見て、私が何を考えているかを理解することができます。

ENOSYSのかなり「特別な」性質を考えると、カーネル/ libcのキャッチオールとしてerrnoを使用することについては留保しています。 私は他の価値観を見なければなりません、誰かがこれについて強い感情/考えを持っていますか?

EIOはどうですか?

EIOはどうですか?

それがエラーをはるかに実行可能にするかどうかはわかりません。 代わりに、errno値を使用しない関数でAPIを拡張するのはどうでしょうか。ただし、たとえば、次のようになります。

  • SCMP_ERROR_UNKNOWN_SYSCALL:syscallはlibseccompによって認識されません:呼び出し元はこれを使用してユーザー入力を拒否できます(たとえば、syscall名のタイプミス)
  • SCMP_ERROR_SYSCALL_NOT_FOR_THIS_ARCH:syscallはlibseccompによって認識されていますが、ここでは使用できません:呼び出し元はこのアーキテクチャのみでこのエラーを無視できます
  • SCMP_ERROR_API_USAGE:libseccompは、正しく記述されたコードでは発生しないはずの呼び出しロジックの問題を検出します。呼び出し元はassert()をトリガーできます。
  • SCMP_ERROR_KERNEL_OTHER:libseccompは入力と呼び出しのシーケンスで問題ありませんでしたが、カーネルは特定の明確に定義されたエラーを返しました。たとえば、ENOMEM、EPERM、ENOSYS:呼び出し元はerrnoのアクションを確認する必要があります。 呼び出し元(EFAULTなど)によるエラーが原因でエラーの理由がlibseccompによって発生することがわかっている場合は、代わりにSCMP_ERROR_API_USAGEを使用できます。
  • SCMP_ERROR_KERNEL_API_USAGE:libseccompは入力などで問題ありませんでしたが、カーネルはそれほど明確で明白な理由でそれを好みませんでした。 これは、カーネルの変更、構成の無効化、libseccompまたは呼び出し元のバグ、非常に奇妙なユーザー入力などを示している可能性があります。呼び出し元のアクションは、イベントとerrnoをログに記録し、開発者(呼び出し元および/またはlibseccomp)に情報を渡すように要求することです。さらなる分析、assert()可能ではありません。

または、errnosを使用したAPIをそのままにしておくこともできますが、新しい関数を使用して上記のエラーコードを要求することもできます。

おそらく、 @ poetteringまたは@keszybzもコメントできます。

それがエラーをはるかに実行可能にするかどうかはわかりません。 代わりに、errno値を使用しない関数でAPIを拡張するのはどうですか...

そうですね、そのようなことを検討する前に(そして、それを実行したいかどうかはわかりませんが)、安定したサポートされているリターンコードを決定する必要があります。 これが私たちがここで取り組んでいることであり、v2.5を対象としています。

v2.5で安定した/サポートされているリターンコードを取得してみましょう。それがどのように行われるかを確認できます。さらに何かを行う必要がある場合は、v2.6でそれを検討できます。

EIOはどうですか?

他の作業の優先順位やいくつかのカーネルの問題のためにしばらくこれをやめましたが、libseccompに戻ったので、ここでEIOを使用するのは間違っているように思えます。 これについてもう少し考えさせてください。

キャッチオールカーネルエラーコードとしてのECANCELEDはどうですか?

私はあなたが@drakenclimberをどう思うか興味があります、あなたはこれについてしばらく静かにしています。

他の作業の優先順位やいくつかのカーネルの問題のためにしばらくこれをやめましたが、libseccompに戻ったので、ここでEIOを使用するのは間違っているように思えます。 これについてもう少し考えさせてください。

こっちも一緒。 最近は少し忙しいです:/。

私はあなたが@drakenclimberをどう思うか興味があります、あなたはこれについてしばらく静かにしています。

もちろん。 スレッド全体をもう一度読みたいので、チャイムを鳴らします。

キャッチオールカーネルエラーコードとしてのECANCELEDはどうですか?

私はECANCELEDにあまり精通していなかったことを認めます。 カーネルソースの使用法を簡単に調べ、グーグル検索も行いました。 ECANCELEDは、以前に使用したlibseccomp、prctl()、またはその他のAPIとの衝突はなく、カーネルがスローするエラーを合理的にカプセル化できると思います。

tl; dr-カーネルエラーのキャッチオールとしてECANCELEDを使用するのはかっこいいです。

まことにありがとうございます。 中途半端なパッチセットを持っています。仕上げて、レビュー用のPRとして提出します。

このページは役に立ちましたか?
0 / 5 - 0 評価