6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

strcpyもstrncpyも危険

Posted at

C言語の文字列は厄介

文字列は文字の配列で、Null終端で最後という仕様なので、
どうやっても番兵スタイルになる。
番兵とはNullのことで、処理の進め方として、Nullになるまで処理するというスタイルのこと。
単方向リストなどで使う。
Nullがないと処理がリークするので、Null保証する仕組みを作ったりする。

strcpy関数は危険

strcpy関数は引数が2個のみでサイズを指定できないため、
Null終端があったとしても危険

#define SRC_LENGTH 10
#define DEST_LENGTH 3

char src[SRC_LENGTH];
char dest[DEST_LENGTH];

strcpy(dest,src);

strncpy関数も危険

strcpy関数は危険なので、strncpy関数を使うとサイズ指定ができるが、これも危険である。
以下のコードは危険コード。

#define SRC_LENGTH 10
#define DEST_LENGTH 3

char src[SRC_LENGTH];
char dest[DEST_LENGTH];

memset(src, "aiueoaiue", SRC_LENGTH);
strncpy(dest,src, DEST_LENGTH);

このコードのstrncpy関数呼び出し後のdest変数の中身は

dest[0] = 'a';
dest[1] = 'i';
dest[2] = 'u';

となるので、Null終端がない。

解決策はあるのか?

本質的には無理だと思う。C言語の特性なので、頑張る。

strncpy(dest,src, DEST_LENGTH);
dest[DEST_LRNGTH-1] = '\0';

でとりあえずリークはしない。リークの是正目的だけは満たせる。

Linuxカーネルに

strscpy関数なるものがある。

ssize_t strscpy(char *dest, const char *src, size_t count)
{
	const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
	size_t max = count;
	long res = 0;

	if (count == 0)
		return -E2BIG;

#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
	/*
	 * If src is unaligned, don't cross a page boundary,
	 * since we don't know if the next page is mapped.
	 */
	if ((long)src & (sizeof(long) - 1)) {
		size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1));
		if (limit < max)
			max = limit;
	}
#else
	/* If src or dest is unaligned, don't do word-at-a-time. */
	if (((long) dest | (long) src) & (sizeof(long) - 1))
		max = 0;
#endif

	while (max >= sizeof(unsigned long)) {
		unsigned long c, data;

		c = read_word_at_a_time(src+res);
		if (has_zero(c, &data, &constants)) {
			data = prep_zero_mask(c, data, &constants);
			data = create_zero_mask(data);
			*(unsigned long *)(dest+res) = c & zero_bytemask(data);
			return res + find_zero(data);
		}
		*(unsigned long *)(dest+res) = c;
		res += sizeof(unsigned long);
		count -= sizeof(unsigned long);
		max -= sizeof(unsigned long);
	}

	while (count) {
		char c;

		c = src[res];
		dest[res] = c;
		if (!c)
			return res;
		res++;
		count--;
	}

	/* Hit buffer length without finding a NUL; force NUL-termination. */
	if (res)
		dest[res-1] = '\0';

	return -E2BIG;
}

コピーするだけでこのボリューム。さすがC言語。さすがLinuxカーネル。
これの解説はあとで。

使う側の利点としては
strscpy関数は

  • コピー元がNull終端でなくてよい
  • コピー先は必ずNull終端
  • コピー先のサイズが足りない場合はエラーを返す

なので、うまくこれを使っていきたい。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?