*ฅ^•ﻌ•^ฅ* ✨✨  HWisnu's blog  ✨✨ о ฅ^•ﻌ•^ฅ

Make your own safe C functions

Introduction

blah blah blah sorry I'm too lazy to write this part. The point is if the safe function alternative is not available in your system (as pointed out in the mid section of this article), you can always make your own version of safe function.

What is memcpy?

memcpy is a standard C function that copies data from one location to another. It is declared in the string.h header file and is widely used in C programming.

void* memcpy(void* dest, const void* src, size_t n);

The memcpy function takes three arguments: dest, src, and n. dest is the destination address where the data will be copied, src is the source address where the data is located, and n is the number of bytes to be copied.

Overlapping memory regions

The main issue here memcpy does not check overlapping regions and may produce incorrect results (the dreaded undefined behavior)

Take a look at this simple C program:

int main()
{
    char buffer[10] = "abcdefgh";
    memcpy(buffer+3, buffer, 5);
    printf("\nUsing memcpy: \n");
    for (int i=0; i<10; i++) {
        printf("%c ", buffer[i]);
    }
    printf("\n");

    return 0;
}

Compiling the program it will result in:

a b c a b c d b

which is incorrect. You might encounter other variation of results, different from what's shown above.

Create a safe alternative: s_memcpy

Check out this syntax for s_memcpy (a custom function):

void* s_memcpy(void* dest, const void* src, size_t n)
{
    assert(dest != NULL);
    assert(src != NULL);
    assert(n > 0);

    if (dest == src) {
        return dest; // no need to copy
    }

    if ((uintptr_t)dest < (uintptr_t)src + n && (uintptr_t)dest + n > (uintptr_t)src) {
        // Memory regions overlap, use memmove
        return memmove(dest, src, n);
    }

    // Memory regions do not overlap, use memcpy
    return memcpy(dest, src, n);
}

Detailed explanation:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{
    char buffer2[10] = "abcdefgh";
    s_memcpy(buffer2+3, buffer2, 5);
    printf("\nUsing s_memcpy: \n");
    for (int i=0; i<10; i++) {
        printf("%c ", buffer2[i]);
    }
    printf("\n");

    return 0;
}

Program's result:

a b c a b c d e

This is the correct output.

Use compiler flags!

The easiest method is to use compiler flags. Modern C compilers are powerful you'd be stupid if you don't leverage its features to help you write safe C programs!

I use alias when I compile C program:

alias gcc-14_safest='gcc-14 -ggdb -Wall -Werror -Wextra -fsanitize=address -fno-omit-frame-pointer'

Try compile the first memcpy example code using the above compiler flags, it would throw error right away and you would know on the spot something's incorrect and need fixing.

error: 'memcpy' accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3 [-Werror=restrict]

Summary

#c #low level #programming #safety