Tomáš Zato
Tomáš Zato

Reputation: 53226

How to detect snprintf failure?

I am using snprintf to format string using user-defined format (also given as string). The code looks like this:

void DataPoint::valueReceived( QVariant value ) {
    // Get the formating QVariant, which is only considered valid if it's string
    QVariant format = this->property("format");
    if( format.isValid() && format.type()==QMetaType::QString && !format.isNull() ) {
        // Convert QString to std string
        const std::string formatStr = format.toString().toStdString();
        LOGMTRTTIINFO(pointName<<"="<<value.toString().toUtf8().constData()<<"=>"<<formatStr<<"["<<formatStr.length()<<'\n');
        // The attempt to catch exceptions caused by invalid formating string
        try {
            if( value.type() == QMetaType::QString ) {
                // Treat value as string (values are allways ASCII)
                const std::string array = value.toString().toStdString();
                const char* data = (char*)array.c_str();
                // Assume no more than 10 characters are added during formating.
                char* result = (char*)calloc(array.length()+10, sizeof(char));
                snprintf(result, array.length()+10, formatStr.c_str(), data);
                value = result;
            }
            // If not string, then it's a number.
            else {
                double data = value.toDouble();
                char* result = (char*)calloc(30, sizeof(char));
                // Even 15 characters is already longer than largest number you can make any sense of
                snprintf(result, 30, formatStr.c_str(), data);
                LOGMTRTTIINFO(pointName<<"="<<data<<"=>"<<formatStr<<"["<<formatStr.length()<<"]=>"<<result<<'\n');
                value = result;
            }
        } catch(...) {
            LOGMTRTTIERR("Format error in "<<pointName<<'\n');
        }
    }
    ui->value->setText(value.toString());
}

As you can see I assumed there will be some exception. But there's not, invalid formatting string results in gibberish. This is what I get if I try to format double using %s:

image description

So is there a way to detect that invalid formatting option was selected, such as formatting number as string or vice-versa? And what if totally invalid formatting string is given?

Upvotes: 2

Views: 3862

Answers (2)

Some programmer dude
Some programmer dude

Reputation: 409356

You ask if it's possible to detect format/argument mismatch at run-time, right? Then the short and only answer is no.

To expand on that "no" it's because Variable-argument functions (functions using the ellipsis ...) have no kind of type-safety. The compiler will convert some types of arguments to others (e.g. char or short will be converted to int, float will be converted to double), and if you use a literal string for the format some compilers will be able to parse the string and check the arguments you pass.

However since you pass a variable string, that can change at run-time, the compiler have no possibility for any kind of compile-time checking, and the function must trust that the format string passed is using the correct formatting for the arguments passed. If it's not then you have undefined behavior.


It should be noted that snprintf might not actually fail when being passed mismatching format specifier and argument value.

For example if using the %d format to print an int value, but then passing a double value, the snprintf would happily extract sizeof(int) bytes from the double value, and interpret it as an int value. The value printed will be quite unexpected, but there won't be a "failure" as such. Only undefined behavior (as mentioned above).

Thus it's not really possible to detect such errors or problems at all. At least not through the code. This is something that needs proper testing and code-review to catch.

Upvotes: 4

Idos
Idos

Reputation: 15320

What happens when snprintf fails?

When snprintf fails, POSIX requires that errno is set:

If an output error was encountered, these functions shall return a negative value and set errno to indicate the error.

Also you can find some relevant information regarding how to handle snprintf failures Here.

Upvotes: 2

Related Questions