strlen 有安全版本吗?

发布于 2024-11-05 17:57:46 字数 343 浏览 1 评论 0原文

std::strlen 不处理不是 \0 的 C 字符串终止。有安全版本吗?

PS 我知道在 c++ 中应该使用 std::string 而不是 c 字符串,但在这种情况下我的字符串存储在共享内存中。

编辑

好的,我需要添加一些解释。

我的应用程序正在从共享内存(具有一定长度)获取字符串,因此它可以表示为字符数组。如果编写此字符串的库中存在错误,则该字符串不会以零结尾,并且 strlen 可能会失败。

std::strlen doesn't handle c strings that are not \0 terminated. Is there a safe version of it?

PS I know that in c++ std::string should be used instead of c strings, but in this case my string is stored in a shared memory.

EDIT

Ok, I need to add some explanation.

My application is getting a string from a shared memory (which is of some length), therefore it could be represented as an array of characters. If there is a bug in the library writing this string, then the string would not be zero terminated, and the strlen could fail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

倦话 2024-11-12 17:57:46

您已添加该字符串位于共享内存中。这是保证可读且大小固定的。因此,您可以使用 size_t MaxPossibleSize = startOfSharedMemory + sizeOfSharedMemory - input; strnlen(input, MaxPossibleSize)(注意 strnlen 中额外的 n)。

如果input之后的共享内存中没有\0,则返回MaxPossibleSize,如果有,则返回字符串长度。 (最大可能的字符串长度当然是MaxPossibleSize-1,以防共享内存的最后一个字节是第一个\0

You've added that the string is in shared memory. That's guaranteed readable, and of fixed size. You can therefore use size_t MaxPossibleSize = startOfSharedMemory + sizeOfSharedMemory - input; strnlen(input, MaxPossibleSize) (mind the extra n in strnlen).

This will return MaxPossibleSize if there's no \0 in the shared memory following input, or the string length if there is. (The maximal possible string length is of course MaxPossibleSize-1, in case the last byte of shared memory is the first \0)

牛↙奶布丁 2024-11-12 17:57:46

不以 null 结尾的 C 字符串不是 C 字符串,它们只是字符数组,并且无法找到它们的长度。

C strings that are not null-terminated are not C strings, they are simply arrays of characters, and there is no way of finding their length.

剩余の解释 2024-11-12 17:57:46
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;
}
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;
}
野侃 2024-11-12 17:57:46

如果你定义一个 C 字符串,

char* cowSays = "moo";

那么你会自动在末尾得到 '\0' 并且 strlen 将返回 3。如果你这样定义它:

char iDoThis[1024] = {0};

你会得到一个空缓冲区(和字符数组,所有这些都是空字符)。然后,只要不超过缓冲区长度,您就可以用您喜欢的内容填充它。一开始strlen会返回0,一旦你写完一些东西,你也会从strlen得到正确的数字。

您也可以这样做:

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

但这会很糟糕,因为您不知道该数组中有什么。它可能会击中空字符,而您可能不会击中。要点是空字符是声明字符串结束的定义的标准方式。

没有空字符意味着根据定义字符串尚未完成。改变这一点将打破字符串工作的范式。你想做的就是制定自己的规则。 C++ 可以让你做到这一点,但你必须自己编写大量代码。

编辑
根据新添加的信息,您想要做的是循环数组并手动检查空字符。如果您只需要 ASCII 字符(特别是需要字母数字字符),您还应该进行一些验证。这假设您知道最大尺寸。
如果您不需要验证字符串的内容,那么您可以使用 strnlen 系列函数之一:
http://msdn.microsoft.com/en -us/library/z50ty2zh%28v=vs.80%29.aspx
http://linux.about.com/library/cmd/blcmdl3_strnlen.htm

If you define a c-string as

char* cowSays = "moo";

then you autmagically get the '\0' at the end and strlen would return 3. If you define it like:

char iDoThis[1024] = {0};

you get an empty buffer (and array of characters, all of which are null characters). You can then fill it with what you like as long as you don't over-run the buffer length. At the start strlen would return 0, and once you have written something you would also get the correct number from strlen.

You could also do this:

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

but that would be bad, because you have no idea what is in that array. It could hit a null character you might not. The point is that the null character is the defined standard manner to declare that the string is finished.

Not having a null character means by definition that the string is not finished. Changing that will break the paradigm of how the string works. What you want to do is make up your own rules. C++ will let you do that, but you will have to write a lot of code yourself.

EDIT
From your newly added info, what you want to do is loop over the array and check for the null character by hand. You should also do some validation if you are expecting ASCII characters only (especially if you are expecting alpha-numeric characters). This assumes that you know the maximum size.
If you do not need to validate the content of the string then you could use one of the strnlen family of functions:
http://msdn.microsoft.com/en-us/library/z50ty2zh%28v=vs.80%29.aspx
http://linux.about.com/library/cmd/blcmdl3_strnlen.htm

萝莉病 2024-11-12 17:57:46

是的,从 C11 开始:

size_t strnlen_s( const char *str, size_t strsz );

位于

Yes, since C11:

size_t strnlen_s( const char *str, size_t strsz );

Located in <string.h>

雨后彩虹 2024-11-12 17:57:46

获得一个更好的库,或者验证您拥有的库 - 如果您不能相信您的库会按照它所说的去做,那么您希望您的程序如何做?

也就是说,假设您知道字符串所在的缓冲区的长度,那么

buffer[-1+sizeof(buffer)]=0 ;
 x = strlen(buffer) ; 
  • 使缓冲区大于所需的大小,然后您就可以测试该库了。

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

Get a better library, or verify the one you have - if you can't trust you library to do what it says it will, then how the h%^&l do you expect your program to?

Thats said, Assuming you know the length of the buiffer the string resides, what about

buffer[-1+sizeof(buffer)]=0 ;
 x = strlen(buffer) ; 
  • make buffer bigger than needed and you can then test the lib.

    assert(x<-1+sizeof(buffer));
    
毁梦 2024-11-12 17:57:46

C11 包括“安全”函数,例如 strnlen_s。 strnlen_s 采用额外的最大长度参数(size_t)。如果在检查这么多字符后未找到空字符,则返回此参数。如果提供了空指针,它还会返回第二个参数。

size_t strnlen_s(const char *, size_t);

虽然是 C11 的一部分,但建议您通过 __STDC_LIB_EXT1__ 的定义来检查您的编译器是否支持这些边界检查“安全”函数。此外,如果用户打算使用此类函数,则在包含 string.h 之前,还必须将另一个宏 __STDC_WANT_LIB_EXT1__ 设置为 1。有关这些函数起源的一些 Stack Overflow 评论,请参阅此处,以及此处获取 C++ 文档。

GCC 和 Clang 还支持 POSIX 函数 strnlen,并在 string.h 中提供它。 Microsoft 也提供了 strnlen,它也可以在 string.h 中找到。

C11 includes "safe" functions such as strnlen_s. strnlen_s takes an extra maximum length argument (a size_t). This argument is returned if a null character isn't found after checking that many characters. It also returns the second argument if a null pointer is provided.

size_t strnlen_s(const char *, size_t);

While part of C11, it is recommended that you check that your compiler supports these bounds-checking "safe" functions via its definition of __STDC_LIB_EXT1__. Furthermore, a user must also set another macro, __STDC_WANT_LIB_EXT1__, to 1, before including string.h, if they intend to use such functions. See here for some Stack Overflow commentary on the origins of these functions, and here for C++ documentation.

GCC and Clang also support the POSIX function strnlen, and provide it within string.h. Microsoft too provide strnlen which can also be found within string.h.

束缚m 2024-11-12 17:57:46

您需要对字符串进行编码。例如:

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

如果您知道共享内存位置的第一个 sizeof(size_t) 字节是字符数组的大小,则可以接受任何字符数组。当你想以这种方式链接数组时,事情会变得很棘手。

最好相信您的另一端终止它的字符串或滚动您自己的 strlen,该 strlen 不会超出共享内存段的边界(前提是您至少知道该段的大小)。

You will need to encode your string. For example:

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

You can then accept any array of characters if you know the first sizeof(size_t) bytes of the shared memory location is the size of the char array. It gets tricky when you want to chain arrays this way.

It's better to trust your other end to terminate it's strings or roll your own strlen that does not go outside the bounderies of the shared memory segment (providing you know at least the size of that segment).

迷路的信 2024-11-12 17:57:46

如果您需要获取共享内存的大小,请尝试使用

// 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;

shm_size - 1(如果您确定它是 null 终止的)而不是使用 strlen 。否则你可以通过 data[shm_size - 1] = '\0'; 来终止它然后使用 strlen(数据);

If you need to get the size of shared memory, try to use

// 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;

Instead of using strlen you can use shm_size - 1 if you are sure that it is null terminated. Otherwise you can null terminate it by data[shm_size - 1] = '\0'; then use strlen(data);

胡大本事 2024-11-12 17:57:46

这个便携式金块怎么样:

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

How about this portable nugget:

int safeStrlen(char *buf, int max)
{
   int i;
   for(i=0;buf[i] && i<max; i++){};
   return i;
}
鹤仙姿 2024-11-12 17:57:46

正如 Neil Butterworth 在上面的回答中已经说过的那样:不以 \0 字符结尾的 C 字符串不是 C 字符串!

您唯一的机会是编写一个不可变的适配器或创建带有 \0 终止字符的 C 字符串的有效副本的东西。当然,如果输入错误并且有一个像这样定义的C字符串:

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

确实会导致意外的行为,因为现在内存中可能存在类似123@4x\0的东西。因此,例如 strlen() 的结果现在是 6,而不是预期的 3。

以下方法展示了如何在任何情况下创建安全的 C 字符串:

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 字符串时不会写入其他内容的内存。

但这不是你想要的。我知道,但是没有其他方法可以在不终止的情况下实现 char 数组的长度。这甚至不是一种方法。它只是确保即使用户(或开发人员)插入 ***** 也能正常工作。

As Neil Butterworth already said in his answer above: C-Strings which are not terminated by a \0 character, are no C-Strings!

The only chance you do have is to write an immutable Adaptor or something which creates a valid copy of the C-String with a \0 terminating character. Of course, if the input is wrong and there is an C-String defined like:

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

will indeed result in unexpected behavior, because there can be something like 123@4x\0 in the memory now. So the result of of strlen() for example is now 6 and not 3 as expected.

The following approach shows how to create a safe C-String in any case:

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;
}

This ensures that if you manipulate the C-String to not write on the memory of something else.

But this is not what you wanted. I know, but there is no other way to achieve the length of a char array without termination. This isn't even an approach. It just ensures that even if the User (or Dev) is inserting ***** to work fine.

平定天下 2024-11-12 17:57:46

一个简单的解决方案:

buff[BUFF_SIZE -1] = '\0'

ofc 这不会告诉你字符串最初是否正好是 BUFF_SIZE-1 长或者它只是没有终止......所以你需要额外的逻辑。

a simple solution:

buff[BUFF_SIZE -1] = '\0'

ofc this will not tell you if the string originally was exactly BUFF_SIZE-1 long or it was just not terminated... so you need xtra logic for that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文