Лучший подход для защиты char [] при выходе из функции? - PullRequest
2 голосов
/ 06 апреля 2009

Я поддерживаю часть кода на C, где массивы символов часто заполняются путем передачи их в функции и использования результата в виде строки, которая записывается для вывода. Однако после того, как он обработан функцией, проверка массивов не производится, и мне интересно, какой из них лучше всего подходит?

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

void Unpack(char* inbuf, char* outbuf);

int main(int argc, char* argv[])
{
    char* inData = "abc";
    char outData[4];
    char result[14];

    Unpack(inData, outData);
    outData[3] = '\0';  // Insert this to safeguard array before using as string.

    _snprintf(result, sizeof(result), "blah %0s blah", outData);
    printf(result);

    return 0;
}

void Unpack(char* inbuf, char* outbuf) {
    for(int index=0; index<3; index++) {
        *outbuf++ = *inbuf++;
    } 
}

Ответы [ 6 ]

5 голосов
/ 06 апреля 2009

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

Если вы обеспокоены тем, что в выходном массиве может даже не быть допустимой строки C с завершением '\ 0', вы решили эту проблему, вставив '\ 0' методом грубой силы. Кроме того, вы выбрали оптимальное расположение, последний байт массива нельзя использовать ни для чего другого, если предполагается, что массив содержит строку с символом \ 0.

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

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

4 голосов
/ 06 апреля 2009

Если вы владеете обоими кодами, лучшим подходом будет изменение сигнатуры Unpack для передачи размера выходного буфера, чтобы он мог отвечать за добавление символа '\ 0'.

Если у вас нет контроля над распаковкой, я напишу тонкий слой, который сделает всю работу.

unpack_safe(char *in, char* out, size_t len) {
  unpack(in, out);
  out[len-1]='\0';
}

При этом вы можете защитить только строку с ненулевым символом в конце, но если строка может содержать нулевые символы, это не будет работать должным образом, и передача размера intput и output output будет правильным поведением.

4 голосов
/ 06 апреля 2009

Защищать их от чего? Если вы пытаетесь предотвратить такие вещи, как переполнение буфера, вы слишком поздно; у функции, которую вы вызвали, был полный доступ к использованию и злоупотреблениям, и вы мало что можете сделать после того, как выбор был «злоупотреблением».

3 голосов
/ 06 апреля 2009

Установка последнего элемента в '\ 0' правильно завершит строку, но не исправит ущерб, который был нанесен, когда функция записывалась вне буфера. В этом случае вы только скрываете ошибку, и программа может выдать неправильные результаты или позже произойдет сбой при совершенно не связанном вызове функции или при возврате.

По моему мнению, было бы лучше определить, записала ли функция вне своего буфера, и аварийно завершить работу программы. Это облегчает поиск и, следовательно, исправление ошибки. Чтобы добиться этого, вы можете установить последний байт буфера равным '\ 0' перед вызовом функции и проверить (с помощью assert ()), если это все еще имеет место, когда функция возвращает.

И, как уже сказал Билл Форстер, всегда полезно также передавать длину буфера вызываемым функциям

1 голос
/ 06 апреля 2009

Если вас беспокоит END-MARKER, например 0x55AA или какой-то зарезервированный символ. Но если вас беспокоит целостность данных, вы можете попробовать какой-нибудь заголовок с некоторой контрольной суммой или CRC (циклическая проверка избыточности).

1 голос
/ 06 апреля 2009

При использовании необработанных указателей всегда есть вероятность переполнения буфера. Всегда безопасно использовать классы-оболочки, такие как std :: string, CString и т. Д.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...