Libseccomp: RFE: более точно определенные возвращаемые значения ошибок

Созданный на 7 окт. 2017  ·  24Комментарии  ·  Источник: seccomp/libseccomp

Глядя на возвращаемые значения ошибок libseccomp для системных вызовов, невозможно определить, какая из следующих причин вызвала ошибки:

  1. системный вызов не существует на некоторых арках
  2. системный вызов не может быть сопоставлен с какой-либо аркой (потому что он мультиплексирован, подумайте о сокете/сокетном вызове)
  3. другие случаи ошибок

При построении фильтра seccomp вызывающая сторона может считать некоторые из этих причин фатальными, но не другие, поэтому потребуется более подробная информация об ошибке (более широкий набор значений ошибки, чем просто -EINVAL ).

См. также systemd PR 6952 .

enhancement prioritmedium

Самый полезный комментарий

Большое спасибо. У меня есть наполовину готовый набор патчей, который я закончу и отправлю в качестве PR для обзора.

Все 24 Комментарий

Привет, @topimiettinen , извините, что мы так долго шли к этому, но я думаю, пришло время это исправить.

@drakenclimber это будет круто. Все API-интерфейсы libseccomp должны иметь справочные страницы на этом этапе (если их нет, нам нужно создать проблему для этого), со всеми справочными страницами, имеющими некоторые ручные комментарии «отрицательные значения при ошибке» в разделе ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ. Я думаю, нам нужно сделать следующее:

  • вручную проверять каждый вызов API для создания списка возможных возвращаемых значений
  • решите, имеют ли эти возвращаемые значения смысл, измените код, если они не имеют смысла.
  • задокументируйте каждое возможное возвращаемое значение на соответствующей странице руководства с кратким объяснением того, что указывает код ошибки.

Мысли?

Привет, @topimiettinen , извините, что мы так долго шли к этому, но я думаю, пришло время это исправить.

@drakenclimber это будет круто. Все API-интерфейсы libseccomp должны иметь справочные страницы на этом этапе (если их нет, нам нужно создать проблему для этого), со всеми справочными страницами, имеющими некоторые ручные комментарии «отрицательные значения при ошибке» в разделе ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ. Я думаю, нам нужно сделать следующее:

* 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 в том, чтобы сделать этот материал проще в использовании :)

Черт.... Я неохотно согласен со всем, что вы написали выше, особенно с требуемыми усилиями :). Получение правильных кодов возврата, а затем их документирование потребует очень больших усилий.

Да, это одна из причин, по которой эта проблема так долго висела в воздухе, но я откладывал ее достаточно долго (по крайней мере, @drakenclimber может сказать, что он откладывал ее меньше года!). Позже сегодня (завтра?) Я разобью это на куски/несколько вопросов (с некоторыми предложениями), чтобы было немного легче решать их по частям.

Как положительный момент, похоже, что libseccomp действительно использует только девять уникальных значений 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

... это должно помочь немного сократить проблемное пространство, особенно если мы сможем согласовать общее семантическое значение для каждого кода ошибки в библиотеке (что мы обязательно должны сделать).

Это немного позже, чем предполагалось, но вот полный список функций, составляющих API libseccomp:

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».

Возможные группы функций, которые должны иметь похожие пути кода и возвращаемые значения.

  • Группа А
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_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)
  • Группа С
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_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_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() может возвращать значения EINVAL, ENOMEM, ESRCH, а также значения errno из системных вызовов prctl() (только EACCES, EFAULT, EINVAL для PR_SET_NO_NEW_PRIVS и PR_SET_SECCOMP) и seccomp() (EACCES, EFAULT, EINVAL, ENOMEM, ESRCH) в соответствии с их руководством. страницы.

Когда дело доходит до передачи libseccomp ошибок системного вызова вызывающей стороне, например, prctl() и seccomp(), я думаю, что нам просто нужно скрыть их за одним значением errno (возможно, ENOSYS?), чтобы на libseccomp не влияли любые изменения в ядре (или отличия ABI).

Если это станет проблемой для отладки, возможно, мы могли бы ввести новый атрибут, который будет передавать значение errno непосредственно вызывающей стороне.

Возможно, но тогда было бы неплохо убедиться в отсутствии поломки ABI/API, если пользователи libseccomp уже ожидают определенных значений errno. Первоначальная проблема заключалась в том, что некоторые системные вызовы существуют только на некоторых архитектурах (например, ugetrlimit только на x86_32), но их нельзя было отличить от неправильно введенного имени системного вызова, поэтому потребуются другие (возможно, синтетические) коды ошибок. .

Что ж, как мы уже говорили ранее, в настоящее время мы на самом деле не гарантируем конкретные значения errno, только «отрицательные значения при сбое», поэтому, хотя было бы прискорбно сломать пользователей, которые в настоящее время делают предположения о конкретных значениях errno, я думаю, изменить ситуацию обеспечение надежной гарантии отсутствия ошибок для версий ядра и ABI в будущем является выгодным компромиссом.

Что ж, как мы уже говорили ранее, в настоящее время мы на самом деле не гарантируем конкретные значения errno, только «отрицательные значения при сбое», поэтому, хотя было бы прискорбно сломать пользователей, которые в настоящее время делают предположения о конкретных значениях errno, я думаю, изменить ситуацию обеспечение надежной гарантии отсутствия ошибок для версий ядра и ABI в будущем является выгодным компромиссом.

Я согласен.

Как только эта оценка будет завершена и мы обновим значения 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
    Уже хорошо определенный, возвращает значение системного вызова или __NR_SCMP_ERROR в случае ошибки.

  • [x] seccomp_syscall_resolve_name_rewrite
    Уже хорошо определенный, возвращает значение системного вызова или __NR_SCMP_ERROR в случае ошибки.

  • [x] seccomp_syscall_resolve_name
    Уже хорошо определенный, возвращает значение системного вызова или __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().

  • [x] seccomp_notify_receive
    В настоящее время возвращает: EOPNOTSUPP и ECANCELED. Страница руководства уже указывает -1 при ошибке, что, вероятно, относится только к ошибке seccomp().

  • [x] seccomp_notify_respond
    В настоящее время возвращает: EOPNOTSUPP и ECANCELED. Страница руководства уже указывает -1 при ошибке, что, вероятно, относится только к ошибке seccomp().

  • [x] seccomp_notify_id_valid
    В настоящее время возвращает: EOPNOTSUPP и ECANCELED. Страница руководства уже указывает -ENOENT при ошибке (неверный идентификатор), что, вероятно, относится только к ошибке seccomp().

  • [x] seccomp_notify_fd
    Уже хорошо определено, возвращает уведомление fd.

  • [x] seccomp_export_pfc
    В настоящее время возвращает: EINVAL и ECANCELED.

  • [x] seccomp_export_bpf
    В настоящее время возвращает: EINVAL, ENOMEM и ECANCELED.

Теперь, когда у нас есть список того, какие функции возвращают какие коды ошибок, я чувствую себя немного лучше, тем более, что мы уже достаточно последовательны в том, как мы используем наши коды ошибок. Этот последний бит должен помочь чрезвычайно.

Я собираюсь начать PR, чтобы мы могли начать собирать исправления и отзывы об изменениях, я скоро опубликую это здесь.

Учитывая довольно "особый" характер ENOSYS, у меня есть сомнения по поводу использования errno в качестве универсального ядра/libc. Мне придется взглянуть на другие значения, есть ли у кого-нибудь сильные чувства/мысли по этому поводу?

По-прежнему многого не хватает, в основном правок справочной страницы и комментариев к коду (не говоря уже о тестировании), но вы можете посмотреть следующую ветку, чтобы понять, о чем я думаю:

Учитывая довольно "особый" характер ENOSYS, у меня есть сомнения по поводу использования errno в качестве универсального ядра/libc. Мне придется взглянуть на другие значения, есть ли у кого-нибудь сильные чувства/мысли по этому поводу?

А ЭИО?

А ЭИО?

Я не знаю, сделает ли это ошибку более действенной. Как насчет того, чтобы вместо этого расширить API функциями, которые не используют значения errno, а, например:

  • SCMP_ERROR_UNKNOWN_SYSCALL: системный вызов неизвестен libseccomp: вызывающая сторона может использовать это, чтобы отклонить ввод пользователя (например, опечатка в имени системного вызова)
  • SCMP_ERROR_SYSCALL_NOT_FOR_THIS_ARCH: системный вызов известен libseccomp, но недоступен здесь: вызывающая сторона может игнорировать эту ошибку только для этой архитектуры
  • SCMP_ERROR_API_USAGE: libseccomp обнаруживает проблему с логикой вызова, которая не должна возникать в правильно написанном коде: вызывающая сторона может инициировать assert()
  • SCMP_ERROR_KERNEL_OTHER: libseccomp был в порядке с входными данными и последовательностью вызовов, но ядро ​​вернуло определенные четко определенные ошибки, например, ENOMEM, EPERM, ENOSYS: вызывающая сторона должна проверить errno для действия. В случае, если причина ошибки известна libseccomp из-за ошибки, сделанной вызывающей стороной (скажем, EFAULT), возможно, вместо этого можно использовать SCMP_ERROR_API_USAGE.
  • SCMP_ERROR_KERNEL_API_USAGE: libseccomp был в порядке с вводом и т. д., но ядру это не понравилось по не столь ясным и очевидным причинам. Это может указывать на изменение ядра, отключенную конфигурацию, ошибку в libseccomp или вызывающей программе, очень странный пользовательский ввод и т. д. Действия для вызывающей стороны могут заключаться в регистрации события и errno с запросом на передачу информации разработчикам (вызывающей стороне и/или libseccomp) для дальнейший анализ невозможен.

В качестве альтернативы API с ошибками можно было бы оставить как есть, но можно было бы использовать новую функцию для запроса вышеуказанного кода ошибки.

Возможно , @poettering или @keszybz тоже могли бы прокомментировать.

Я не знаю, сделает ли это ошибку более действенной. Как насчет того, чтобы вместо этого расширить API функциями, которые не используют значения errno...

Что ж, прежде чем мы сможем даже подумать о том, чтобы сделать что-то подобное (и я не уверен, что мы хотим это сделать), нам нужно остановиться на стабильных и поддерживаемых кодах возврата. Это то, над чем мы сейчас работаем и на что мы нацелены в версии 2.5.

Давайте получим стабильные/поддерживаемые коды возврата в версии 2.5, и мы увидим, как это происходит, если нам нужно сделать что-то дополнительное, мы можем рассмотреть это для версии 2.6.

А ЭИО?

На какое-то время я отказался от этого из-за других приоритетов работы и некоторых вещей ядра, но теперь, когда я вернулся к libseccomp, я понимаю, что использование EIO здесь кажется неправильным. Позвольте мне еще немного подумать об этом.

А как насчет ECANCELED как универсального кода ошибки ядра?

Мне любопытно, что вы думаете, @drakenclimber , какое-то время вы молчали об этом.

На какое-то время я отказался от этого из-за других приоритетов работы и некоторых вещей ядра, но теперь, когда я вернулся к libseccomp, я понимаю, что использование EIO здесь кажется неправильным. Позвольте мне еще немного подумать об этом.

То же самое. В последнее время все было немного беспокойно :/.

Мне любопытно, что вы думаете, @drakenclimber , какое-то время вы молчали об этом.

Конечно. Я хочу еще раз прочитать всю ветку, а потом вмешаюсь.

А как насчет ECANCELED как универсального кода ошибки ядра?

Признаюсь, я не был хорошо знаком с ECANCELED. Я кратко просмотрел исходный код ядра на предмет его использования, а также выполнил поиск в Google. ECANCELED не имеет коллизий с предыдущими libseccomp, prctl() или другими API, которые мы использовали, и я думаю, что он может разумно инкапсулировать любую ошибку, которую выдает нам ядро.

tl; dr — я согласен с ECANCELED как с нашей защитой от ошибок ядра.

Большое спасибо. У меня есть наполовину готовый набор патчей, который я закончу и отправлю в качестве PR для обзора.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги