From 4a7c6d9f58421473d6f5d03fe331ba0c70a8824b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 11 Mar 2023 19:43:22 +0100 Subject: [PATCH 01/22] Establish global state --- Modules/socketmodule.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6a6f8cf7392e1c..6695741a67e65a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -541,7 +541,16 @@ remove_unusable_flags(PyObject *m) #define INADDR_NONE (-1) #endif +typedef struct { +} socket_state; + +static socket_state global_state; + +#define GLOBAL_STATE() (&global_state) + +#define clinic_state() GLOBAL_STATE() #include "clinic/socketmodule.c.h" +#undef clinic_state /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ @@ -7323,15 +7332,11 @@ PyDoc_STRVAR(socket_doc, See the socket module for documentation."); static struct PyModuleDef socketmodule = { - PyModuleDef_HEAD_INIT, - PySocket_MODULE_NAME, - socket_doc, - -1, - socket_methods, - NULL, - NULL, - NULL, - NULL + .m_base = PyModuleDef_HEAD_INIT, + .m_name = PySocket_MODULE_NAME, + .m_doc = socket_doc, + .m_size = sizeof(socket_state), + .m_methods = socket_methods, }; PyMODINIT_FUNC From 220a0aa56928e8b68d29d75f56fdbd97d5c9e868 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 09:39:46 +0100 Subject: [PATCH 02/22] Add sock_type to state --- Modules/socketmodule.c | 124 +++++++++----------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 2 files changed, 56 insertions(+), 69 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6695741a67e65a..35e741572d25d2 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -337,9 +337,9 @@ static FlagRuntimeInfo win_runtime_flags[] = { /*[clinic input] module _socket -class _socket.socket "PySocketSockObject *" "&sock_type" +class _socket.socket "PySocketSockObject *" "clinic_state()->sock_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7a8313d9b7f51988]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2db2489bd2219fd8]*/ static int remove_unusable_flags(PyObject *m) @@ -542,6 +542,10 @@ remove_unusable_flags(PyObject *m) #endif typedef struct { + /* The sock_type variable contains pointers to various functions, + some of which call new_sockobject(), which uses sock_type, so + there has to be a circular reference. */ + PyTypeObject *sock_type; } socket_state; static socket_state global_state; @@ -560,12 +564,6 @@ static socket_state global_state; static PyObject *socket_herror; static PyObject *socket_gaierror; -/* A forward reference to the socket type object. - The sock_type variable contains pointers to various functions, - some of which call new_sockobject(), which uses sock_type, so - there has to be a circular reference. */ -static PyTypeObject sock_type; - #if defined(HAVE_POLL_H) #include #elif defined(HAVE_SYS_POLL_H) @@ -1054,11 +1052,12 @@ init_sockobject(PySocketSockObject *s, static PySocketSockObject * new_sockobject(SOCKET_T fd, int family, int type, int proto) { - PySocketSockObject *s; - s = (PySocketSockObject *) - PyType_GenericNew(&sock_type, NULL, NULL); - if (s == NULL) + socket_state *state = GLOBAL_STATE(); + PyTypeObject *tp = state->sock_type; + PySocketSockObject *s = (PySocketSockObject *)tp->tp_alloc(tp, 0); + if (s == NULL) { return NULL; + } if (init_sockobject(s, fd, family, type, proto) == -1) { Py_DECREF(s); return NULL; @@ -5227,13 +5226,23 @@ sock_finalize(PySocketSockObject *s) PyErr_SetRaisedException(exc); } +static int +sock_traverse(PySocketSockObject *s, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(s)); + return 0; +} + static void sock_dealloc(PySocketSockObject *s) { - if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0) + PyTypeObject *tp = Py_TYPE(s); + if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0) { return; - - Py_TYPE(s)->tp_free((PyObject *)s); + } + PyObject_GC_UnTrack(s); + tp->tp_free((PyObject *)s); + Py_DECREF(tp); } @@ -5519,55 +5528,26 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, /* Type object for socket objects. */ -static PyTypeObject sock_type = { - PyVarObject_HEAD_INIT(0, 0) /* Must fill in type value later */ - "_socket.socket", /* tp_name */ - sizeof(PySocketSockObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)sock_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)sock_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - sock_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - sock_methods, /* tp_methods */ - sock_memberlist, /* tp_members */ - sock_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - sock_initobj, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - sock_new, /* tp_new */ - PyObject_Del, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - (destructor)sock_finalize, /* tp_finalize */ +static PyType_Slot sock_slots[] = { + {Py_tp_dealloc, sock_dealloc}, + {Py_tp_traverse, sock_traverse}, + {Py_tp_repr, sock_repr}, + {Py_tp_doc, (void *)sock_doc}, + {Py_tp_methods, sock_methods}, + {Py_tp_members, sock_memberlist}, + {Py_tp_getset, sock_getsetlist}, + {Py_tp_init, sock_initobj}, + {Py_tp_new, sock_new}, + {Py_tp_finalize, sock_finalize}, + {0, NULL}, +}; + +static PyType_Spec sock_spec = { + .name = "_socket.socket", + .basicsize = sizeof(PySocketSockObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = sock_slots, }; @@ -7309,7 +7289,8 @@ sock_get_api(void) return NULL; } - capi->Sock_Type = (PyTypeObject *)Py_NewRef(&sock_type); + socket_state *state = GLOBAL_STATE(); + capi->Sock_Type = (PyTypeObject *)Py_NewRef(state->sock_type); capi->error = Py_NewRef(PyExc_OSError); capi->timeout_error = Py_NewRef(PyExc_TimeoutError); return capi; @@ -7347,7 +7328,6 @@ PyInit__socket(void) if (!os_init()) return NULL; - Py_SET_TYPE(&sock_type, &PyType_Type); m = PyModule_Create(&socketmodule); if (m == NULL) return NULL; @@ -7365,10 +7345,18 @@ PyInit__socket(void) PyModule_AddObject(m, "gaierror", Py_NewRef(socket_gaierror)); PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError); - if (PyModule_AddObject(m, "SocketType", Py_NewRef(&sock_type)) != 0) + socket_state *state = GLOBAL_STATE(); + PyObject *sock_type = PyType_FromMetaclass(NULL, m, &sock_spec, NULL); + if (sock_type == NULL) { return NULL; - if (PyModule_AddObject(m, "socket", Py_NewRef(&sock_type)) != 0) + } + state->sock_type = (PyTypeObject *)sock_type; + if (PyModule_AddObjectRef(m, "SocketType", sock_type) < 0) { return NULL; + } + if (PyModule_AddObjectRef(m, "socket", sock_type) < 0) { + return NULL; + } #ifdef ENABLE_IPV6 has_ipv6 = Py_True; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 57b8542fb46482..e81f888550c602 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -397,7 +397,6 @@ Modules/_pickle.c - UnpicklerMemoProxyType - Modules/_pickle.c - Unpickler_Type - Modules/ossaudiodev.c - OSSAudioType - Modules/ossaudiodev.c - OSSMixerType - -Modules/socketmodule.c - sock_type - Modules/xxmodule.c - Null_Type - Modules/xxmodule.c - Str_Type - Modules/xxmodule.c - Xxo_Type - From 94ca00c5eec32d0ac08ffc94a6ec6e0f83bc8472 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 09:49:54 +0100 Subject: [PATCH 03/22] Add exceptions to state --- Modules/socketmodule.c | 49 +++++++++++++-------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 - 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 35e741572d25d2..7f5c821a05a1ca 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -546,6 +546,11 @@ typedef struct { some of which call new_sockobject(), which uses sock_type, so there has to be a circular reference. */ PyTypeObject *sock_type; + + /* Global variable holding the exception type for errors detected + by this module (but not argument type or memory errors, etc.). */ + PyObject *socket_herror; + PyObject *socket_gaierror; } socket_state; static socket_state global_state; @@ -559,11 +564,6 @@ static socket_state global_state; /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ -/* Global variable holding the exception type for errors detected - by this module (but not argument type or memory errors, etc.). */ -static PyObject *socket_herror; -static PyObject *socket_gaierror; - #if defined(HAVE_POLL_H) #include #elif defined(HAVE_SYS_POLL_H) @@ -658,7 +658,8 @@ set_herror(int h_error) v = Py_BuildValue("(is)", h_error, "host not found"); #endif if (v != NULL) { - PyErr_SetObject(socket_herror, v); + socket_state *state = GLOBAL_STATE(); + PyErr_SetObject(state->socket_herror, v); Py_DECREF(v); } @@ -685,7 +686,8 @@ set_gaierror(int error) v = Py_BuildValue("(is)", error, "getaddrinfo failed"); #endif if (v != NULL) { - PyErr_SetObject(socket_gaierror, v); + socket_state *state = GLOBAL_STATE(); + PyErr_SetObject(state->socket_gaierror, v); Py_DECREF(v); } @@ -7332,20 +7334,31 @@ PyInit__socket(void) if (m == NULL) return NULL; - PyModule_AddObject(m, "error", Py_NewRef(PyExc_OSError)); - socket_herror = PyErr_NewException("socket.herror", - PyExc_OSError, NULL); - if (socket_herror == NULL) + socket_state *state = GLOBAL_STATE(); + // Create exceptions and types, and add them to the module state. + state->socket_herror = PyErr_NewException("socket.herror", + PyExc_OSError, NULL); + if (state->socket_herror == NULL) { + return NULL; + } + if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) { + return NULL; + } + if (PyModule_AddObjectRef(m, "herror", state->socket_herror) < 0) { return NULL; - PyModule_AddObject(m, "herror", Py_NewRef(socket_herror)); - socket_gaierror = PyErr_NewException("socket.gaierror", PyExc_OSError, - NULL); - if (socket_gaierror == NULL) + } + state->socket_gaierror = PyErr_NewException("socket.gaierror", + PyExc_OSError, NULL); + if (state->socket_gaierror == NULL) { + return NULL; + } + if (PyModule_AddObjectRef(m, "gaierror", state->socket_gaierror) < 0) { return NULL; - PyModule_AddObject(m, "gaierror", Py_NewRef(socket_gaierror)); - PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError); + } + if (PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError) < 0) { + return NULL; + } - socket_state *state = GLOBAL_STATE(); PyObject *sock_type = PyType_FromMetaclass(NULL, m, &sock_spec, NULL); if (sock_type == NULL) { return NULL; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index e81f888550c602..f02d976ec5f1c7 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -420,8 +420,6 @@ Modules/_cursesmodule.c - PyCursesError - Modules/_decimal/_decimal.c - DecimalException - Modules/_tkinter.c - Tkinter_TclError - Modules/ossaudiodev.c - OSSAudioError - -Modules/socketmodule.c - socket_herror - -Modules/socketmodule.c - socket_gaierror - Modules/xxlimited_35.c - ErrorObject - Modules/xxmodule.c - ErrorObject - From fc9cdcef4fc4545f33d39d1d080f8d6b156d29b1 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 09:55:29 +0100 Subject: [PATCH 04/22] Add accept4_works to state --- Modules/socketmodule.c | 29 ++++++++++++++------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 7f5c821a05a1ca..8ec6f34970cde0 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -551,6 +551,13 @@ typedef struct { by this module (but not argument type or memory errors, etc.). */ PyObject *socket_herror; PyObject *socket_gaierror; + +#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ + int accept4_works; +#endif +#endif } socket_state; static socket_state global_state; @@ -2823,10 +2830,6 @@ struct sock_accept { }; #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) -/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ -static int accept4_works = -1; -#endif static int sock_accept_impl(PySocketSockObject *s, void *data) @@ -2845,15 +2848,16 @@ sock_accept_impl(PySocketSockObject *s, void *data) #endif #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - if (accept4_works != 0) { + socket_state *state = GLOBAL_STATE(); + if (state->accept4_works != 0) { ctx->result = accept4(s->sock_fd, addr, paddrlen, SOCK_CLOEXEC); - if (ctx->result == INVALID_SOCKET && accept4_works == -1) { + if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) { /* On Linux older than 2.6.28, accept4() fails with ENOSYS */ - accept4_works = (errno != ENOSYS); + state->accept4_works = (errno != ENOSYS); } } - if (accept4_works == 0) + if (state->accept4_works == 0) ctx->result = accept(s->sock_fd, addr, paddrlen); #else ctx->result = accept(s->sock_fd, addr, paddrlen); @@ -2906,7 +2910,8 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) #else #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - if (!accept4_works) + socket_state *state = GLOBAL_STATE(); + if (!state->accept4_works) #endif { if (_Py_set_inheritable(newfd, 0, NULL) < 0) { @@ -7335,6 +7340,12 @@ PyInit__socket(void) return NULL; socket_state *state = GLOBAL_STATE(); +#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + state->accept4_works = -1; +#endif +#endif + // Create exceptions and types, and add them to the module state. state->socket_herror = PyErr_NewException("socket.herror", PyExc_OSError, NULL); diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index f02d976ec5f1c7..be57e0bc3a2287 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -517,7 +517,6 @@ Modules/cjkcodecs/cjkcodecs.h - mapping_list - Modules/readline.c - libedit_append_replace_history_offset - Modules/readline.c - using_libedit_emulation - Modules/readline.c - libedit_history_start - -Modules/socketmodule.c - accept4_works - Modules/socketmodule.c - sock_cloexec_works - ##----------------------- From 29b4d1c20a50a79c6680cd6e01a6fcf55580aaa6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 10:02:15 +0100 Subject: [PATCH 05/22] Add sock_cloexec_works to state --- Modules/socketmodule.c | 40 ++++++++++++--------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 8ec6f34970cde0..b54a47b3cb6162 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -558,6 +558,12 @@ typedef struct { int accept4_works; #endif #endif + +#ifdef SOCK_CLOEXEC + /* socket() and socketpair() fail with EINVAL on Linux kernel older + * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ + int sock_cloexec_works; +#endif } socket_state; static socket_state global_state; @@ -5301,12 +5307,6 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Initialize a new socket object. */ -#ifdef SOCK_CLOEXEC -/* socket() and socketpair() fail with EINVAL on Linux kernel older - * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ -static int sock_cloexec_works = -1; -#endif - /*ARGSUSED*/ #ifndef HAVE_SOCKET @@ -5337,7 +5337,8 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, #ifndef MS_WINDOWS #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &sock_cloexec_works; + socket_state *state = GLOBAL_STATE(); + int *atomic_flag_works = &(state->sock_cloexec_works); #else int *atomic_flag_works = NULL; #endif @@ -5492,15 +5493,16 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, /* UNIX */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - if (sock_cloexec_works != 0) { + socket_state *state = GLOBAL_STATE(); + if (state->sock_cloexec_works != 0) { fd = socket(family, type | SOCK_CLOEXEC, proto); - if (sock_cloexec_works == -1) { + if (state->sock_cloexec_works == -1) { if (fd >= 0) { - sock_cloexec_works = 1; + state->sock_cloexec_works = 1; } else if (errno == EINVAL) { /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ - sock_cloexec_works = 0; + state->sock_cloexec_works = 0; fd = socket(family, type, proto); } } @@ -6217,7 +6219,8 @@ socket_socketpair(PyObject *self, PyObject *args) int family, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &sock_cloexec_works; + socket_state *state = GLOBAL_STATE(); + int *atomic_flag_works = &(state->sock_cloexec_works); #else int *atomic_flag_works = NULL; #endif @@ -6235,15 +6238,16 @@ socket_socketpair(PyObject *self, PyObject *args) /* Create a pair of socket fds */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - if (sock_cloexec_works != 0) { + socket_state *state = GLOBAL_STATE(); + if (state->sock_cloexec_works != 0) { ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); - if (sock_cloexec_works == -1) { + if (state->sock_cloexec_works == -1) { if (ret >= 0) { - sock_cloexec_works = 1; + state->sock_cloexec_works = 1; } else if (errno == EINVAL) { /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ - sock_cloexec_works = 0; + state->sock_cloexec_works = 0; ret = socketpair(family, type, proto, sv); } } @@ -7346,6 +7350,10 @@ PyInit__socket(void) #endif #endif +#ifdef SOCK_CLOEXEC + state->sock_cloexec_works = -1; +#endif + // Create exceptions and types, and add them to the module state. state->socket_herror = PyErr_NewException("socket.herror", PyExc_OSError, NULL); diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index be57e0bc3a2287..4e1d17edd6cd8d 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -517,7 +517,6 @@ Modules/cjkcodecs/cjkcodecs.h - mapping_list - Modules/readline.c - libedit_append_replace_history_offset - Modules/readline.c - using_libedit_emulation - Modules/readline.c - libedit_history_start - -Modules/socketmodule.c - sock_cloexec_works - ##----------------------- ## state From 93a2b621b80d22d027b2eb490ead6fdbf477910e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 10:07:10 +0100 Subject: [PATCH 06/22] Add defaulttimeout to state --- Modules/socketmodule.c | 21 +++++++++++++-------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index b54a47b3cb6162..c2aee09fb2bf32 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -552,6 +552,9 @@ typedef struct { PyObject *socket_herror; PyObject *socket_gaierror; + /* Default timeout for new sockets */ + _PyTime_t defaulttimeout; + #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ @@ -1013,9 +1016,6 @@ sock_call(PySocketSockObject *s, /* Initialize a new socket object. */ -/* Default timeout for new sockets */ -static _PyTime_t defaulttimeout = _PYTIME_FROMSECONDS(-1); - static int init_sockobject(PySocketSockObject *s, SOCKET_T fd, int family, int type, int proto) @@ -1047,8 +1047,9 @@ init_sockobject(PySocketSockObject *s, else #endif { - s->sock_timeout = defaulttimeout; - if (defaulttimeout >= 0) { + socket_state *state = GLOBAL_STATE(); + s->sock_timeout = state->defaulttimeout; + if (state->defaulttimeout >= 0) { if (internal_setblocking(s, 0) == -1) { return -1; } @@ -6882,11 +6883,12 @@ Get host and port for a sockaddr."); static PyObject * socket_getdefaulttimeout(PyObject *self, PyObject *Py_UNUSED(ignored)) { - if (defaulttimeout < 0) { + socket_state *state = GLOBAL_STATE(); + if (state->defaulttimeout < 0) { Py_RETURN_NONE; } else { - double seconds = _PyTime_AsSecondsDouble(defaulttimeout); + double seconds = _PyTime_AsSecondsDouble(state->defaulttimeout); return PyFloat_FromDouble(seconds); } } @@ -6906,7 +6908,8 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg) if (socket_parse_timeout(&timeout, arg) < 0) return NULL; - defaulttimeout = timeout; + socket_state *state = GLOBAL_STATE(); + state->defaulttimeout = timeout; Py_RETURN_NONE; } @@ -7344,6 +7347,8 @@ PyInit__socket(void) return NULL; socket_state *state = GLOBAL_STATE(); + state->defaulttimeout = _PYTIME_FROMSECONDS(-1); + #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) state->accept4_works = -1; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 4e1d17edd6cd8d..df50b4ebc389e8 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -543,4 +543,3 @@ Modules/readline.c - sigwinch_ohandler - Modules/readline.c - completed_input_string - Modules/rotatingtree.c - random_stream - Modules/rotatingtree.c - random_value - -Modules/socketmodule.c - defaulttimeout - From 4164f96eb1ed0819a7081d42700d8e069599cf82 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 10:14:18 +0100 Subject: [PATCH 07/22] Refactor: replace state query with param in set_herror() --- Modules/socketmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c2aee09fb2bf32..4e26f28e295114 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -664,7 +664,7 @@ set_error(void) #if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR) static PyObject * -set_herror(int h_error) +set_herror(socket_state *state, int h_error) { PyObject *v; @@ -674,7 +674,6 @@ set_herror(int h_error) v = Py_BuildValue("(is)", h_error, "host not found"); #endif if (v != NULL) { - socket_state *state = GLOBAL_STATE(); PyErr_SetObject(state->socket_herror, v); Py_DECREF(v); } @@ -5728,7 +5727,8 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) if (h == NULL) { /* Let's get real error message to return */ - set_herror(h_errno); + socket_state *state = GLOBAL_STATE(); + set_herror(state, h_errno); return NULL; } From dc740d1454b7ad358b75f16300b8e148bcfb3a75 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 11:04:26 +0100 Subject: [PATCH 08/22] Refactor: replace state query with param in set_gaierror() --- Modules/socketmodule.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 4e26f28e295114..cb3b3168165af9 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -685,7 +685,7 @@ set_herror(socket_state *state, int h_error) #ifdef HAVE_GETADDRINFO static PyObject * -set_gaierror(int error) +set_gaierror(socket_state *state, int error) { PyObject *v; @@ -701,7 +701,6 @@ set_gaierror(int error) v = Py_BuildValue("(is)", error, "getaddrinfo failed"); #endif if (v != NULL) { - socket_state *state = GLOBAL_STATE(); PyErr_SetObject(state->socket_gaierror, v); Py_DECREF(v); } @@ -1118,7 +1117,8 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int outcome of the first call. */ if (error) { res = NULL; // no-op, remind us that it is invalid; gh-100795 - set_gaierror(error); + socket_state *state = GLOBAL_STATE(); + set_gaierror(state, error); return -1; } switch (res->ai_family) { @@ -1229,7 +1229,8 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int Py_END_ALLOW_THREADS if (error) { res = NULL; // no-op, remind us that it is invalid; gh-100795 - set_gaierror(error); + socket_state *state = GLOBAL_STATE(); + set_gaierror(state, error); return -1; } if (res->ai_addrlen < addr_ret_size) @@ -6726,7 +6727,8 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) Py_END_ALLOW_THREADS if (error) { res0 = NULL; // gh-100795 - set_gaierror(error); + socket_state *state = GLOBAL_STATE(); + set_gaierror(state, error); goto err; } @@ -6825,7 +6827,8 @@ socket_getnameinfo(PyObject *self, PyObject *args) Py_END_ALLOW_THREADS if (error) { res = NULL; // gh-100795 - set_gaierror(error); + socket_state *state = GLOBAL_STATE(); + set_gaierror(state, error); goto fail; } if (res->ai_next) { @@ -6857,7 +6860,8 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getnameinfo(res->ai_addr, (socklen_t) res->ai_addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags); if (error) { - set_gaierror(error); + socket_state *state = GLOBAL_STATE(); + set_gaierror(state, error); goto fail; } From abf00c3a7004a8efc51477e2f1dde98608c0c385 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 11:15:35 +0100 Subject: [PATCH 09/22] Refactor: replace state query with param in new_sockobject() --- Modules/socketmodule.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index cb3b3168165af9..9084695ffb74fa 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1064,9 +1064,9 @@ init_sockobject(PySocketSockObject *s, in NEWOBJ()). */ static PySocketSockObject * -new_sockobject(SOCKET_T fd, int family, int type, int proto) +new_sockobject(socket_state *state, SOCKET_T fd, int family, int type, + int proto) { - socket_state *state = GLOBAL_STATE(); PyTypeObject *tp = state->sock_type; PySocketSockObject *s = (PySocketSockObject *)tp->tp_alloc(tp, 0); if (s == NULL) { @@ -6220,8 +6220,8 @@ socket_socketpair(PyObject *self, PyObject *args) SOCKET_T sv[2]; int family, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; -#ifdef SOCK_CLOEXEC socket_state *state = GLOBAL_STATE(); +#ifdef SOCK_CLOEXEC int *atomic_flag_works = &(state->sock_cloexec_works); #else int *atomic_flag_works = NULL; @@ -6240,7 +6240,6 @@ socket_socketpair(PyObject *self, PyObject *args) /* Create a pair of socket fds */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - socket_state *state = GLOBAL_STATE(); if (state->sock_cloexec_works != 0) { ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); if (state->sock_cloexec_works == -1) { @@ -6269,10 +6268,10 @@ socket_socketpair(PyObject *self, PyObject *args) if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0) goto finally; - s0 = new_sockobject(sv[0], family, type, proto); + s0 = new_sockobject(state, sv[0], family, type, proto); if (s0 == NULL) goto finally; - s1 = new_sockobject(sv[1], family, type, proto); + s1 = new_sockobject(state, sv[1], family, type, proto); if (s1 == NULL) goto finally; res = PyTuple_Pack(2, s0, s1); From 9c1c9c63543b740bbb8dea6c61882530de2c8265 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:11:42 +0100 Subject: [PATCH 10/22] Refactor: replace state query with param in init_sockobject() --- Modules/socketmodule.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9084695ffb74fa..9b744767975630 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1015,7 +1015,7 @@ sock_call(PySocketSockObject *s, /* Initialize a new socket object. */ static int -init_sockobject(PySocketSockObject *s, +init_sockobject(socket_state *state, PySocketSockObject *s, SOCKET_T fd, int family, int type, int proto) { s->sock_fd = fd; @@ -1045,7 +1045,6 @@ init_sockobject(PySocketSockObject *s, else #endif { - socket_state *state = GLOBAL_STATE(); s->sock_timeout = state->defaulttimeout; if (state->defaulttimeout >= 0) { if (internal_setblocking(s, 0) == -1) { @@ -1072,7 +1071,7 @@ new_sockobject(socket_state *state, SOCKET_T fd, int family, int type, if (s == NULL) { return NULL; } - if (init_sockobject(s, fd, family, type, proto) == -1) { + if (init_sockobject(state, s, fd, family, type, proto) == -1) { Py_DECREF(s); return NULL; } @@ -5335,10 +5334,10 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, { SOCKET_T fd = INVALID_SOCKET; + socket_state *state = GLOBAL_STATE(); #ifndef MS_WINDOWS #ifdef SOCK_CLOEXEC - socket_state *state = GLOBAL_STATE(); int *atomic_flag_works = &(state->sock_cloexec_works); #else int *atomic_flag_works = NULL; @@ -5494,7 +5493,6 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, /* UNIX */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - socket_state *state = GLOBAL_STATE(); if (state->sock_cloexec_works != 0) { fd = socket(family, type | SOCK_CLOEXEC, proto); if (state->sock_cloexec_works == -1) { @@ -5526,7 +5524,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, } #endif } - if (init_sockobject(self, fd, family, type, proto) == -1) { + if (init_sockobject(state, self, fd, family, type, proto) == -1) { SOCKETCLOSE(fd); return -1; } From 82c00c4e3e4cdf6f035ca72b2b61c4ab6e41e3b7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:15:07 +0100 Subject: [PATCH 11/22] Refactor: replace state query with param in gethost_common() --- Modules/socketmodule.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9b744767975630..1f059fd8404073 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5715,7 +5715,8 @@ sock_decode_hostname(const char *name) /* Convenience function common to gethostbyname_ex and gethostbyaddr */ static PyObject * -gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) +gethost_common(socket_state *state, struct hostent *h, struct sockaddr *addr, + size_t alen, int af) { char **pch; PyObject *rtn_tuple = (PyObject *)NULL; @@ -5726,7 +5727,6 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af) if (h == NULL) { /* Let's get real error message to return */ - socket_state *state = GLOBAL_STATE(); set_herror(state, h_errno); return NULL; } @@ -5900,8 +5900,9 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args) addr.ss_family. Therefore, we cast the sockaddr_storage into sockaddr to access sa_family. */ + socket_state *state = GLOBAL_STATE(); sa = SAS2SA(&addr); - ret = gethost_common(h, SAS2SA(&addr), sizeof(addr), + ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), sa->sa_family); #ifdef USE_GETHOSTBYNAME_LOCK PyThread_release_lock(netdb_lock); @@ -5999,7 +6000,8 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) h = gethostbyaddr(ap, al, af); #endif /* HAVE_GETHOSTBYNAME_R */ Py_END_ALLOW_THREADS - ret = gethost_common(h, SAS2SA(&addr), sizeof(addr), af); + socket_state *state = GLOBAL_STATE(); + ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), af); #ifdef USE_GETHOSTBYNAME_LOCK PyThread_release_lock(netdb_lock); #endif From 72e69e35ee0a8e31d15b20f8148fb6a8d8241b0b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:18:51 +0100 Subject: [PATCH 12/22] Refactor: replace state query with param in sock_get_api() --- Modules/socketmodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 1f059fd8404073..75f104ee90674a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7298,7 +7298,7 @@ sock_destroy_api(PyObject *capsule) } static PySocketModule_APIObject * -sock_get_api(void) +sock_get_api(socket_state *state) { PySocketModule_APIObject *capi = PyMem_Malloc(sizeof(PySocketModule_APIObject)); if (capi == NULL) { @@ -7306,7 +7306,6 @@ sock_get_api(void) return NULL; } - socket_state *state = GLOBAL_STATE(); capi->Sock_Type = (PyTypeObject *)Py_NewRef(state->sock_type); capi->error = Py_NewRef(PyExc_OSError); capi->timeout_error = Py_NewRef(PyExc_TimeoutError); @@ -7406,7 +7405,7 @@ PyInit__socket(void) PyModule_AddObject(m, "has_ipv6", Py_NewRef(has_ipv6)); /* Export C API */ - PySocketModule_APIObject *capi = sock_get_api(); + PySocketModule_APIObject *capi = sock_get_api(state); if (capi == NULL) { Py_DECREF(m); return NULL; From 274c412a9f6905b4da0a2f8108d6dffa8120f582 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:22:31 +0100 Subject: [PATCH 13/22] Refactor: replace state query with param in setipaddr() --- Modules/socketmodule.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 75f104ee90674a..5fab921b5b73d7 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1095,7 +1095,8 @@ static PyThread_type_lock netdb_lock; an error occurred; then an exception is raised. */ static int -setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) +setipaddr(socket_state *state, const char *name, struct sockaddr *addr_ret, + size_t addr_ret_size, int af) { struct addrinfo hints, *res; int error; @@ -1116,7 +1117,6 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int outcome of the first call. */ if (error) { res = NULL; // no-op, remind us that it is invalid; gh-100795 - socket_state *state = GLOBAL_STATE(); set_gaierror(state, error); return -1; } @@ -1228,7 +1228,6 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int Py_END_ALLOW_THREADS if (error) { res = NULL; // no-op, remind us that it is invalid; gh-100795 - socket_state *state = GLOBAL_STATE(); set_gaierror(state, error); return -1; } @@ -1747,6 +1746,7 @@ static int getsockaddrarg(PySocketSockObject *s, PyObject *args, sock_addr_t *addrbuf, int *len_ret, const char *caller) { + socket_state *state = GLOBAL_STATE(); switch (s->sock_family) { #if defined(AF_UNIX) @@ -1912,7 +1912,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 0; } struct sockaddr_in* addr = &addrbuf->in; - result = setipaddr(host.buf, (struct sockaddr *)addr, + result = setipaddr(state, host.buf, (struct sockaddr *)addr, sizeof(*addr), AF_INET); idna_cleanup(&host); if (result < 0) @@ -1957,7 +1957,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 0; } struct sockaddr_in6* addr = &addrbuf->in6; - result = setipaddr(host.buf, (struct sockaddr *)addr, + result = setipaddr(state, host.buf, (struct sockaddr *)addr, sizeof(*addr), AF_INET6); idna_cleanup(&host); if (result < 0) @@ -5683,8 +5683,12 @@ socket_gethostbyname(PyObject *self, PyObject *args) if (PySys_Audit("socket.gethostbyname", "O", args) < 0) { goto finally; } - if (setipaddr(name, (struct sockaddr *)&addrbuf, sizeof(addrbuf), AF_INET) < 0) + socket_state *state = GLOBAL_STATE(); + int rc = setipaddr(state, name, (struct sockaddr *)&addrbuf, + sizeof(addrbuf), AF_INET); + if (rc < 0) { goto finally; + } ret = make_ipv4_addr(&addrbuf); finally: PyMem_Free(name); @@ -5874,8 +5878,10 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args) if (PySys_Audit("socket.gethostbyname", "O", args) < 0) { goto finally; } - if (setipaddr(name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0) + socket_state *state = GLOBAL_STATE(); + if (setipaddr(state, name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0) { goto finally; + } Py_BEGIN_ALLOW_THREADS #ifdef HAVE_GETHOSTBYNAME_R #if defined(HAVE_GETHOSTBYNAME_R_6_ARG) @@ -5900,7 +5906,6 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args) addr.ss_family. Therefore, we cast the sockaddr_storage into sockaddr to access sa_family. */ - socket_state *state = GLOBAL_STATE(); sa = SAS2SA(&addr); ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), sa->sa_family); @@ -5958,8 +5963,10 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) goto finally; } af = AF_UNSPEC; - if (setipaddr(ip_num, sa, sizeof(addr), af) < 0) + socket_state *state = GLOBAL_STATE(); + if (setipaddr(state, ip_num, sa, sizeof(addr), af) < 0) { goto finally; + } af = sa->sa_family; ap = NULL; /* al = 0; */ @@ -6000,7 +6007,6 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) h = gethostbyaddr(ap, al, af); #endif /* HAVE_GETHOSTBYNAME_R */ Py_END_ALLOW_THREADS - socket_state *state = GLOBAL_STATE(); ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), af); #ifdef USE_GETHOSTBYNAME_LOCK PyThread_release_lock(netdb_lock); From d839af07afc2e43a8656e9d3482a180a35e09bad Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:28:02 +0100 Subject: [PATCH 14/22] Add state pointer to PySocketSockObject --- Modules/socketmodule.c | 3 ++- Modules/socketmodule.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 5fab921b5b73d7..8b785455c1efe3 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -541,7 +541,7 @@ remove_unusable_flags(PyObject *m) #define INADDR_NONE (-1) #endif -typedef struct { +typedef struct _socket_state { /* The sock_type variable contains pointers to various functions, some of which call new_sockobject(), which uses sock_type, so there has to be a circular reference. */ @@ -1052,6 +1052,7 @@ init_sockobject(socket_state *state, PySocketSockObject *s, } } } + s->state = state; return 0; } diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index f31ba532a6c60d..956cad2032425f 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -311,6 +311,8 @@ typedef union sock_addr { like the address family, which is used to decode socket address arguments properly. */ +struct _socket_state; // Forward decl. + typedef struct { PyObject_HEAD SOCKET_T sock_fd; /* Socket file descriptor */ @@ -322,6 +324,7 @@ typedef struct { sets a Python exception */ _PyTime_t sock_timeout; /* Operation timeout in seconds; 0.0 means non-blocking */ + struct _socket_state *state; } PySocketSockObject; /* --- C API ----------------------------------------------------*/ From 13d14daddddff1d1ceeeb7081088735de841c8af Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 14:31:57 +0100 Subject: [PATCH 15/22] Use sock pointer from sock type context where applicable --- Modules/socketmodule.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 8b785455c1efe3..6d6ce6c7b6553c 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1747,7 +1747,6 @@ static int getsockaddrarg(PySocketSockObject *s, PyObject *args, sock_addr_t *addrbuf, int *len_ret, const char *caller) { - socket_state *state = GLOBAL_STATE(); switch (s->sock_family) { #if defined(AF_UNIX) @@ -1913,7 +1912,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 0; } struct sockaddr_in* addr = &addrbuf->in; - result = setipaddr(state, host.buf, (struct sockaddr *)addr, + result = setipaddr(s->state, host.buf, (struct sockaddr *)addr, sizeof(*addr), AF_INET); idna_cleanup(&host); if (result < 0) @@ -1958,7 +1957,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 0; } struct sockaddr_in6* addr = &addrbuf->in6; - result = setipaddr(state, host.buf, (struct sockaddr *)addr, + result = setipaddr(s->state, host.buf, (struct sockaddr *)addr, sizeof(*addr), AF_INET6); idna_cleanup(&host); if (result < 0) @@ -2855,7 +2854,7 @@ sock_accept_impl(PySocketSockObject *s, void *data) #endif #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - socket_state *state = GLOBAL_STATE(); + socket_state *state = s->state; if (state->accept4_works != 0) { ctx->result = accept4(s->sock_fd, addr, paddrlen, SOCK_CLOEXEC); @@ -2917,7 +2916,7 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) #else #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - socket_state *state = GLOBAL_STATE(); + socket_state *state = s->state; if (!state->accept4_works) #endif { From b4edf64aaf1e1236ad35e5b44bc6ad14c296eb9c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 12 Mar 2023 17:49:46 +0100 Subject: [PATCH 16/22] Multi-phase init --- Modules/socketmodule.c | 149 +++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6d6ce6c7b6553c..ea1c393ee0593b 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -108,6 +108,7 @@ Local naming conventions: #define PY_SSIZE_T_CLEAN #include "Python.h" #include "pycore_fileutils.h" // _Py_set_inheritable() +#include "pycore_moduleobject.h" // _PyModule_GetState #include "structmember.h" // PyMemberDef #ifdef _Py_MEMORY_SANITIZER @@ -569,11 +570,25 @@ typedef struct _socket_state { #endif } socket_state; -static socket_state global_state; +static inline socket_state * +get_module_state(PyObject *mod) +{ + void *state = _PyModule_GetState(mod); + assert(state != NULL); + return (socket_state *)state; +} + +static struct PyModuleDef socketmodule; -#define GLOBAL_STATE() (&global_state) +static inline socket_state * +find_module_state_by_def(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &socketmodule); + assert(mod != NULL); + return get_module_state(mod); +} -#define clinic_state() GLOBAL_STATE() +#define clinic_state() (find_module_state_by_def(type)) #include "clinic/socketmodule.c.h" #undef clinic_state @@ -5334,7 +5349,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, { SOCKET_T fd = INVALID_SOCKET; - socket_state *state = GLOBAL_STATE(); + socket_state *state = find_module_state_by_def(Py_TYPE(self)); #ifndef MS_WINDOWS #ifdef SOCK_CLOEXEC @@ -5683,7 +5698,7 @@ socket_gethostbyname(PyObject *self, PyObject *args) if (PySys_Audit("socket.gethostbyname", "O", args) < 0) { goto finally; } - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); int rc = setipaddr(state, name, (struct sockaddr *)&addrbuf, sizeof(addrbuf), AF_INET); if (rc < 0) { @@ -5878,7 +5893,7 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args) if (PySys_Audit("socket.gethostbyname", "O", args) < 0) { goto finally; } - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); if (setipaddr(state, name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0) { goto finally; } @@ -5963,7 +5978,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) goto finally; } af = AF_UNSPEC; - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); if (setipaddr(state, ip_num, sa, sizeof(addr), af) < 0) { goto finally; } @@ -6226,7 +6241,7 @@ socket_socketpair(PyObject *self, PyObject *args) SOCKET_T sv[2]; int family, type = SOCK_STREAM, proto = 0; PyObject *res = NULL; - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); #ifdef SOCK_CLOEXEC int *atomic_flag_works = &(state->sock_cloexec_works); #else @@ -6732,7 +6747,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) Py_END_ALLOW_THREADS if (error) { res0 = NULL; // gh-100795 - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); set_gaierror(state, error); goto err; } @@ -6832,7 +6847,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) Py_END_ALLOW_THREADS if (error) { res = NULL; // gh-100795 - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); set_gaierror(state, error); goto fail; } @@ -6865,7 +6880,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getnameinfo(res->ai_addr, (socklen_t) res->ai_addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags); if (error) { - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); set_gaierror(state, error); goto fail; } @@ -6892,7 +6907,7 @@ Get host and port for a sockaddr."); static PyObject * socket_getdefaulttimeout(PyObject *self, PyObject *Py_UNUSED(ignored)) { - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); if (state->defaulttimeout < 0) { Py_RETURN_NONE; } @@ -6917,7 +6932,7 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg) if (socket_parse_timeout(&timeout, arg) < 0) return NULL; - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(self); state->defaulttimeout = timeout; Py_RETURN_NONE; @@ -7334,27 +7349,16 @@ PyDoc_STRVAR(socket_doc, \n\ See the socket module for documentation."); -static struct PyModuleDef socketmodule = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = PySocket_MODULE_NAME, - .m_doc = socket_doc, - .m_size = sizeof(socket_state), - .m_methods = socket_methods, -}; - -PyMODINIT_FUNC -PyInit__socket(void) +static int +socket_exec(PyObject *m) { - PyObject *m, *has_ipv6; - - if (!os_init()) - return NULL; + PyObject *has_ipv6; - m = PyModule_Create(&socketmodule); - if (m == NULL) - return NULL; + if (!os_init()) { + return -1; + } - socket_state *state = GLOBAL_STATE(); + socket_state *state = get_module_state(m); state->defaulttimeout = _PYTIME_FROMSECONDS(-1); #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) @@ -7371,36 +7375,36 @@ PyInit__socket(void) state->socket_herror = PyErr_NewException("socket.herror", PyExc_OSError, NULL); if (state->socket_herror == NULL) { - return NULL; + return -1; } if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) { - return NULL; + return -1; } if (PyModule_AddObjectRef(m, "herror", state->socket_herror) < 0) { - return NULL; + return -1; } state->socket_gaierror = PyErr_NewException("socket.gaierror", PyExc_OSError, NULL); if (state->socket_gaierror == NULL) { - return NULL; + return -1; } if (PyModule_AddObjectRef(m, "gaierror", state->socket_gaierror) < 0) { - return NULL; + return -1; } if (PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError) < 0) { - return NULL; + return -1; } PyObject *sock_type = PyType_FromMetaclass(NULL, m, &sock_spec, NULL); if (sock_type == NULL) { - return NULL; + return -1; } state->sock_type = (PyTypeObject *)sock_type; if (PyModule_AddObjectRef(m, "SocketType", sock_type) < 0) { - return NULL; + return -1; } if (PyModule_AddObjectRef(m, "socket", sock_type) < 0) { - return NULL; + return -1; } #ifdef ENABLE_IPV6 @@ -7413,21 +7417,18 @@ PyInit__socket(void) /* Export C API */ PySocketModule_APIObject *capi = sock_get_api(state); if (capi == NULL) { - Py_DECREF(m); - return NULL; + return -1; } PyObject *capsule = PyCapsule_New(capi, PySocket_CAPSULE_NAME, sock_destroy_api); if (capsule == NULL) { sock_free_api(capi); - Py_DECREF(m); - return NULL; + return -1; } if (PyModule_AddObject(m, PySocket_CAPI_NAME, capsule) < 0) { Py_DECREF(capsule); - Py_DECREF(m); - return NULL; + return -1; } /* Address families (we only support AF_INET and AF_UNIX) */ @@ -8782,7 +8783,7 @@ PyInit__socket(void) PyObject *tmp; tmp = PyLong_FromUnsignedLong(codes[i]); if (tmp == NULL) - return NULL; + return -1; PyModule_AddObject(m, names[i], tmp); } } @@ -8805,10 +8806,58 @@ PyInit__socket(void) #ifdef MS_WINDOWS /* remove some flags on older version Windows during run-time */ if (remove_unusable_flags(m) < 0) { - Py_DECREF(m); - return NULL; + return -1; } #endif - return m; + return 0; +} + +static struct PyModuleDef_Slot socket_slots[] = { + {Py_mod_exec, socket_exec}, + {0, NULL}, +}; + +static int +socket_traverse(PyObject *mod, visitproc visit, void *arg) +{ + socket_state *state = get_module_state(mod); + Py_VISIT(state->sock_type); + Py_VISIT(state->socket_herror); + Py_VISIT(state->socket_gaierror); + return 0; +} + +static int +socket_clear(PyObject *mod) +{ + socket_state *state = get_module_state(mod); + Py_CLEAR(state->sock_type); + Py_CLEAR(state->socket_herror); + Py_CLEAR(state->socket_gaierror); + return 0; +} + +static void +socket_free(void *mod) +{ + (void)socket_clear((PyObject *)mod); +} + +static struct PyModuleDef socketmodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = PySocket_MODULE_NAME, + .m_doc = socket_doc, + .m_size = sizeof(socket_state), + .m_methods = socket_methods, + .m_slots = socket_slots, + .m_traverse = socket_traverse, + .m_clear = socket_clear, + .m_free = socket_free, +}; + +PyMODINIT_FUNC +PyInit__socket(void) +{ + return PyModuleDef_Init(&socketmodule); } From c51a696ecab64d9df5ba114dc9fdd5ed464d0326 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 30 Mar 2023 12:32:46 +0200 Subject: [PATCH 17/22] Remove unneeded forward decl --- Modules/socketmodule.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 956cad2032425f..f5ca00450ee92a 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -311,8 +311,6 @@ typedef union sock_addr { like the address family, which is used to decode socket address arguments properly. */ -struct _socket_state; // Forward decl. - typedef struct { PyObject_HEAD SOCKET_T sock_fd; /* Socket file descriptor */ From d8e448f4c2ba063a8a30575e8f5a11f2e6a7df57 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 30 Mar 2023 12:53:10 +0200 Subject: [PATCH 18/22] Add test --- Lib/test/test_socket.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 32252f7b741fda..bb7bf436d2d721 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -8,6 +8,7 @@ import array import contextlib import errno +import gc import io import itertools import math @@ -836,6 +837,12 @@ def requireSocket(*args): class GeneralModuleTests(unittest.TestCase): + @unittest.skipUnless(_socket is not None, 'need _socket module') + def test_socket_type(self): + self.assertTrue(gc.is_tracked(_socket.socket)) + with self.assertRaisesRegex(TypeError, "immutable"): + _socket.socket.foo = 1 + def test_SocketType_is_socketobject(self): import _socket self.assertTrue(socket.SocketType is _socket.socket) From 4332571daf7b041ae074f939d811d2864faa95da Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 30 Mar 2023 13:05:32 +0200 Subject: [PATCH 19/22] fixup dealloc --- Modules/socketmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index ea1c393ee0593b..37cd13b5f73040 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5264,10 +5264,10 @@ sock_traverse(PySocketSockObject *s, visitproc visit, void *arg) static void sock_dealloc(PySocketSockObject *s) { - PyTypeObject *tp = Py_TYPE(s); if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0) { return; } + PyTypeObject *tp = Py_TYPE(s); PyObject_GC_UnTrack(s); tp->tp_free((PyObject *)s); Py_DECREF(tp); From 6a00c54a11467c9e2cb456ee04abe0c57e58123e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 3 Apr 2023 23:43:17 +0200 Subject: [PATCH 20/22] Add NEWS --- .../next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst diff --git a/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst new file mode 100644 index 00000000000000..e7586a223c1415 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst @@ -0,0 +1 @@ +Isolate :mod:`!_socket` (apply :pep:`687`). Patch by Erlend E. Aasland. From e5027c21dac2dda7cc2895b8ea84b551d3312dae Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 4 Apr 2023 13:54:26 +0200 Subject: [PATCH 21/22] Style: remove unneeded parens --- Modules/socketmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 37cd13b5f73040..567f2f9fb3d196 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5353,7 +5353,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, #ifndef MS_WINDOWS #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &(state->sock_cloexec_works); + int *atomic_flag_works = &state->sock_cloexec_works; #else int *atomic_flag_works = NULL; #endif @@ -6243,7 +6243,7 @@ socket_socketpair(PyObject *self, PyObject *args) PyObject *res = NULL; socket_state *state = get_module_state(self); #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &(state->sock_cloexec_works); + int *atomic_flag_works = &state->sock_cloexec_works; #else int *atomic_flag_works = NULL; #endif From 2019a93bd3c0b225cd5ba08d0b3b85d90010209d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 8 Apr 2023 21:39:21 +0200 Subject: [PATCH 22/22] Simplify ADD_EXC macro further --- Modules/socketmodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 81d829f3f0bfd2..656cd546d46d31 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7375,8 +7375,7 @@ socket_exec(PyObject *m) if (VAR == NULL) { \ goto error; \ } \ - int rc = PyModule_AddObjectRef(MOD, NAME, VAR); \ - if (rc < 0) { \ + if (PyModule_AddObjectRef(MOD, NAME, VAR) < 0) { \ goto error; \ } \ } while (0)