malloc and free of a char-array
Here are some things that may help you improve your code.

Fix the bug

Once memory is freed, it should not be referenced again.  Unfortunately, your code allocates memory and then frees it and then returns a pointer to the freed memory.  That's a serious bug!  To fix it, simply omit the free within the function and make sure the caller calls free instead.  Alternatively, you could avoid all of that by reversing the passed string in place.

Use the required #includes

The code uses strlen which means that it should #include &lt;string.h&gt; and malloc and free which means that it should #include &lt;stdlib.h&gt;.  It was not difficult to infer, but it helps reviewers if the code is complete.

Use const where practical

In your revere_string routine, the string passed into the function is not and should not be altered.  You should indicate that fact by declaring it like this:

char* reverse_string(const char* string)


Check for NULL pointers

The code must avoid dereferencing a NULL pointer if the call to malloc fails.  The only indication that it has failed is if malloc returns NULL; if it does, it would probably make most sense to immediately return that NULL pointer.

Learn to use pointers instead of indexing

Using pointers effectively is an important C programming skill.  This code could be made much simpler by doing an in-place reversal of the passed string and by using pointers:

char* reverse_string(char* string) {
    if (string == NULL) 
        return string;
    char *fwd = string;
    char *rev = &amp;string[strlen(string)-1];

    while (rev &gt; fwd) {
        char tmp = *rev;
        *rev-- = *fwd;
        *fwd++ = tmp;
    }
    return string;
}