web-dev-qa-db-ja.com

gcc-8 -Wstringop-truncation良い方法は何ですか?

GCC 8は-Wstringop-truncation警告を追加しました。 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82944 から:

バグ81117のr254630を介してGCC 8.0で追加された-Wstringop-truncation警告は、ソース文字列から終了NUL文字を切り捨てるstrncpy関数の意図しない使用を強調することを特に目的としています。リクエストに含まれるそのような誤用の例は次のとおりです。

char buf[2];

void test (const char* str)
{
  strncpy (buf, str, strlen (str));
}

このコードでも同じ警告が表示されます。

strncpy(this->name, name, 32);

warning: 'char* strncpy(char*, const char*, size_t)' specified bound 32 equals destination size [-Wstringop-truncation`]

this->namechar name[32]であり、nameが潜在的に32を超える長さのchar*であることを考慮してください。namethis->nameにコピーし、32より大きい場合は切り捨てます。size_tは31にする必要があります32の代わりに?よくわかりません。 this->nameがNULで終了する必要はありません。

16
JRR

このメッセージは、まさにあなたがしていることをしていることをあなたに警告しようとしています。多くの場合、それはプログラマーが意図したものではありません。それが意図したものである場合(つまり、コードが文字配列にnull文字が含まれない場合を正しく処理する)、警告をオフにします。

グローバルにオフにしたくない、またはオフにできない場合は、@ doronで指摘されているようにローカルでオフにできます。

#include <string.h>
char d[32];
void f(const char *s) {
#pragma GCC diagnostic Push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    strncpy(d, s, 32);
#pragma GCC diagnostic pop
}
12
user743382

strncpyを使用する正当な理由はほとんどありません。これは非常に危険な機能です。ソース文字列の長さ(ヌル文字を除く)が宛先バッファーのサイズと等しい場合、strncpyは宛先バッファーの末尾にヌル文字を追加しません。したがって、宛先バッファはnullで終了しません。

Linuxでこの種のコードを書く必要があります。

lenSrc = strnlen(pSrc, destSize)
if (lenSrc < destSize)
    memcpy(pDest, pSrc, lenSrc + 1);
else {
    /* Handle error... */
}

あなたの場合、コピーでソースを切り捨てたいが、ヌルで終了する宛先バッファが必要な場合は、次の種類のコードを記述できます。

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp);
pDest[sizeCp] = '\0';

編集:ああ... NULLで終了する必要がない場合は、strncpyを使用するのが適切です。そして、はい、31ではなく32で呼び出す必要があります。この警告を無効にして無視する必要があると思います...正直なところ、これに対する適切な答えはありません...

Edit2:strncpy関数を模倣するために、次のコードを書くことができます:

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp + 1);
2
benjarobin

この新しいGCC警告により、strncpy()は多くのプロジェクトでほとんど使用できなくなります。コードレビューは、警告を生成するコードを受け入れません。しかし、strncpy()が十分に短い文字列でのみ使用され、終端のゼロバイトを書き込むことができる場合、最初に宛先バッファをゼロにしてから、単純なstrcpy()で同じことを実現できます。ジョブ。

実際、strncpy()は関数の1つであり、Cライブラリに含めない方がよいでしょう。もちろん、正当なユースケースがあります。しかし、ライブラリ設計者は、strncpy()に対応する固定サイズの文字列対応版も標準に入れるのを忘れていました。そのような最も重要な関数であるstrnlen()strndup()は、strncpy()が作成されてから数十年後にPOSIX.1に2008年に組み込まれただけです!また、strncpy()で生成された固定長文字列を、正しいCセマンティクスで事前に割り当てられたバッファーにコピーする関数はまだありません。つまり、常に0終了バイトを書き込みます。そのような関数の1つは次のとおりです。

_// Copy string "in" with at most "insz" chars to buffer "out", which
// is "outsz" bytes long. The output is always 0-terminated. Unlike
// strncpy(), strncpy_t() does not zero fill remaining space in the
// output buffer:
char* strncpy_t(char* out, size_t outsz, const char* in, size_t insz){
    assert(outsz > 0);
    while(--outsz > 0 && insz > 0 && *in) { *out++ = *in++; insz--; }
    *out = 0;
    return out;
}
_

混乱を避けるために、strncpy_t()に2つの長さの入力を使用することをお勧めします。単一のsize引数しかなかった場合、それが出力バッファーのサイズであるか、入力文字列の最大長(通常は1つ少ない)。

2
Kai Petzke

警告を抑制する最善の方法は、式を括弧で囲むことです このgRPCパッチのように

(strncpy(req->initial_request.name, lb_service_name,
         GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH));

#pragma診断抑制ソリューションの問題は、コンパイラがプラグマも特定の警告も認識しない場合、#pragma自体が警告を表示することです。また、それは冗長すぎます。

0
Mehrdad Afshari