Проблема проверки перекрытия между двумя строками

В настоящее время я переписываю srtcat() из стандартной библиотеки C и настроил некоторые проверки, чтобы избежать проблем с перекрытием. Проблема в том, что моя программа все равно входит в обработку ошибок.

Вот код:

char *my_strcat(char *restrict dest, const char *restrict src)
{
    size_t dest_len = 0, src_len = 0;
    char *p = dest;
    src_len = my_strlen(src);
    if (!dest || !src)
        return NULL;
    dest_len = my_strlen(dest);
    if (src >= dest && src < dest + dest_len) {
        return NULL;
    }
    if (dest >= src && dest < src + src_len) {
        return NULL;
    }
    while (*p != '\0') p++, dest_len++;
    if (dest_len + src_len + 1 > sizeof(dest))
        return NULL;
    p = dest + dest_len;
    while (*src != '\0')
        *p++ = *src++;
    *p = '\0';
    return dest;
}
size_t my_strlen(const char *s)
{
    size_t count = 0;
    if (s != NULL) {
        while (*s != 0) {
            count++;
            s++;
        }
    }
    return count;
}

Я тестировал так:

int main(int argc, char **argv)
{
    const char *src = "Hello";
    char dest[100] = " world!";
    char *test = my_strcat(dest, src);
    printf("Src : %s Dest : %s\n", src, dest);
    printf("Return adress : %p, Value : %s\n", test, test);
    return 0;
}

Согласно gdb :

if (src >= dest && src < dest + dest_len) 
1: dest = 0x7fffffffda70 " world!"
2: src = 0x555555557004 "Hello"
3: dest_len = 0
4: src_len = 5

Выход

Src : Hello Dest :  world!
Return adress : (nil), Value : (null)

Ты видишь проблему?

Обновлять

Следуя вашим предложениям, я изменил код следующим образом:

char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size)
{
    size_t dest_len = 0, src_len = 0;
    char *p = dest;
    if (!dest || !src)
        return NULL;
    src_len = my_strlen(src);
    dest_len = my_strlen(dest);
    if (src >= dest && src < dest + dest_len) {
        return NULL;
    }
    if (dest >= src && dest < src + src_len) {
        return NULL;
    }
    while (*p != '\0') p++, dest_len++;
    if (dest_len + src_len + 1 > d_size)
        return NULL;
    p = dest + dest_len;
    while (*src != '\0')
        *p++ = *src++;
    *p = '\0';
    return dest;
}

И в основном : char *test = my_strcat(dest, src, sizeof(dest));

Но все равно не работает:

Src : Hello Dest :  world!
Return adress : 0x7fff74bc5650, Value :  world!

🤔 А знаете ли вы, что...
C оставляет множество возможностей для оптимизации кода и достижения высокой производительности приложений.


51
2

Ответы:

dest_len + src_len + 1 > sizeof(dest)

sizeof(dest) — размер указателя sizeof(char*). Если вы хотите проверить, не переполнится ли dest, вы должны передать размер в качестве аргумента. Смотрите strlcpy или strncpy.

src >= dest

Обратите внимание, что сравнение указателей, которые не указывают на один и тот же массив, технически недопустимо. Чтобы привнести немного валидности, вы можете сделать (uintptr_t)stc >= (uintptr_t)dest. Как работает сравнение указателей в C? Можно ли сравнивать указатели, которые не указывают на один и тот же массив? Является ли сравнение двух указателей поведением < undefined, если они оба приведены к целочисленному типу? Почему сравнение указателей с неопределенным поведением все равно дает правильные результаты? и др.


Решено

Пытаясь направить к пониманию этой проблемы, лучше всего представить, какой должен быть работающий код (для изучения). Иногда слишком много слов просто запутывают ситуацию:

char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size) {
    if ( !dest || !src )
        return NULL;

    size_t src_len = strlen( src );
    size_t dest_len = strlen( dest );

    if ( dest_len + src_len + 1 > d_size )
        return NULL;

    char *p = dest + dest_len;
    while( (*p++ = *src++ ) != '\0' )
        ;

    return dest;
}

int main() {
    const char *src = "Hello";
    char dest[100] = " world!";
    printf("Src : %s Dest : %s\n", src, dest);

    char *test = my_strcat( dest, src, sizeof dest );

    if ( test )
        printf("Value : %s\n", test );

    return 0;
}

Теперь можно поэкспериментировать, уменьшив размер dest до чего-то большего, чем «мир!» но меньше, чем "world!Hello"... Возможно, 9 байт???

И теперь, когда конкатенация должна работать (в достаточно большой буфер), добавьте код, чтобы гарантировать отсутствие перекрытия фактических массивов символов. Известен размер dest и измерена длина src.