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, this is then passed into xorbuf as the parameter EKY0 on line 6129.
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:
#ifndef WOLFSSL_DEBUG_ERRORS_ONLY
void WOLFSSL_MSG(const char* msg)
{
if (loggingEnabled)
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);
…
#else
…
#define WOLFSSL_MSG(m)
…
#endif
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++”
{{cta(’42fd5967-604d-43ad-9272-63d7c9d3b48f’)}}