Skip to content

bpo-38037: Fix reference counters in signal module #15701

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

Closed
wants to merge 6 commits into from
Closed

bpo-38037: Fix reference counters in signal module #15701

wants to merge 6 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Sep 5, 2019

Only 3.8 and 3.9 branches are affected.

https://bugs.python.org/issue38037

@brandtbucher brandtbucher added needs backport to 3.7 type-bug An unexpected behavior, bug, or error labels Sep 5, 2019
Copy link
Member

@brandtbucher brandtbucher left a comment

Choose a reason for hiding this comment

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

I'm not familiar with this file, but it looks like these Py_XINCREF calls should be made after the calls to PyModule_AddObject, correct?

If PyModule_AddObject fails, the additional reference isn't stolen... so it isn't needed.

@ghost
Copy link
Author

ghost commented Sep 6, 2019

I'm not familiar with this file, but it looks like these Py_XINCREF calls should be made after the calls to PyModule_AddObject, correct?

If PyModule_AddObject fails, the additional reference isn't stolen... so it isn't needed.

Search PyModule_AddObject in CPython code.
It seems always Py_INCREF first, then call PyModule_AddObject.

If Py_INCREF after calls, maybe the object is already deallocated inside PyModule_AddObject function?
https://github.com/python/cpython/blob/3.8/Python/modsupport.c#L654

@ghost
Copy link
Author

ghost commented Sep 6, 2019

No need needs backport to 3.7 label, only 3.8 and 3.9 branches are affected.

This is a regression caused by commit 9541bd3 (22 Apr 2019)

@brandtbucher
Copy link
Member

brandtbucher commented Sep 6, 2019

If Py_INCREF after calls, maybe the object is already deallocated inside PyModule_AddObject function?
https://github.com/python/cpython/blob/3.8/Python/modsupport.c#L654

Ah, yes. I at least think that we should Py_XDECREF in the failure condition (before goto finally;) though, right?

@brandtbucher
Copy link
Member

brandtbucher commented Sep 6, 2019

The failure looks like it's due to https://bugs.python.org/issue34037, unrelated. Closing and reopening the PR should trigger a rebuild!

@ghost
Copy link
Author

ghost commented Sep 6, 2019

Ah, yes. I at least think that we should Py_XDECREF in the failure condition (before goto finally;) though, right?

Search in CPython code, some sites don't check PyModule_AddObject's return value, some sites don't do Py_DECREF if PyModule_AddObject fails. Looks less normative.

I did all in the last commit.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

LGTM, but I have a few minor comments.

@nanjekyejoannah: Would you mind to also review this change? I would prefer to have a second reviewer.

@brandtbucher
Copy link
Member

brandtbucher commented Sep 6, 2019

@vstinner It looks like the idiom of calling PyModule_AddObject without Py_DECREF'ing on the error condition (or even checking for it at all) has spread quite a bit since https://bugs.python.org/issue26868 was first reported. I'll prepare a PR to fix the other call sites!

I don't want other contributors to think that this is the correct way to use it.

animalize and others added 2 commits September 7, 2019 10:05
@ghost
Copy link
Author

ghost commented Sep 7, 2019

Comments addressed.

@ghost ghost changed the title bpo-38037: Fix ref leaks in signal module bpo-38037: Fix reference counters in signal module Sep 7, 2019
@ghost
Copy link
Author

ghost commented Sep 7, 2019

If you don't mind, I want to reuse PyDict_SetItemString().
It keeps the code neat, and saves a pair of Py_INCREF()/Py_DECREF().

compare

    DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
    if (!DefaultHandler ||
        PyDict_SetItemString(d, "SIG_DFL", DefaultHandler) < 0) {
        goto finally;
    }

to

    DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
    if (!DefaultHandler) {
        goto finally;
    }
    Py_INCREF(DefaultHandler);
    if (PyModule_AddObject(m, "SIG_DFL", DefaultHandler)) {
        Py_DECREF(DefaultHandler);
        goto finally;
    }

Copy link
Contributor

@nanjekyejoannah nanjekyejoannah left a comment

Choose a reason for hiding this comment

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

If you don't mind, I want to reuse PyDict_SetItemString().
It keeps the code neat, and saves a pair of Py_INCREF()/Py_DECREF().

compare

    DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
    if (!DefaultHandler ||
        PyDict_SetItemString(d, "SIG_DFL", DefaultHandler) < 0) {
        goto finally;
    }

to

    DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
    if (!DefaultHandler) {
        goto finally;
    }
    Py_INCREF(DefaultHandler);
    if (PyModule_AddObject(m, "SIG_DFL", DefaultHandler)) {
        Py_DECREF(DefaultHandler);
        goto finally;
    }

Follow the discussion here : https://bugs.python.org/issue24011 on replacing PyDict_SetItemString() calls with PyModule_AddIntMacro() . By the time I made this change, all the PyModule_AddIntMacro() changes had been made save for this in this commit 6782b14

@nanjekyejoannah
Copy link
Contributor

Moving this to a comment : Follow the discussion here : https://bugs.python.org/issue24011 on replacing PyDict_SetItemString() calls with PyModule_AddIntMacro() . By the time I made this change, all the PyModule_AddIntMacro() changes had been made save for this in this commit 6782b14

@ghost
Copy link
Author

ghost commented Sep 9, 2019

I still prefer to use PyDict_SetItemString().

IMO it's suitable for this situation, PyModule_AddObject() is more suitable for adding an object that doesn't need to be held by other variable.

I have another problem.
In issue24011's signalmodule.patch by Christian Heimes, there is a change:

+  finally:
     if (PyErr_Occurred()) {
         Py_DECREF(m);
         m = NULL;
     }
 
-  finally:
     return m;
 }

But this change has not been adopted, is it intentional or negligent? @tiran

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

I don't think that PyModule_AddIntMacro() can be used in this PR. I like the removal of "x" variable, and reuse DefaultHandler, IgnoreHandler and ItimerError.

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

@vstinner It looks like the idiom of calling PyModule_AddObject without Py_DECREF'ing on the error condition (or even checking for it at all) has spread quite a bit since https://bugs.python.org/issue26868 was first reported. I'll prepare a PR to fix the other call sites! I don't want other contributors to think that this is the correct way to use it.

I suggest you to open a new issue at bugs.python.org for that. Before fixing the usage, I suggest to enhance the documentation. Maybe add a short code example:
https://docs.python.org/dev/c-api/module.html?highlight=pymodule_addobject#c.PyModule_AddObject

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

@nanjekyejoannah: PyModule_AddObject() has terrible API, it's easy to misuse it...

@nanjekyejoannah
Copy link
Contributor

@vstinner @animalize agreed.

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

@vstinner @animalize agreed.

If the PR looks good to you, would you mind to use GitHub UI to approve the PR?

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

@animalize: If you want to use PyDict_SetItemString(), please open a separated PR so I can compare the two approaches. Using PyDict_SetItemString() sounds like a good idea: internally, PyModule_AddObject calls PyDict_SetItemString, but in PyInit__signal() we already have have "d" (module dictionary).

@nanjekyejoannah
Copy link
Contributor

nanjekyejoannah commented Sep 9, 2019

In terms of fixing the current regression, this LGTM.

@ghost
Copy link
Author

ghost commented Sep 9, 2019

@animalize: If you want to use PyDict_SetItemString(), please open a separated PR so I can compare the two approaches. Using PyDict_SetItemString() sounds like a good idea: internally, PyModule_AddObject calls PyDict_SetItemString, but in PyInit__signal() we already have have "d" (module dictionary).

Please see this PR, it uses PyDict_SetItemString: #15753

@vstinner
Copy link
Member

vstinner commented Sep 9, 2019

I merged PR #15753 which is simpler than this PR.

@vstinner vstinner closed this Sep 9, 2019
@ghost ghost deleted the ref_leak branch September 9, 2019 14:23
@ghost
Copy link
Author

ghost commented Sep 9, 2019

Thanks for your reviews!

@brandtbucher
Copy link
Member

I suggest you to open a new issue at bugs.python.org for that. Before fixing the usage, I suggest to enhance the documentation. Maybe add a short code example:
https://docs.python.org/dev/c-api/module.html?highlight=pymodule_addobject#c.PyModule_AddObject

I'll open an issue about live code changes. I've already got a doc patch at #15725!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting merge type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants