web-dev-qa-db-ja.com

なぜstrncpyはnullで終了しないのですか?

strncpy()は、おそらくバッファオーバーフローから保護します。しかし、nullで終了せずにオーバーフローを防ぐと、おそらく文字列操作はオーバーフローします。だからこれを防ぐために私は自分がやっていることを見つけます:

_strncpy( dest, src, LEN );
dest[LEN - 1] = '\0';
_

_man strncpy_の結果:

Strncpy()関数は似ていますが、srcのnバイト以下がコピーされます。したがって、srcの最初のnバイトの間にnullバイトがない場合、結果はnullで終了しません。

次のような一見無害なものをヌルで終了することなく:

_   printf( "FOO: %s\n", dest );
_

...クラッシュする可能性があります。


strncpy()のより良い、より安全な代替手段はありますか?

71
Timothy Pratley

strncpyは、より安全なstrcpyとして使用することを意図したものではなく、ある文字列を別の文字列の途中に挿入するために使用されることになっています。

snprintfvsnprintfなどの「安全な」文字列処理関数はすべて、バッファオーバーフローエクスプロイトなどを緩和するために後の標準で追加された修正です。

Wikipedia 独自の安全なstrncatを書く代わりにstrncpyに言及しています:

*dst = '\0'; strncat(dst, src, LEN);

[〜#〜] edit [〜#〜]

文字列がLEN文字以上の場合、nullで文字列を終了すると、strncatがLEN文字を超えることを逃しました。

とにかく、memcpy(...、strlen(...))/ whateverなどの自家製ソリューションの代わりにstrncatを使用するポイントは、strncatの実装がライブラリで最適化されたターゲット/プラットフォームである可能性があることです。

もちろん、dstが少なくともnullcharを保持していることを確認する必要があるため、strncatの正しい使用方法は次のようになります。

if(LEN) { *dst = '\0'; strncat(dst, src, LEN-1); }

また、strncpyは、部分文字列を別の文字列にコピーするのにあまり役に立たないことも認めています。srcがn文字より短い場合、宛先文字列は切り捨てられます。

41
Ernelli

安全なコピーを行うstrlcpyのようなオープンソースの実装が既にあります。

http://en.wikipedia.org/wiki/Strlcpy

参照には、ソースへのリンクがあります。

24
StampedeXV

当初、 7th Edition UNIX ファイルシステム(DIR(5)を参照)には、ファイル名を14バイトに制限するディレクトリエントリがありました。ディレクトリ内の各エントリは、iノード番号の2バイトと名前の14バイトで構成され、14文字にヌルが埋め込まれますが、必ずしもヌルで終了するわけではありません。 strncpy()はこれらのディレクトリ構造で動作するように設計されている、または少なくとも、その構造で完全に動作するというのが私の信念です。

考慮してください:

  • 14文字のファイル名はヌルで終了しませんでした。
  • 名前が14バイトよりも短い場合は、完全な長さ(14バイト)までヌルが埋め込まれました。

これはまさに以下によって達成されるものです。

_strncpy(inode->d_name, filename, 14);
_

したがって、strncpy()は元のニッチアプリケーションに理想的に適合しました。それは偶然にも、ヌルで終わる文字列のオーバーフローを防ぐことについてでした。

(長さ14までのNULLパディングは深刻なオーバーヘッドではないことに注意してください-バッファーの長さが4 KBで、20文字を安全にコピーするだけであれば、余分な4075 NULLは深刻な過剰であり、簡単にできます長いバッファーに繰り返し素材を追加している場合、二次的な動作になります。)

24

Strncpyは、プログラムのserによるスタックオーバーフロー攻撃に対して安全です。エラーからあなたを保護しませんyo nullで終了していないものを印刷するなど、プログラマーはそうします文字列、あなたが説明した方法。

Printfによって印刷される文字数を制限することで、説明した問題によるクラッシュを回避できます。

char my_string[10];
//other code here
printf("%.9s",my_string); //limit the number of chars to be printed to 9
8
Liran Orevi

ここで指定されたstrlcpy()を使用します。 http://www.courtesan.com/todd/papers/strlcpy.html

Libcに実装がない場合は、これを試してください:

size_t strlcpy(char* dst, const char* src, size_t bufsize)
{
  size_t srclen =strlen(src);
  size_t result =srclen; /* Result is always the length of the src string */
  if(bufsize>0)
  {
    if(srclen>=bufsize)
       srclen=bufsize-1;
    if(srclen>0)
       memcpy(dst,src,srclen);
    dst[srclen]='\0';
  }
  return result;
}

(2004年に私が作成-パブリックドメイン専用)

5
alex tingle

strncpy()の代わりに、使用できます

snprintf(buffer, BUFFER_SIZE, "%s", src);

以下は、srcからdestに最大size-1非null文字をコピーし、nullターミネータを追加する1行のライナーです。

static inline void cpystr(char *dest, const char *src, size_t size)
{ if(size) while((*dest++ = --size ? *src++ : 0)); }
3
Christoph

strncpyは、利用可能な文字列バッファーを直接操作します。メモリを直接操作している場合は、バッファーサイズを手動で設定する必要があり、「\ 0」を手動で設定できます。

プレーンCにはこれに勝る代替手段はないと思いますが、生のメモリで遊ぶときと同じくらい注意を払えば、それほど悪くはありません。

3
Arkaitz Jimenez

私は常に好んだ:

 memset(dest, 0, LEN);
 strncpy(dest, src, LEN - 1);

後で修正するためのアプローチですが、それは実際には好みの問題です。

3
stonemetal

新しい拡張機能に依存することなく、過去に次のようなことをしました。

/* copy N "visible" chars, adding a null in the position just beyond them */
#define MSTRNCPY( dst, src, len) ( strncpy( (dst), (src), (len)), (dst)[ (len) ] = '\0')

そしておそらく:

/* pull up to size - 1 "visible" characters into a fixed size buffer of known size */
#define MFBCPY( dst, src) MSTRNCPY( (dst), (src), sizeof( dst) - 1)

なぜ新しい「組み込み」(?)関数の代わりにマクロなのですか?かつてはかなり多くの異なるユニックスがあり、また、私が毎日Cをやっていたときに移植する必要があった他の非UNIX(非ウィンドウ)環境もあったからです。

2
Roboprog

これらの機能は設計されたものよりも進化しているため、「理由」はありません。 「方法」を学ばなければなりません。残念ながら、Linuxのマニュアルページには少なくともこれらの関数の一般的なユースケースの例がなく、レビューしたコードの誤用lotsに気づきました。ここにいくつかメモをしました: http://www.pixelbeat.org/programming/gcc/string_buffers.html

2
pixelbeat