Your code invokes undefined behavior because you pass the destination buffer as one of the source strings of your snprintf() format. This is not supported:
7.21.6.5 The snprintf function
Synopsis
#include <stdio.h>
int snprintf(char * restrict s, size_t n,
             const char * restrict format, ...);
Description
The snprintf function is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If n is zero, nothing is written, and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array. If copying takes place between objects that overlap, the behavior is undefined.
(emphasis mine).
The implementation of snprintf differs between Ubuntu (glibc) and OS/X (Apple libc, based on BSD sources). The behavior differs and cannot be relied upon as it is undefined in all cases.
You can implement safe_strcat() this way:
#include <string.h>
char *safe_strcat(char *dest, size_t size, const char *src) {
    char *p = memchr(dest, '\0', size);
    if (p != NULL) {
        strncat(p, src, size - (p - dest) - 1);
    }
    return dest;
}
Notes:
- do not call this function safe_strncat(), it is really a safe version ofstrcat().
- pass the size of the destination array after the destination pointer itself, not after the source pointer.
- returning the destination pointer does not allow the caller to detect truncation. You could instead return the length of the result if the destination array had been large enough, like snprintf(), you it would still not tell the caller if the destination was not null terminated before the call (forsafe_strcatandsafe_strncat).
You can use the same model for safe versions of strcpy, strncat and strncpy (but not implementing strncpy()'s counter-intuitive semantics):
char *safe_strcpy(char *dest, size_t size, const char *src) {
    if (size > 0) {
        *dest = '\0';
        strncat(dest, src, size - 1);
    }
    return dest;
}
char *safe_strncat(char *dest, size_t size, const char *src, size_t n) {
    char *p = memchr(dest, '\0', size);
    if (p != NULL) {
        if (n > size - (p - dest) - 1)
            n = size - (p - dest) - 1;
        strncat(p, src, n);
    }
    return dest;
}
char *safe_strncpy(char *dest, size_t size, const char *src, size_t n) {
    if (size > 0) {
        if (n > size - 1)
            n = size - 1;
        *dest = '\0';
        strncat(dest, src, n);
    }
    return dest;
}