8000 gh-123954: proposal for improving logic for setting SSL exceptions by picnixz · Pull Request #128391 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-123954: proposal for improving logic for setting SSL exceptions #128391

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

Draft
wants to merge 39 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
da3968c
introduce semantic types for SSL packed errors
picnixz Dec 31, 2024
704e6e3
correct error deduction in `fill_and_set_sslerror` and `_setSSLError`
picnixz Dec 31, 2024
864339a
pre-initialize default exception attributes
picnixz Dec 31, 2024
87a501d
extract logic for fetching library and reason
picnixz Dec 31, 2024
7cb16c0
extract logic for formatting error message
8000 picnixz Dec 31, 2024
4bd14a0
extract logic for building simple error message
picnixz Dec 31, 2024
c29f0f1
extract logic for building SSL verification exceptions
picnixz Dec 31, 2024
d8de992
upgrade logic for building SSL exceptions
picnixz Dec 31, 2024
ba1bf20
fix various issues
picnixz Dec 31, 2024
11ee17c
use `PyOS_snprintf` instead of `PyUnicode_FromFormat`
picnixz Jan 1, 2025
45c7a1e
fix Windows compilation
picnixz Jan 1, 2025
4362dae
Apply suggestions from code review
picnixz Jan 1, 2025
31ce5f0
Update Modules/_ssl.c
picnixz Jan 1, 2025
2fbcae2
post-merge
picnixz Jan 2, 2025
fd7d3ec
use `_Py_hashtable_t` for `err_codes_to_names`
picnixz Jan 3, 2025
7772113
use `_Py_hashtable_t` for `lib_codes_to_names`
picnixz Jan 3, 2025
4a2ed1d
post-merge
picnixz Jan 3, 2025
630c2b5
fix compilation?
picnixz Jan 3, 2025
98c8b99
attempt various things
picnixz Jan 3, 2025
133ec9f
try even more things!
picnixz Jan 3, 2025
fb5d800
:@
picnixz Jan 3, 2025
d6a0529
:@@@@@@@@@@@@@@
picnixz Jan 3, 2025
bb043b2
skip duplicated entries if they are correct
picnixz Jan 3, 2025
f836684
log more stuff
picnixz Jan 3, 2025
db3ed1c
change hash function key comparator?
picnixz Jan 4, 2025
7a198d4
revert macro
picnixz Jan 4, 2025
93a7a9b
full debug
picnixz Jan 4, 2025
95e1021
remove un-necessary specializations
picnixz Jan 4, 2025
b8f0cce
select when to use verbose SSL errors
picnixz Jan 5, 2025
689568f
reduce the number of format units
picnixz Jan 5, 2025
5583aa5
optimize `format_ssl_error_message`
picnixz Jan 5, 2025
a0ab1c2
temporarily patch CI
picnixz Jan 5, 2025
6b38769
fix assertions
picnixz Jan 5, 2025
3ecba48
now fix assertions correctly
picnixz Jan 5, 2025
d7f1ab1
use ImportWarning insteaad of RuntimeWarning
picnixz Jan 5, 2025
32f7cba
clear exception after emitting the warning
picnixz Jan 5, 2025
a88ec91
fix error path when formatting message
picnixz Jan 9, 2025
a9a6a50
Merge remote-tracking branch 'upstream/main' into perf/ssl/error-123954
picnixz Jan 11, 2025
68f8bc7
include hexadecimal error code value when reporting unknown error
picnixz Jan 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
include hexadecimal error code value when reporting unknown error
  • Loading branch information
picnixz committed Jan 11, 2025
commit 68f8bc7825175b4cced3d696174afdd054e16466
76 changes: 51 additions & 25 deletions Modules/_ssl.c 8000
Original file line number Diff line number Diff line change
Expand Up @@ -570,18 +570,29 @@ ssl_error_lineno_width(int lineno)
return 1 + (size_t)log10(lineno);
}

/* Dataclass used for constructing SSL errors. */
typedef struct ssl_error_info {
/* The SSL error number to pass to the exception constructor. */
int ssl_errno;
/*
* The SSL packed error code. If nonzero and no error message is
* given, this will be used to generate a default error message.
*/
py_ssl_errcode errcode;

const char *filename;
int lineno;
} ssl_error_info;

/*
* Construct a Unicode object containing the formatted SSL error message.
*
* The caller is responsible to pass ASCII-encoded objects.
*/
static PyObject *
format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
const char *errstr,
const char *filename, int lineno)
const char *errstr, const ssl_error_info info)
{
assert(errstr != NULL);

#define CHECK_OBJECT(x) \
do { \
assert(x == NULL || PyUnicode_CheckExact(x)); \
Expand All @@ -592,7 +603,20 @@ format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
CHECK_OBJECT(verify);
#undef CHECK_OBJECT

char tmp[32];
if (errstr == NULL && info.errcode != 0) {
/* at most 8 hexadecimal digits in info.errcode (unsigned long) */
if (snprintf(tmp, 32, "unknown error (0x%lx)", info.errcode) != -1) {
errstr = tmp;
}
}
if (errstr == NULL) {
errstr = "unknown error";
}

const char *filename = info.filename;
const size_t filename_len = ssl_error_filename_width(filename);
int lineno = info.lineno;
const size_t lineno_len = ssl_error_lineno_width(lineno);
/* exact length of "(_ssl.c:LINENO)" */
const size_t suffix_len = 1 + filename_len + 1 + lineno_len + 1;
Expand All @@ -601,6 +625,7 @@ format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
PyObject *res = NULL;
#define CSTRBUF(x) ((const char *)PyUnicode_DATA((x)))
#define CHARBUF(x) ((char *)PyUnicode_DATA((x)))

if (lib && reason && verify) {
/* [LIB: REASON] ERROR: VERIFY (_ssl.c:LINENO) */
const char *lib_cstr = CSTRBUF(lib);
Expand Down Expand Up @@ -679,21 +704,18 @@ format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
* Parameters
*
* exc_type The SSL exception type.
* ssl_errno The SSL error number to pass to the exception constructor.
* lib The ASCII-encoded library obtained from a packed error code.
* reason The ASCII-encoded reason obtained from a packed error code.
* errstr The non-NULL error message to use.
*
* A non-NULL library or reason is stored in the final exception object.
*/
static PyObject *
build_ssl_simple_error(_sslmodulestate *state, PyObject *exc_type, int ssl_errno,
build_ssl_simple_error(_sslmodulestate *state, PyObject *exc_type,
PyObject *lib, PyObject *reason, const char *errstr,
const char *filename, int lineno)
const ssl_error_info info)
{
/* build message */
PyObject *message = format_ssl_error_message(lib, reason, NULL, errstr,
filename, lineno);
PyObject *message = format_ssl_error_message(lib, reason, NULL, errstr, info);
if (message == NULL) {
return NULL;
}
Expand All @@ -703,6 +725,7 @@ build_ssl_simple_error(_sslmodulestate *state, PyObject *exc_type, int ssl_errno
* in the lowest bits of the error code and thus is compatible with
* the ERR_GET_REASON() macro.
*/
int ssl_errno = info.ssl_errno;
assert(ssl_errno == ERR_GET_REASON(ssl_errno));
PyObject *args = Py_BuildValue("iN", ssl_errno, message /* stolen */);
if (args == NULL) {
Expand Down Expand Up @@ -739,9 +762,9 @@ build_ssl_simple_error(_sslmodulestate *state, PyObject *exc_type, int ssl_errno
* Same as build_ssl_simple_error() but for SSL verification errors.
*/
static PyObject *
build_ssl_verify_error(_sslmodulestate *state, PySSLSocket *sslsock, int ssl_errno,
build_ssl_verify_error(_sslmodulestate *state, PySSLSocket *sslsock,
PyObject *lib, PyObject *reason, const char *errstr,
const char *filename, int lineno)
const ssl_error_info info)
{
assert(sslsock != NULL);
PyObject *exc = NULL, *exc_dict = NULL, *verify = NULL, *verify_code = NULL;
Expand Down Expand Up @@ -776,12 +799,13 @@ build_ssl_verify_error(_sslmodulestate *state, PySSLSocket *sslsock, int ssl_err
goto fail;
}
/* build message */
PyObject *message = format_ssl_error_message(lib, reason, verify,
errstr, filename, lineno);
PyObject *message = format_ssl_error_message(lib, reason, verify, errstr,
info);
if (message == NULL) {
goto fail;
}
/* see build_ssl_simple_error() for the assertion's rationale */
int ssl_errno = info.ssl_errno;
assert(ssl_errno == ERR_GET_REASON(ssl_errno));
PyObject *args = Py_BuildValue("iN", ssl_errno, message /* stolen */);
if (args == NULL) {
Expand Down Expand Up @@ -822,7 +846,8 @@ build_ssl_verify_error(_sslmodulestate *state, PySSLSocket *sslsock, int ssl_err

static void
fill_and_set_sslerror(_sslmodulestate *state,
PySSLSocket *sslsock, PyObject *exc_type,
PySSLSocket *sslsock, /* may be NULL */
PyObject *exc_type, /* socket exception type */
int ssl_errno /* passed to exc_type.__init__() */,
py_ssl_errcode errcode /* for a default message */,
const char *errstr /* may be NULL */,
Expand All @@ -833,22 +858,23 @@ fill_and_set_sslerror(_sslmodulestate *state,
ssl_error_fetch_lib_and_reason(state, errcode, &lib, &reason);
}
if (errstr == NULL && errcode) {
// When ERR_reason_error_string() returns NULL, build_ssl_*_error()
// will use a default error message containing the hexadecimal value
// of the unknown error code.
errstr = ERR_reason_error_string(errcode);
}
if (errstr == NULL) {
// ERR_reason_error_string() may return NULL
errstr = "unknown error";
}
const ssl_error_info info = {
.ssl_errno = ssl_errno,
.errcode = errcode,
.filename = filename,
.lineno = lineno
};
PyObject *exc;
if (sslsock != NULL && exc_type == state->PySSLCertVerificationErrorObject) {
exc = build_ssl_verify_error(state, sslsock, ssl_errno,
lib, reason, errstr,
filename, lineno);
exc = build_ssl_verify_error(state, sslsock, lib, reason, errstr, info);
}
else {
exc = build_ssl_simple_error(state, exc_type, ssl_errno,
lib, reason, errstr,
filename, lineno);
exc = build_ssl_simple_error(state, exc_type, lib, reason, errstr, info);
}
Py_XDECREF(reason);
Py_XDECREF(lib);
Expand Down
Loading
0