strncpy копирование больше указанного размера

У меня есть следующий пример кода, который имитирует код в приложении.

#include <iostream>
#include <string.h>
#include <cstring>
#include <atlstr.h>
using namespace std;

void test(char *s, int size)
{
//s = "";
int lens = strlen(s);
char *str1 = "(( State:0.000000 Std30c5  =  State:T ) OR (( State:0.000000 Std30c6  =  State:T )  OR (( State:0.000000 Std30c7  =  State:T ) OR (( State:0.000000 Std30c8  =  State:T ) OR (( State:0.000000 Std30c9  =  State:T ) OR (( State:0.000000 Std30ca  =  State:T ) OR (( State:0.000000 Std30cb  =  State:T ) OR (( State:0.000000 Std30cc  =  State:T ) OR (( State:0.000000 Std30cd  =  State:T ) OR (( State:0.000000 Std30ce  =  State:T ) OR (( State:0.000000 Std30cf  =  State:T ) OR ( ...0.000000   =  State:T ))))))))))))";
int len1 = strlen(str1);
strncpy(s, str1, 512);
int len = strlen(s);

}

int main()
{

char strDisplay[512] = "";
test(strDisplay, 512);cout << strDisplay << endl;
system("pause");
return 0;
}

Результат:
lenofstrtest = 523;
lenofstr1 = 512;

strtest = «((Состояние: 0,000000 Std30c5 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c6 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c7 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c8 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c9 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30ca = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cb = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cc = Состояние: T) ИЛИ ( (Состояние: 0,000000 Std30cd = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30ce = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cf = Состояние: T) ИЛИ (… 0,000000 = Состояние: T)))))) ))))))ÌÌÌÌJ ¢ £ Ш. öö

Почему strncpy копирует дополнительные символы?

(Это вызывает проблему, так как неправильный strnlen вызывает логику распаковки!)

Я думаю, что это связано с «ошибка 512 байт strncpy» … пожалуйста, помогите мне понять эту ошибку.

1

Решение

strncpy не добавляет завершающий символ ‘\ 0’ к усеченным строкам, что вызывает проблемы, с которыми вы столкнулись. Если строка не завершена правильно, то выглядит, что она длиннее, но на самом деле вы видите данные, которые помещаются в буфер после вашего буфера. Это может вызвать серьезные проблемы.

Вместо strncpy ты должен использовать strlcpy который правильно завершает строку и возвращает длину исходной строки, которую вы можете сравнить с длиной вашего буфера, чтобы узнать, была ли строка обрезана или нет. strncpy возвращает указатель на указатель на ваш буфер (что не очень полезно, так как вы уже знаете его — вы передали его в качестве первого аргумента) и не сообщает вам, произошло ли какое-либо усечение.

Смотри man strlcpy:

Функции strlcpy () и strlcat () копируют и объединяют строки
с теми же параметрами ввода и вывода, что и snprintf (3). Oни
разработаны, чтобы быть более безопасными, более последовательными и менее подверженными ошибкам
замены для легко неправильно используемых функций strncpy (3) и
strncat (3). strlcpy () и strlcat () принимают полный размер
целевой буфер и гарантированное NUL-завершение, если есть место.
Обратите внимание, что место для NUL должно быть включено в dstsize.

а также Обработка строки C — Замены в Википедии:

Наиболее популярной заменой [a] являются функции strlcat и strlcpy,
которая появилась в OpenBSD 2.4 в декабре 1998 года. [84] Эти функции
всегда записывать один NUL в буфер назначения, обрезая результат
если необходимо, и вернуть размер буфера, который был бы необходим,
который позволяет обнаружить усечение и обеспечивает размер для
создание нового буфера, который не будет усекаться.

К сожалению, он не включен в glibc — см. Безопасная переносимость бумага по
Дэмиен Миллер (PDF):

API-интерфейсы strlcpy и strlcat правильно проверяют границы целевого буфера,
nul-terminate во всех случаях и возвращает длину исходной строки,
позволяя обнаружение усечения. Этот API был принят большинством
современные операционные системы и множество автономных программных пакетов,
включая OpenBSD (там, где он возник), Sun Solaris, FreeBSD, NetBSD,
ядро Linux, rsync и проект GNOME. Заметное исключение
является стандартной библиотекой C GNU, glibc [12], чей сопровождающий
стойко отказывается включать эти улучшенные API, маркируя их
«Ужасно неэффективное дерьмо BSD» [4], несмотря на предварительные доказательства того, что они
в большинстве случаев быстрее, чем API, которые они заменяют [13]. В следствии,
более 100 программных пакетов в дереве портов OpenBSD
поддерживать свои собственные замены strlcpy и / или strlcat или эквивалентные
API — не идеальное положение вещей.

Он доступен для Linux в библиотеке libbsd:

В Debian, Ubuntu и других дистрибутивах есть пакеты:

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

/*
* Copyright (c) 1998 Todd C. Miller <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <sys/types.h>
#include <string.h>

/*
* Copy src to string dst of size siz.  At most siz-1 characters
* will be copied.  Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;

/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}

/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0';      /* NUL-terminate dst */
while (*s++)
;
}

return(s - src - 1);    /* count does not include NUL */
}

Источник: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11

1

Другие решения

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

strncpy(s, str1, n );
s[n-1] = '\0';

В противном случае код будет небезопасным, как в вашем случае.

Примите во внимание, что нет никакого смысла использовать эти два заголовка вместе

#include <string.h>
#include <cstring>

Удалите первый заголовок и используйте только второй заголовок в C ++.

#include <cstring>
0

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

char strDisplay[513] = "";
strDisplay[512] = '\0'; //recommended
0

strncpy плохая функция, потому что она не генерирует строку. Если вы хотите использовать обработку строк в стиле C, тогда snprintf проще использовать безопасно:

snprintf(s, size, "%s", str1);

Обратите внимание, что char *str1 = "... устарела в C ++; ты можешь использовать char const *str1 = "... вместо.

-1

использование std::string в C ++. Он автоматически позаботится обо всех этих проблемах для вас.

#include <iostream>
#include <string>
std::string test()
{
return "(( State:0.000000 Std30c5  =  State:T ) OR (( State:0.000000 Std30c6  =  State:T )  OR (( State:0.000000 Std30c7  =  State:T ) OR (( State:0.000000 Std30c8  =  State:T ) OR (( State:0.000000 Std30c9  =  State:T ) OR (( State:0.000000 Std30ca  =  State:T ) OR (( State:0.000000 Std30cb  =  State:T ) OR (( State:0.000000 Std30cc  =  State:T ) OR (( State:0.000000 Std30cd  =  State:T ) OR (( State:0.000000 Std30ce  =  State:T ) OR (( State:0.000000 Std30cf  =  State:T ) OR ( ...0.000000   =  State:T ))))))))))))";
}

int main()
{
std::cout << test() << std::endl;
return 0;
}

Обратите внимание, что не требуется управление памятью, временные буферы магического размера или нулевые терминаторы.

-1
По вопросам рекламы [email protected]