Как этот буфер может быть переполнен?

Я заранее прошу прощения за бесполезное название этого вопроса, но, похоже, ничто не подходит лучше.

Идея здесь состоит в том, чтобы скопировать argv в другую переменную, по сути сделав его копию. Итак, основная идея того, что делает функция, заключается в том, чтобы использовать malloc(), чтобы запросить некоторое пространство для копии, а затем выполнить итерацию по argv, создавая копии каждого элемента.

Это код, с которым я работаю, среда разработки сейчас Visual Studio 2019 (даже если это не строго компилятор C...):

// Returns a copy of an array of strings (intended for argv, but should work with any of them):
wchar_t** copyArgv(size_t argc, wchar_t* argv[]) {
    // Allocate space for the array of arguments:
    wchar_t** argsCopy = malloc(((argc + 1) * sizeof(wchar_t*)));
    if (!argsCopy)
        return NULL;
    // Copy each one of them:
    for (size_t i = 0; i < argc; i++) {
        argsCopy[i] = _wcsdup(argv[i]);
        if (!argsCopy[i]) {
            // Should also free any previous copied string I left that part out in the paste.
            free(argsCopy);
            return NULL;
        }
    }
    argsCopy[argc] = NULL;
    return argsCopy;
}

Я пробовал разные способы сделать копию argv, но каждый из них позволяет VS полагать, что может быть переполнение буфера, когда я делаю копию аргумента (строка: argsCopy[i] = _wcsdup(argv[i]);) или считываю недопустимым данные в следующей строке, означающие чтение за пределами зарезервированного пространства.

Все это привело меня к мысли, что проблема заключается в (сейчас) единственном вызове malloc(), чтобы зарезервировать пространство для массива аргументов.

И все же я бьюсь головой о стену, пытаясь понять, в чем проблема, я имею в виду, я думаю, что я требую достаточно места.

Я пробовал и другие компиляторы, последние стабильные версии Clang и GCC, похоже, не показывают такого предупреждения. Поэтому я решил спросить вас, бывалые программисты, можете ли вы обнаружить проблему или это какая-то ошибка компилятора (вряд ли, я уверен, что).

Для справки: точные предупреждения, которые выдает VS2019 (в 64-битной компиляции):

В назначении:

Переполнение буфера при записи в argsCopy: размер записи составляет "((argc + 1)) * sizeof (wchar_t *)", но может быть записано "16".

Следующая строка, тест для NULL:

Чтение недопустимых данных из argsCopy: читаемый размер составляет ((argc + 1)) * sizeof (wchar_t *) 'байтов, но может быть прочитано 16 байтов.

Ответы

Ответ 1

Это предупреждения от статического анализатора. Например, он пытается распознать ситуации переполнения буфера.

Предупреждение

Важно отметить, что это предупреждения, а не сообщения об ошибках. Компилятор говорит, что может быть что-то потенциально не так. Статический анализ, как правило, сложная вещь.

Ложно положительный

Нет ситуации переполнения буфера, поэтому это ложный положительный результат. Я бы предположил, что это сообщение исчезнет в будущем обновлении.

Немного изменить код

Если мы изменим строку выделения памяти следующим образом:

wchar_t** argsCopy = (wchar_t**)calloc(argc + 1, sizeof(wchar_t*));

тогда больше не будет предупреждений от Visual Studio 2019.

Количество выделенных байтов остается неизменным. Тем не менее, предупреждения исчезают.

Тестовое задание

Перед изменением список ошибок VS выглядит следующим образом:

before

После применения предложенных мной изменений исчезли предупреждения:

after

Ответ 2

Вы не должны освобождать память, указанную двойным указателем. Это может привести к утечке памяти. Вы должны были делать

free(argsCopy[0]);

Ответ 3

Однако я могу ошибаться, поиграв с онлайн-копией визуальной студии (https://rextester.com/l/c_online_compiler_visual), я вынужден предположить, что вы забыли включить string.h или wchar.h (любой из них работает). Visual Studio предполагает, что ваш возвращаемый тип является целым числом вместо wchar_t *, так как функция не определена. Кажется, что есть немного "волшебства" из-за того, что это зарезервированная функция, начинающаяся с _, и поэтому она не выдает других предупреждений? Опять же без вашего точного окружения, хотя я вынужден частично спекулировать (ваш комментарий об изменении цели дал мне, надеюсь, правильный совет).

Ответ 4

Ключевым моментом может быть то, что вы не использовали недостаточно места для хранения данных, которые хотите скопировать.

Я не знаю, действительно ли я понимаю, что вы хотите сделать, я предполагаю, что вы хотите скопировать двумерный символьный массив в другой сегмент памяти, а затем вернуть его адрес, и массив имеет строки 'argc', адрес каждой строки строки хранится в массиве argv.

Но почему вы использовали argc+1 вместо argc? Для malloc дополнительного пространства для предотвращения переполнения буфера? и что более важно, sizeof(wchar_t*) вернет размер указателя (8 байт в 64-битной системе). Он не вернет размер одной строки в нужном нам двумерном массиве.

Ответ 5

1) Один из способов репликации argv описан ниже, но. 2) Я не могу понять, почему вы хотите сделать копию argv? какие варианты использования/пользовательские проблемы это решает?

как я упоминал в (1), здесь есть один из способов, который заключается в копировании всего содержимого argv в ваш буфер. Это выглядит примерно так (PS: могут быть ошибки компиляции, когда я печатаю в телефоне, находясь в такси, поэтому у меня нет доступа к высококачественному C-компилятору для перекрестной проверки)

int numArgc = argc
char** argvCopy;

for (i=0;i<argc,i++)
{

 argvCopy[i] = malloc(sizeof(char)*strlen(argv[i]));
 strcpy(argvCopy[i], argv[i]);

}

//please do not forget to Free this malloc'ed memory (a very common C programming error) //when you don't need it anymore 

пожалуйста, расскажите проблему, которую вы хотите решить