Search code examples
cstring-formattingvariadic-functionstypechecking

Checking data behind pointer at runtime to avoid segfaults : (wchar_t *) vs. (char *)


I have a printf-like function that can handle %s (char *) and %ls (wchar_t *) conversions. Everything works fine if I pass the right argument for the right conv specifier.

But if I pass a char * to my function when it expects a wchar_t *, it may segfault (Null-terminating byte being located at the second byte of a wchar_t for instance). Since I access this argument through va_arg() I can't be sure of the type.

If I consider that this char array is always NUL-terminated, I could check byte after byte to correctly handle the NUL-terminating char and stop memory access after it. But then I wouldn't be able to handle wchar_t legit values like this :

0b XXXXXXXX XXXXXXXX 00000000 XXXXXXXX

I'm already using the __attribute__ printf GNU C extension. But I this function may be used by a python programm through ctypes, so format/type checking at compiling may not be enough.

It there a way to perform such checking at runtime in my C function?

(NB : "There is no such way" may be the answer, but I'm still asking in order to be completely sure.)


Solution

  • No, this is not possible.

    In a typical C implementation, the type system only exists as an aid at compile time.* At runtime, all you have is bytes of data, and there's no way to even tell a pointer from a number (besides educated guessing).

    Technically, you're not even allowed to va_arg(ap, const char*) and then examine memory if the original argument was not a char*, signed char*, unsigned char* or void*, or a const-related version of such a type. The type passed to va_arg is always required to be a compatible type. (One reason is there's no guarantee pointers to different types have the same size, layout, and meaning.)

    (*In C++ there's a bit more to the story, because data representing types is stored connected to polymorphic objects to make dynamic_cast and typeid work correctly, and associated with all exception objects to make catch blocks work correctly. But none of this is compatible with va_arg.)