Hello,
part of the software I use to deal with in the company I work for is written in C and checked by SonarQube.
The logging system of this software is based on a batch of functions, targeting different logging levels and/or different logging domains. Some of these functions also make the software aborting (on purpose). Unfortunately, SonarQube seems to be not aware of this, and signals a lot of false issues in the code that comes after the call.
The following code snippet is an example of such kind of function that make our software aborting:
klib_DevelMessageFatalLog( "Assertion in %s at line %u", __FILE__, __LINE__) klib_DevelMessageFatalLog() is actually an alias (defined with #define) for calling another function named klib_gen_message() with its second argument set to 1. klib_gen_message(..., 1, ..., <message>) registers the message on the application log and then calls exit() for terminating the execution.
It seems that SonarQube is not aware that klib_DevelMessageFatalLog() will terminate the program (I can imagine that is not so simple to detect programatically), so I have been forced to add an abort() call immediately after every klib_DevelMessageFatalLog() for fixing some c:S2259 reported bugs.
We would like to instruct SonarQube to recognize klib_DevelMessageFatalLog(...) as a call that stops the execution and does not return the control, so no line of code after it has been called can be executed.
if ((SysDevices = g_object_new(DEV_TYPE_OBJECT, NULL)) == NULL) {
klib_UserMessageFatalLog("Cannot create one DEVObject instance. Giving up!");
}
SysDevices->poeRunnable = FALSE; // <--- SonarQube complains that SysDevices could be NULL here
SonarQube complains that SysDevices could be NULL since it is not aware that klib_UserMessageFatalLog() ends with an exit() (or abort()) call.
We worked around the issue in this way:
if ((SysDevices = g_object_new(DEV_TYPE_OBJECT, NULL)) == NULL) {
klib_UserMessageFatalLog("Cannot create one DEVObject instance. Giving up!");
abort();
}
SysDevices->poeRunnable = FALSE; // <--- SonarQube does not bother any more
But the abort() call we added is totally unnecessary.
This is an example, but we encountered this issue in many points of our code base.
Our CFamily analyzer is generally aware that abort() and exit() are both no-return. You can quickly check these things, if you like, by using Sonar on godbolt.org. Feel free to check out this small example at Compiler Explorer; you can comment-out the call to abort() and Sonar will correctly report an error message as the pointer variable might be null.
It seems that in your case, the analyzer is not able to fully see through the klib_DevelMessageFatalLog() macro construct. You mentioned that the construct eventually ends in a call to the klib_gen_message() function which potentially ends the program’s execution by calling abort() or exit().
I’m assuming that the implementation of implementation of klib_gen_message() is not available in the compilation units for which the analyzer reports the null pointer dereference. In that case, the analyzer “cannot see” the call to the no-return functions and hence conservatively assumes that SysDevices might be null. This would also explain why SonarQube no longer reports an issue if you manually put an abort() after using the klib_UserMessageFatalLog() function-like macro.
To avoid said reports, you need to make the code that calls to no-return functions available for analysis. You could either artificially add an abort() at the end of your function-like macro definition or make the implementation of klib_UserMessageFatalLog() available to the compilation unit under analysis, for instance.
Please let me know if you have any further questions.