-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SML: Segfault when accessing null pointer without error message #451
Comments
Sorry could you provide an example piece of code that triggers the segfault? |
Example Soar rule to generate the output link:
CPP SML code to read the output link. #include <iostream>
#include "sml_Client.h"
using namespace std;
using namespace sml;
void MyPrintEventHandler(smlPrintEventId id, void *pUserData, Agent *pAgent, char const *pMessage)
{
// In this case the user data is a string we're building up
std::string *pTrace = (std::string *)pUserData;
(*pTrace) += pMessage;
}
int main(int argc, char const *argv[])
{
Kernel *pKernel = Kernel::CreateKernelInNewThread();
sml::Agent *pAgent = pKernel->CreateAgent("test");
pAgent->LoadProductions("main.soar");
pAgent->RunSelf(1);
// Works as expected.
pAgent->GetOutputLink()->GetParameterValue("hello");
// This results in a segfault as expected, but no hint which read operation on the
// output link actually caused the segfault.
pAgent->GetOutputLink()->GetParameterValue("world");
string dummy;
cin >> dummy;
return 0;
} Potential update to improve the situation in #include <stdexcept>
...
char const* GetParameterValue(char const* pAttribute) const
{
WMElement* pWME = FindByAttribute(pAttribute, 0) ;
if (pWME == NULL){
std::string err_msg = "[Error]: " + this->m_AttributeName + " has no child attribute: " + std::string(pAttribute);
throw std::invalid_argument(err_msg);
}
return pWME->GetValueAsString();
} A more subtle change would be to replace the throw with a print command, e.g. |
Doing throws would be more semantic imo, and writing to cout could be dangerous/bad in some scenarios since the SML bindings can be embedded in other programs |
If this is actually desired I would be willing to create a PR. One important remark: this could be considered a breaking API change since checking for returned null pointers no longer works and would require replacing with a try-catch structure. Maybe someone with experience with SWIG could comment on the effects for the other language interfaces. |
There are also null pointer returns in WMElement conversions to identifier, string, int and float elements without error messages, which could also be updated in case the conversion is not implemented in inherited classes. Soar/Core/ClientSML/src/sml_ClientWMElement.h Lines 121 to 124 in 0c644c3
|
Okay, thanks for opening this! It does seem like a serious usability issue, so it would be nice to address it. Returning I agree with Jonathan, though: I think that throwing an exception would be the clearest way to handle this. We don't use We could add one try-catch test to each of the SML tests to demonstrate the behavior; we currently run Java, Python and Tcl tests in CI already, but haven't been able to add a C# one yet.
I don't follow. Existing code that checks for null would look like this, right? WMElement* pWME = FindByAttribute(pAttribute, 0) ;
if (pWME == NULL){
// handle here
}
const char* val = pWME->GetValueAsString();
... Wouldn't this continue to work fine? It doesn't call |
Yes, that could continue to work as long as the The follow up question would be if the Error Messages As far as error messages are concerned, I would expect to get something like this for the previous example: ERROR: output-link has no child attribute world. This includes information about the last valid WME as well as the child element that could not be found. What other information could be valuable? SWIG A quick search on SWIG and CPP exceptions:
|
Regarding your note on I've fixed the main issue described in the thread and will open a PR shortly. |
Previous behavior here was to segfault! An exception is much more user-friendly. Add the SWIG machinery for catching thrown exceptions in the SML binding libraries, too. We wrap every SML function with a try/catch. I don't know if this is actually heavy or not, but at least one implementation out there thought it was and went an alternative route where each method has to be explicitly marked to catch exceptions: https://github.com/KiCad/kicad-source-mirror/blob/47e4ebb32a1366d60649879381eac819f7c7131d/common/swig/ki_exception.i#L41 Add tests for TCL, Python and Java that demonstrate the exceptions being translated for each host language. We don't have C# tests yet T_T. Notice we did have to add the `atexit` handler back to prevent a segfault when the exception is not caught correctly; I don't know exactly why. Filed #464. Fixes #451.
It looks good to me. Thank you for implementing! |
This is not a bug, rather more a quality of live improvement suggestion
When reading the output link via SML (FindAttribute) and one attribute does not exist results in a returned null pointer. Reading the null pointer obviously results in a segfault.
One could check for null pointers every time but code readability suffers in my opinion while prototyping and debugging with external code could be associated with high efforts.
I would suggest throwing an error naming the parent I’d that is still valid and the invalid child parameter instead returning a null pointer.
While the Adaption in the cpp code is fairly simple, I don’t know the consequences for the SWIG interfaces.
Someone has an idea about it and can estimate if this is feasible?
The text was updated successfully, but these errors were encountered: