Может ли argv exec* содержать значение 0 в нескольких местах?

avatar
Jake Chasan
7 апреля 2018 в 21:44
87
1
2

Я читал, что после того, как exec создает новый процесс,

argv представляет собой массив строк аргументов с argv[argc] == 0

Что произойдет, если одно из других значений в массиве argv окажется равным 0? Будет ли количество аргументов (argc) вычисляться неправильно при запуске дочернего процесса?

Я прочитал это на странице 34 ABI AMD64 (https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf).

Источник

Ответы (1)

avatar
Hadi Brais
8 апреля 2018 в 00:20
4

Системный вызов execve (который используется всеми функциями exec*) имеет аргумент вида char *const argv[]. Ядро вычисляет argc путем перебора предоставленного argv как

static int count(struct user_arg_ptr argv, int max)
{
    int i = 0;

    if (argv.ptr.native != NULL) {
        for (;;) {
            const char __user *p = get_user_arg_ptr(argv, i);

            if (!p)
                break;

            if (IS_ERR(p))
                return -EFAULT;

            if (i >= max)
                return -E2BIG;
            ++i;

            if (fatal_signal_pending(current))
                return -ERESTARTNOHAND;
            cond_resched();
        }
    }
    return i;
}

Функция get_user_arg_ptr фактически вычисляет индекс в массиве argv и возвращает указатель, хранящийся в этом индексе. Цикл прерывается при четырех условиях, два из которых относятся к вашему вопросу:

  • На первом NULL в массиве argv. Если за первым NULL в argv следуют другие указатели, они игнорируются. Наличие более одного NULL пахнет ошибкой в ​​программе, создавшей argv.
  • Когда количество указателей больше или равно MAX_ARG_STRINGS, что определено как 0x7FFFFFFF. В этом случае системный вызов завершается ошибкой.

Возвращенное значение i присваивается argc при возврате get_user_arg_ptr.

Еще один случай, когда завершение NULL в argv имеет значение, это когда само приложение использует argv следующим образом:

for(char **p = argv; *p != NULL; ++p)
{
   // ...
}

Это часть ABI Linux, которая argv оканчивается на NULL, поэтому такой код допустим и переносим во все реализации Linux. Кстати, этот код легален и в Windows. Поэтому argc предоставляется только для удобства.

Кроме того, в стандартах C и C++ в 5.1.2.2.1 и 3.6.1 указано, что если argc больше нуля, то все значения в argv[0] по argv[argc-1] не должны быть -null указатели на строки с завершающим нулем. Также argv[argc] должно быть нулевым, а argc неотрицательным. См. также этот ответ.