GrammaTech CodeSonar Analysis of a bug in wolfSSLTweet
WolfSSL is a lightweight, portable, and embedded SSL/TLS library. Written in C it’s 20 times smaller than OpenSSL. The code is open source but wolfSSL is also available with commercial support. We decided to take a look at the code with CodeSonar to see if there are any bugs or vulnerabilities to be found. In short, we found an uninitialized variable bug that gets somewhat exacerbated by compiler options that disable diagnostics.
Here is part of the report from CodeSonar.
wc_AesEncrypt is normally expected to write data to
outBlock. In this calling context, the then passed into
xorbuf as the parameter mask. That is then dereferenced on line 192.
What CodeSonar is found is that there is a possible abnormal situation where
wc_AesEncrypt returns early without having written to that buffer. Reading uninitialized memory is undefined behavior, so this is clearly a potentially serious bug. However what CodeSonar also makes clear is that a helpful diagnostic is also suppressed due to the way the code is compiled.
Line 1410 above makes it clear that the intention was to send a warning message to the user but in this build, that won’t happen because
WOLFSSL_MSG() expands to a macro that does nothing. That line is useful in a debugging context, but that’s not helpful if this error were to trigger in the field.
Here’s the definition of
WOLFSSL_MSG in wolfssl/wolfcrypt/logging.c:
void WOLFSSL_MSG(const char* msg)
wolfssl_log(INFO_LOG , msg);
In this particular case, CodeSonar’s deep analysis has followed possible paths in the application, including error conditions, like in this example.
#if defined(DEBUG_WOLFSSL) && !defined(WOLFSSL_DEBUG_ERRORS_ONLY)
WOLFSSL_API void WOLFSSL_MSG(const char* msg);
Note the macro in the else part of the
#ifdef that causes
WOLFSSL_MSG to expand to nothing.
This illustrates two notable things about CodeSonar’s analysis. First, because it does a very precise parse of the code, it knows exactly how the preprocessor macros were expanded. Because complicated preprocessor directives can be very difficult to understand, this knowledge helps users see through the confusion easily.
Second, note that the path that leads to the read of the uninitialized memory is an error path. Such paths are not typically executed very often, and can be quite difficult to test, especially in combination, so there is a much higher probability that bugs are found along those paths. Consequently, these are the paths that attackers often try to exploit because it can give them a foothold that ultimately allows them to implement an exploit. This is a key strength of path-sensitive static analysis: in principle it is capable of exploring all paths of execution, so it is good at finding bugs in the corner cases.
Interested in learning more? Read our guide "Advanced Static Analysis for C/C++"