gh-150101: Expose OpenSSL's error queue through SSLError.error_queue#150103
gh-150101: Expose OpenSSL's error queue through SSLError.error_queue#150103samatjain wants to merge 4 commits into
Conversation
Documentation build overview
|
| and may change between OpenSSL versions (i.e. do not rely on the exact | ||
| wording of these messages). | ||
|
|
||
| .. versionadded:: 3.16 |
There was a problem hiding this comment.
FYI we spell this "versionadded:: next" and the docs automation replaces it with the right version at the right time. (don't worry about it, 3.16 is correct)
|
I would rather not expose internal error queue and let better error messages. Mnemonics are not always up to date either |
| eq_msg, eq_filename, eq_func, eq_lineno | ||
| ); | ||
| } | ||
| if (PyList_Append(error_queue, current_eq_msg) != 0) { |
There was a problem hiding this comment.
decref current_eq_msg after this (regardless of success or failure) to avoid a memory leak.
| /* populate error_list from OpenSSL's error queue */ | ||
| unsigned int q_pos = 0; /* Error queue position */ | ||
| const char *eq_filename, *eq_func, *eq_data; | ||
| char eq_msg[256]; |
There was a problem hiding this comment.
I'd rather we did not put large things on the stack. Have it be a pointer set to NULL here. allocate space within the while loop if null before we use it. free it unconditionally when not NULL later on the ways out of the function.
|
|
||
| /* Presumably, we no longer need the OpenSSL error queue after this, so | ||
| we can call ERR_get_error (destructive) instead of ERR_peek_error */ | ||
| while ((openssl_errorcode = ERR_get_error_all(&eq_filename, &eq_lineno, &eq_func, |
There was a problem hiding this comment.
I think this is OpenSSL >= 3.x only - we also work with some 1.1.1-ish forks such as AWS-LC and can still build and link against 1.1.1 itself so conditional compilation is likely needed here.
| Py_DECREF(error_queue); | ||
|
|
||
| PyErr_SetObject(type, err_value); | ||
| fail: |
There was a problem hiding this comment.
fail: needs to Py_XDECREF(error_queue);
| if (q_pos == 0) { | ||
| /* errcode should have come from a caller, and should have been | ||
| returned from ERR_peek_last_error() */ | ||
| assert(openssl_errorcode == errcode); |
There was a problem hiding this comment.
is this correct? oldest first in one list vs value in errcode being the last one when there are multiple.
| if (state->str_verify_code == NULL) { | ||
| return -1; | ||
| } | ||
| state->str_error_queue = PyUnicode_InternFromString("error_queue"); |
There was a problem hiding this comment.
Add a relevant Py_CLEAR(state->...) for this.
| errstr = ERR_reason_error_string(errcode); | ||
| } | ||
|
|
||
| /* populate error_list from OpenSSL's error queue */ |
| self.assertEqual(cm.exception.library, 'PEM') | ||
| regex = "(NO_START_LINE|UNSUPPORTED_PUBLIC_KEY_TYPE)" | ||
| self.assertRegex(cm.exception.reason, regex) | ||
| self.assertTrue(len(cm.exception.error_queue) >= 1) |
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
See gh-150101 for background
In the associated PR, we drain the OpenSSL error queue and expose it via the error_queue attribute on the SSLError exception. With this modification to Python's SSL module, we can see what's in that queue:
With script:
We get that OpenSSL error queue that's helpful in debugging these kinds of problems:
It's worth noting, that on a stock Linux distribution (e.g. Ubuntu 24.04), the above script doesn't fail. Trying to attempt to raise SSLError in the limited ways I know how, e.g.
results in only 1 item in the OpenSSL error queue (see first line, you may need to scroll sideways):
i.e.
is new.
I can't think up of a way to raise an SSLError that results in a longer OpenSSL error queue that's significantly different than what was already in the original exception. As such, the unit tests are sort of superficial (and difficult to reliably improve, as the messages in the queue may change depending on the version of OpenSSL used). Even without a better test case, outputing what OpenSSL provides directly (e.g. we add the OpenSSL filenames and functions here) may help folks because those errors may be more Google-able.