web-dev-qa-db-ja.com

strlenの安全なバージョンはありますか?

std :: strlen は、\ 0で終了していないC文字列を処理しません。安全なバージョンはありますか?

PS私はc ++ではstd :: stringをc文字列の代わりに使用する必要があることを知っていますが、この場合、私の文字列は共有メモリに保存されます。

[〜#〜]編集[〜#〜]

わかりました、いくつか説明を追加する必要があります。

私のアプリケーションは(ある程度の長さの)共有メモリから文字列を取得しているため、文字の配列として表すことができます。ライブラリにこの文字列を書き込むバグがある場合、文字列はゼロで終了せず、strlenは失敗する可能性があります。

26
BЈовић

C-stringを次のように定義した場合

char* cowSays = "moo";

次に、最後に '\ 0'が自動的に取得され、strlenは3を返します。次のように定義すると、

char iDoThis[1024] = {0};

空のバッファ(および文字の配列、すべてnull文字)を取得します。バッファの長さを超過しない限り、好きなもので埋めることができます。最初はstrlenは0を返し、何かを書き込んだ後はstrlenから正しい数を取得します。
これを行うこともできます:

char uhoh[100];
int len = strlen(uhoh);

しかし、その配列に何があるか分からないので、それは悪いでしょう。それはあなたがそうでないかもしれないヌル文字を打つ可能性があります。ポイントは、ヌル文字が 定義された標準 文字列が終了したことを宣言する方法。
ヌル文字がないことは、 定義により 文字列が終了していないこと。これを変更すると、文字列の動作のパラダイムが崩れます。あなたがしたいことはあなた自身のルールを作り上げることです。 C++ではそれができますが、多くのコードを自分で書く必要があります。

編集 新しく追加した情報から、実行したいことは、配列をループして、ヌル文字を手動で確認することです。 ASCII文字のみを予期している場合は特に検証を行う必要があります(特に英数字を予期している場合)。これは、最大サイズがわかっていることを前提としています。検証する必要がない場合文字列の内容strnlenファミリの関数の1つを使用できます: http://msdn.Microsoft.com/en-us/library/z50ty2zh%28v=vs.80% 29.aspx
http://linux.about.com/library/cmd/blcmdl3_strnlen.htm

8
Dennis

文字列が共有メモリにあることを追加しました。これは読み取り可能で、サイズが固定されていることが保証されています。したがって、size_t MaxPossibleSize = startOfSharedMemory + sizeOfSharedMemory - input; strnlen(input, MaxPossibleSize)を使用できます(nの余分なstrnlenに注意してください)。

これは、MaxPossibleSizeに続く共有メモリに\0がない場合はinputを返し、存在する場合は文字列の長さを返します。 (共有メモリの最後のバイトが最初のMaxPossibleSize-1である場合、可能な最大文字列長はもちろん\0です)

15
MSalters

Nullで終了していないC文字列はC文字列ではなく、単に文字の配列であり、長さを見つける方法はありません。

12
size_t safe_strlen(const char *str, size_t max_len)
{
    const char * end = (const char *)memchr(str, '\0', max_len);
    if (end == NULL)
        return max_len;
    else
        return end - str;
}
6

より優れたライブラリを入手するか、またはライブラリを確認してください。ライブラリが信頼できるとは言えない場合、プログラムはどのようにh%^&lに期待していますか?

それは言った、あなたがストリングが存在するbuifferの長さを知っていると仮定すると、どうですか

buffer[-1+sizeof(buffer)]=0 ;
 x = strlen(buffer) ; 
  • 必要以上にバッファを大きくすると、libをテストできます。

    assert(x<-1+sizeof(buffer));
    
3
mattnz

共有メモリのサイズを取得する必要がある場合は、

// get memory size
struct shmid_ds shm_info;
size_t shm_size;
int shm_rc;
if((shm_rc = shmctl(shmid, IPC_STAT, &shm_info)) < 0)
    exit(101);
shm_size = shm_info.shm_segsz;

Strlenを使用する代わりに、nullで終了していることが確実な場合は、shm_size-1を使用できます。それ以外の場合は、data [shm_size-1] = '\ 0'でnullで終了できます。次にstrlen(data)を使用します。

0
edo888

C11には、strnlen_sなどの「安全な」関数が含まれています。 strnlen_sは、追加の最大長引数を取ります(size_t)。この引数は、多くの文字を確認してもnull文字が見つからない場合に返されます。また、nullポインターが指定されている場合は、2番目の引数を返します。

size_t strnlen_s(const char *, size_t);

C11の一部ですが、__STDC_LIB_EXT1__の定義を介して、コンパイラがこれらの境界チェックの「安全な」関数をサポートしていることを確認することをお勧めします。さらに、このような関数を使用する場合は、__STDC_WANT_LIB_EXT1__を含める前に、別のマクロ1string.hに設定する必要があります。これらの関数の起源に関するいくつかのスタックオーバーフローの解説については here を、C++のドキュメントについては here を参照してください。

GCCとClangはPOSIX関数strnlenもサポートし、string.h内で提供します。 Microsoftもstrnlenを提供しています。これはstring.hにもあります。

0
user2023370

文字列をエンコードする必要があります。例えば:

struct string
{
    size_t len;
    char *data;
} __attribute__(packed);

共有メモリの場所の最初のsizeof(size_t)バイトがchar配列のサイズであることがわかっている場合は、任意の文字配列を受け入れることができます。この方法で配列をチェインしたい場合は注意が必要です。

もう一方の端を信頼して文字列を終了するか、共有メモリセグメントの境界の外に出ない独自のstrlenをロールすることをお勧めします(少なくともそのセグメントのサイズがわかっている場合)。

0
Mel

簡単な解決策:

buff[BUFF_SIZE -1] = '\0'

ofcこれは、文字列が元々正確にBUFF_SIZE-1であったのか、それとも単に終了しなかったのかを通知しません...そのため、xtraロジックが必要です。

0
NoSenseEtAl

このポータブルナゲットはどうですか。

int safeStrlen(char *buf, int max)
{
   int i;
   for(i=0;buf[i] && i<max; i++){};
   return i;
}
0
hotplasma

Neil Butterworth は上記の回答ですでに述べています:\ 0文字で終了していないC-StringはC-Stringではありません!

唯一の可能性は、不変のアダプターまたは\ 0終了文字を含むC-Stringの有効なコピーを作成する何かを書くことです。もちろん、入力が間違っていて、C-Stringが次のように定義されている場合:

char cstring[3] = {'1','2','3'};

現在、メモリに123@4x\0のようなものが存在する可能性があるため、実際には予期しない動作が発生します。したがって、たとえばstrlen()の結果は、期待どおり3ではなく6になりました。

次のアプローチは、どのような場合でも安全なC-Stringを作成する方法を示しています。

char *createSafeCString(char cStringToCheck[]) {
    //Cast size_t to integer
    int size = static_cast<int>(strlen(cStringToCheck)) ;
    //Initialize new array out of the stack of the method
    char *pszCString = new char[size + 1];
    //Copy data from one char array to the new
    strncpy(pszCString, cStringToCheck, size);
    //set last character to the \0 termination character
    pszCString[size] = '\0';
    return pszCString;
}

これにより、C-Stringを操作して別のメモリに書き込みを行わないようにすることができます。

しかし、これはあなたが望んだものではありません。私は知っていますが、終了せずにchar配列の長さを実現する他の方法はありません。これはアプローチでもありません。これは、ユーザー(または開発者)が*****を挿入しても正常に機能することを保証します。

0
Spektakulatius