Home > Net >  How to disallow literal string but not dynamic string pointers in C function arguments
How to disallow literal string but not dynamic string pointers in C function arguments

Time:10-29

This is my very first question here, I think. I wrote my C code trying to respect "-Wall -Wextra -Werror -ansi -pedantic", but I can be flexible.

I wrote a function with the following prototype :

int messagequeue_add(messagequeue_t *p_queue, char* p_msg, const size_t p_size)

I use "realloc(p_msg, XXX)" in this function. Thus, it is not supposed to receive a string literal pointer, but only a dynamic pointer. How to forbid string literal pointers and allow string dynamic pointers only, in the function prototype, if possible with standard C89 or C99

I already met functions disallowing literal strings in their prototype arguments (error/warning at compile time), but I can not remember where and how it was defined.

I tried to Google a lot, went to my whole paper-library, this question is too specific and it was impossible to find the needle in the haystack. I'm quite experienced, but I do need help, this time.

Full function code is :

int messagequeue_add(messagequeue_t *p_queue, char* p_msg, const size_t p_size) {
    messagequeueitem_t l_messagequeueitem;

    /* Parameters sanity checks for early return */
    if (!p_queue) {
        warnx("%s:%s():%d (%s@%s): p_queue can not be NULL",
              __FILE__, __func__, __LINE__, __DATE__, __TIME__);
        return -1;
    }
    if (!(*p_queue)) {
        warnx("%s:%s():%d (%s@%s): *p_queue can not be NULL",
              __FILE__, __func__, __LINE__, __DATE__, __TIME__);
        return -1;
    }
    if ((!p_msg)&&(p_size)) {
        warnx("%s:%s():%d (%s@%s): p_msg can not be NULL with p_size>0",
             __FILE__, __func__, __LINE__, __DATE__, __TIME__);
        return -1;
    }

    ASSERT(p_queue);
    ASSERT(*p_queue);
    ASSERT( ((NULL==(*p_queue)->head)&&(NULL==(*p_queue)->tail))
            ||((NULL!=(*p_queue)->head)&&(NULL!=(*p_queue)->tail)));
    ASSERT((p_msg)||((!p_msg)&&(!p_size)));

    /* Create messagequeueitem_t structure */
    if (NULL == ((l_messagequeueitem) = (messagequeueitem_t)malloc(sizeof(struct messagequeueitem_s)))) {
        warn("%s:%s():%d (%s@%s): malloc failed",
             __FILE__, __func__, __LINE__, __DATE__, __TIME__);
        return -1;
    }

    ASSERT(l_messagequeueitem);
    l_messagequeueitem->size = p_size;
    l_messagequeueitem->next=NULL;

    /* Do not create a copy of the message but take ownership of the allocated
     * contents. */

    /* Resize the memory allocation to fit in the case sizeof(p_msg) != p_size */
    DBG_PRINTF("p_msg=%p",p_msg);
    if (NULL == ( l_messagequeueitem->msg = realloc( p_msg, l_messagequeueitem->size))) {
        warn("%s:%s():%d (%s@%s): realloc failed",
                __FILE__, __func__, __LINE__, __DATE__, __TIME__);
        free(l_messagequeueitem);
        return -1;
    }

    if ((*p_queue)->tail)
        (*p_queue)->tail->next = l_messagequeueitem;
    else
        (*p_queue)->head = l_messagequeueitem;
    (*p_queue)->tail = l_messagequeueitem;

    return 0;
}

CodePudding user response:

I wrote a function with the following prototype :

int messagequeue_add(messagequeue_t *p_queue, char* p_msg, const size_t p_size)

I use "realloc(p_msg, XXX)" in this function. Thus, it is not supposed to receive a string literal pointer, but only a dynamic pointer. How to forbid string literal pointers and allow string dynamic pointers only, in the function prototype, if possible with standard C89 or C99

No version of standard C provides a mechanism to distinguish between pointers (in)to string literals and other pointers to characters. The elements of the array that a character string literal represents have type char, so pointers to them can have type char *, C does not specify the presence of any metadata by which these can be distinguished from other values of the same type.

This is usually dealt with via coding convention. Specifically, as a matter of code convention,

  1. a pointer to or into a string literal may be assigned only to variables and function prameters that are pointers to const data, and

  2. const correctness is rigorously observed (no casting away const or ditching it by other means).

That implies that where you want to accept pointers that may point to string literals, you use type const char *.

Adhering to such conventions precludes providing pointers to string literals to code that expects pointers to modifiable data.


Specific compilers may offer features that help. For example, many have ways to generate warnings and / or errors for violations of const correctness. Some have options specifically aimed at detecting attempts to modify string literals. For example, if you compile with GCC's and enable its static analyzer then it will detect (at least some) attempts to write to const data and string literals.

BUT

You have characterized your problem too narrowly. If you want to be able to realloc() the p_msg pointer passed to your function, then that pointer must have been previously obtained by dynamic allocation (and not since free()d). That is a much stronger requirement than that it not point to a string literal, so focusing on the string literal angle may not be so useful.

You should absolutely document this requirement, and you should propagate that requirement to the documentation of other functions as appropriate. But you're not likely to get a compiler assist like you could if the requirement were merely that the pointed-to data could be modified. C does not provide any value or data type property that describes data being modifiable but not de-/re-allocatable. There are, however, tools for detecting such issues at runtime. Valgrind can do that, for example.

  • Related