-
Notifications
You must be signed in to change notification settings - Fork 37
Fixed ExtractAddress causing Segmentation fault if it received None object. (#481) #482
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
Fixed ExtractAddress causing Segmentation fault if it received None object. (#481) #482
Conversation
I don't think this should raise unless |
I thought the same thing at first, but there is one thing that clearly breaks consistency, and that is when you override the hooked function. from entities.hooks import EntityCondition, EntityPreHook
@EntityPreHook(EntityCondition.is_player, 'give_named_item')
def pre_give_named_item(args):
return None # None cannot override the function, nor can it return NULL. And those who do not clearly know the following behavior will create unnecessary confusion, unable to understand why None returns NULL elsewhere, but not with a function override. Source.Python/src/core/modules/memory/memory_hooks.cpp Lines 129 to 132 in bcea09e
|
That's a callback, not a function. And that's by design, because When you call any functions exported from C++, Moreover, you are likely to get the same crash if |
I am not talking about the callback, I'm talking about the function that is expected to return DataType.POINTER, If NULL exists to serve as NULL, then everything should align with NULL. If not, then why does NULL exist?
|
It's by design. Calling a Python function that does not return anything, for example: def pre_give_named_item(args):
do_whatever_but_does_not_return_anything() Automatically return Regardless, there is a clear distinction between a function and a callback. When EDIT: As mentioned above, this crash: class Foo:
def _ptr(self):
return None
ptr.set_pointer(Foo()) # Crash This is inconsistent: ptr.set_string_pointer(None) # Work
ptr.set_pointer(None) # ValueError: None was passed, expected Pointer(0) or NULL. This is also inconsistent: player.give_named_item('weapon_awp', econ_item_view=None) # Work
@EntityPreHook(EntityCondition.is_player, 'give_named_item')
def pre_give_named_item(args):
args[3] = None # ValueError: None was passed, expected Pointer(0) or NULL. Etc. The fix for this should really be as simple as ensuring inline unsigned long ExtractAddress(object oPtr, bool bValidate = false)
{
CPointer* pPtr;
extract<CPointer *> extractor(oPtr);
if (!extractor.check())
{
oPtr = oPtr.attr(GET_PTR_NAME)();
pPtr = extract<CPointer *>(oPtr);
}
else
{
pPtr = extractor();
}
if (!pPtr)
{
if (bValidate)
BOOST_RAISE_EX...
else
return 0;
}
if (bValidate)
pPtr->Validate();
return pPtr->m_ulAddr;
} The same should also be done there: Source.Python/src/core/sp_python.cpp Lines 339 to 340 in 12a24f4
|
It is not by design, both are just compromises made due to the constraints imposed by The function arguments provided by make_object(Vector, None) # Crash!
Entity.create(None) # Crash!
ptr.set_string_array(None) # Crash! It should be considered an exception to the function provided by Boost.Python and should not get users into the habit of setting NULL with None. NULL should be used in situations where NULL(Pointer(0)) can be used. |
Oops, linked the wrong PR. I actually meant #481. For now, the fix should be sufficient. In the past we allowed None to be used instead of NULL for convenience. The main reason why NULL exists are pre hooks. As you know you can return a value unequal to None to prevent the actual function from executing. If you do so, the returned value is returned to the caller. If a function does not return anything in Python, None is returned implicitly. So, in the hook handler we check against None and allow the original function to be executed. But this is a problem if you really want to return a NULL pointer. That's why we added NULL, which can be used in these cases. I do agree that explicitly passing NULL instead of None would make it more safe. Then we also need to update our hook handler to receive a tuple containing two values: a hook action and the value that should be returned to the caller. Or maybe some classes like this: def my_hook1(args):
return HookAction.Continue() # or None
def my_hook2(args):
return HookAction.Override(my_value) |
#481