Skip to content
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

Prevent segfault in PyKDL when asking for nonexisting segment of chain #228

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

DriesDM
Copy link
Member

@DriesDM DriesDM commented Mar 13, 2020

Added methodcode to the Chain class to throw an IndexError when trying to retrieve a segment that doesn't exist.

Copy link
Member

@meyerj meyerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for your contribution!

PyErr_SetString(PyExc_IndexError, "Chain index out of range");
return 0;
}
sipRes = &(sipCpp->getSegment(a0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I am not sure if this is valid in SIP. The /Factory/ annotation indicates that the returned pointer is owned by Python, but actually the returned pointer must not be deleted independently. I wonder whether that annotation was actually correct even for the original version of the method.

Why did you change the return type from const Segment& to const Segment*?

Could anybody who is more proficient with SIP could jump in (@smits)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not mean to change the return type, that must have slipped in while I was testing, good catch.

Copy link
Collaborator

@MatthijsBurgh MatthijsBurgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be implemented in pybind version

python_orocos_kdl/PyKDL/kinfam.sip Outdated Show resolved Hide resolved
@MatthijsBurgh MatthijsBurgh dismissed their stale review August 7, 2020 21:34

Added changes myself

@@ -175,8 +175,12 @@ void init_kinfam(pybind11::module &m)
chain.def("addChain", &Chain::addChain);
chain.def("getNrOfJoints", &Chain::getNrOfJoints);
chain.def("getNrOfSegments", &Chain::getNrOfSegments);
chain.def("getSegment", (Segment& (Chain::*)(unsigned int)) &Chain::getSegment);
chain.def("getSegment", (const Segment& (Chain::*)(unsigned int) const) &Chain::getSegment);
chain.def("getSegment", [](const Chain &c, unsigned int nr)
Copy link
Member

@meyerj meyerj Sep 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not so familiar with PyBind11, but this lambda function returns a const Segment & unconditionally, because the input is a const Chain &. Does it make a difference? Is the returned Segment supposed to have reference semantics or does const-correctness matter at all for the Python bindings?

The previous version had a const and a non-const overload, and the SIP version below only has a non-const implementation of getSegment(nr) and the const declaration has been removed.

From this document in the pybind11 repo I understand that the const-ness of returned values is even cast away by pybind11, and that the returned Segment instance is still mutable in Python, with reference semantics? In that case, would

Suggested change
chain.def("getSegment", [](const Chain &c, unsigned int nr)
chain.def("getSegment", [](Chain &c, unsigned int nr)

have worked here, too, to emphasize that the returned Segment is mutable and the Chain instance can be modified through this method?

@meyerj
Copy link
Member

meyerj commented Sep 7, 2020

It should be noted that this change is not consistent with the C++ version of getSegment(), which deliberately does not implement any checks on the index, probably for performance reasons. In C++ it is up to the caller to make sure that the given index is valid.

/**
* Request the nr'd segment of the chain. There is no boundary
* checking.
*
* @param nr the nr of the segment starting from 0
*
* @return a constant reference to the nr'd segment
*/
const Segment& getSegment(unsigned int nr)const;
/**
* Request the nr'd segment of the chain. There is no boundary
* checking.
*
* @param nr the nr of the segment starting from 0
*
* @return a reference to the nr'd segment
*/
Segment& getSegment(unsigned int nr);

@MatthijsBurgh
Copy link
Collaborator

@meyerj Do you think we should implement such checks for all index functions in PyKDL? I will work on a new PR as this one is outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants