Home > database >  Strlen safety in C
Strlen safety in C

Time:10-28

Imagine having this code and you don't really know what to except in the pointer of char (a string terminated or an array of chars (string not terminated)), is possibile to use strlen function on a safe way that handle not terminated string? (prevent overflows if the input is not a string terminanted) or can you fix it only by knowing the size of what you pass in input? so the function will become foo(char *c, size_t MAXSIZE)?

void foo(char *c) {
    a = strlen(c);
}

CodePudding user response:

It's not safe to use just strlen if it's not a null-terminated byte string. According to cppreference:

The behavior is undefined if str is not a pointer to a null-terminated byte string.

If you want to cover the case of not null-terminated byte string then you should use the size_t strnlen_s( const char *str, size_t strsz ), which works just like normal strlen with the exception:

that the function returns zero if str is a null pointer and returns strsz if the null character was not found in the first strsz bytes of str.

CodePudding user response:

It is not possible unless you pass the size.

You can avoid crashing, at least - by asking the operating system how much readable memory there is at this address. (Windows: call VirtualQuery. Linux: read /proc/self/maps). But it's not helpful. There can be lots of readable memory after your string that's totally unrelated to your string but just happened to get allocated after it. Finding out how much memory is safe to read doesn't tell you how long the string is.

CodePudding user response:

We never know what really is behind a pointer. It could also only be a pointer to a single character. Even by passing a size, you could imagine someone passing a bad pointer and an unrelated value.

C is not a safe language, it does not have runtime type checks. You can do anything you want, you can't prevent others from doing anything they want with your functions.

CodePudding user response:

To check if the buffer contains a null terminator within a maximum number of characters, the memchr function can be used. The following function safe_strlen behaves like the strlen_s function defined by Annex K of the C specification, and uses memchr to find the position of the first (if any) null terminator in the buffer.

#include <stdint.h>
#include <string.h>

/**
 * Get the length of a possibly null terminated string, clamped to a maximum.
 *
 * If \p s is not NULL, searches up to \p maxsize bytes from \p s to find the
 * first null terminator, if any.
 *
 * \param s        Start of string.
 * \param maxsize  Maximum number of bytes to search.
 *
 * \return 0 if \p s is \c NULL.
 * \return \p maxsize if null terminator not found.
 * \return length of null terminated string if null terminator found.
 */
size_t safe_strlen(const char *s, size_t maxsize)
{
    size_t length = 0;
    if (s)
    {
#if PTRDIFF_MAX < SIZE_MAX
        /* May need to search the buffer in chunks. */
        while (maxsize)
#endif
        {
            const char *e;
            size_t pos;
#if PTRDIFF_MAX < SIZE_MAX
            if (maxsize > PTRDIFF_MAX)
            {
                /* Limit size of chunk. */
                pos = PTRDIFF_MAX;
            }
            else
#endif
            {
                /* This is the final chunk. */
                pos = maxsize;
            }
            /* Search for null terminator in chunk. */
            e = memchr(s, 0, pos);
            if (e) {
                /* Null terminator found. */
                pos = e - s;  /* position of null terminator in chunk */
#if PTRDIFF_MAX < SIZE_MAX
                /* Make this the final chunk. */
                maxsize = pos;
#endif
            }
            /* Update returned length. */
            length  = pos;
#if PTRDIFF_MAX < SIZE_MAX
            /* Advance to next chunk. */
            s  = pos;
            maxsize -= pos;
#endif
        }
    }
    return length;
}

The code is complicated by the necessity to deal with buffer sizes larger than PTRDIFF_MAX if PTRDIFF_MAX is less than SIZE_MAX. The core functionality without the extra safety checks is as follows:

/* less safe version of the above - may result in undefined behavior. */
size_t less_safe_strlen(const char *s, size_t maxsize)
{
    size_t length = 0;
    if (s)
    {
        const char *e = memchr(s, 0, maxsize);
        if (e)
        {
            length = e - s;
        }
        else
        {
            length = maxsize;
        }
    }
    return length;
}
  • Related