Skip to content

Commit 7447706

Browse files
committed
gh-127081: use re-entrant variants of get{proto,serv}by{name,port}
Add configure tests and defines for getservbyname_r, getservbyport_r, and getprotobyname_r. Use these if available, otherwise fallback to the thread-unsafe variants. Add a unit test to exercise getprotobyname, which is currently untested. TODO: - Are there any platforms which define the unsafe variants but not the re-entrant ones? If not we can simplify the #ifdef hell somewhat. - Do the re-entrant functions have the same signature on all platforms? - These changes follow the existing code's practice: allocate a fixed-size (and overly large) buffer, and don't properly handle the error case if it is too small. Should this be fixed? If so should existing code also be fixed?
1 parent 815061c commit 7447706

File tree

7 files changed

+180
-6
lines changed

7 files changed

+180
-6
lines changed

Lib/test/test_socket.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,10 @@ def testGetServBy(self):
12971297
self.assertRaises(OverflowError, socket.getservbyport, -1)
12981298
self.assertRaises(OverflowError, socket.getservbyport, 65536)
12991299

1300+
def testGetProtoByName(self):
1301+
self.assertEqual(socket.getprotobyname('tcp'), 6)
1302+
self.assertRaises(OSError, socket.getprotobyname, 'non-existent proto')
1303+
13001304
def testDefaultTimeout(self):
13011305
# Testing default timeout
13021306
# The default timeout should initially be None
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix thread safety issues with getservbyname, getservbyport, and
2+
getprotobyname.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix libc thread safety issues with :mod:`socket` by replacing
2+
``getservbyname``, ``getservbyport``, and ``getprotobyname`` with ``*_r``
3+
re-entrant versions.

Modules/socketmodule.c

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6282,7 +6282,7 @@ Return the true host name, a list of aliases, and a list of IP addresses,\n\
62826282
for a host. The host argument is a string giving a host name or IP number.");
62836283
#endif
62846284

6285-
#ifdef HAVE_GETSERVBYNAME
6285+
#if defined(HAVE_GETSERVBYNAME_R) || defined (HAVE_GETSERVBYNAME)
62866286
/* Python interface to getservbyname(name).
62876287
This only returns the port number, since the other info is already
62886288
known or not useful (like the list of aliases). */
@@ -6292,6 +6292,12 @@ static PyObject *
62926292
socket_getservbyname(PyObject *self, PyObject *args)
62936293
{
62946294
const char *name, *proto=NULL;
6295+
#ifdef HAVE_GETSERVBYNAME_R
6296+
struct servent entry;
6297+
/* TODO: The man page says 1024 is usually enough, start with that and
6298+
retry if insufficient? */
6299+
char buf[16384];
6300+
#endif
62956301
struct servent *sp;
62966302
if (!PyArg_ParseTuple(args, "s|s:getservbyname", &name, &proto))
62976303
return NULL;
@@ -6301,7 +6307,11 @@ socket_getservbyname(PyObject *self, PyObject *args)
63016307
}
63026308

63036309
Py_BEGIN_ALLOW_THREADS
6310+
#ifdef HAVE_GETSERVBYNAME_R
6311+
getservbyname_r(name, proto, &entry, buf, sizeof(buf), &sp);
6312+
#else
63046313
sp = getservbyname(name, proto);
6314+
#endif
63056315
Py_END_ALLOW_THREADS
63066316
if (sp == NULL) {
63076317
PyErr_SetString(PyExc_OSError, "service/proto not found");
@@ -6318,7 +6328,7 @@ The optional protocol name, if given, should be 'tcp' or 'udp',\n\
63186328
otherwise any protocol will match.");
63196329
#endif
63206330

6321-
#ifdef HAVE_GETSERVBYPORT
6331+
#if defined(HAVE_GETSERVBYPORT_R) || defined (HAVE_GETSERVBYPORT)
63226332
/* Python interface to getservbyport(port).
63236333
This only returns the service name, since the other info is already
63246334
known or not useful (like the list of aliases). */
@@ -6329,6 +6339,12 @@ socket_getservbyport(PyObject *self, PyObject *args)
63296339
{
63306340
int port;
63316341
const char *proto=NULL;
6342+
#ifdef HAVE_GETSERVBYPORT_R
6343+
struct servent entry;
6344+
/* TODO: The man page says 1024 is usually enough, start with that and
6345+
retry if insufficient? */
6346+
char buf[16384];
6347+
#endif
63326348
struct servent *sp;
63336349
if (!PyArg_ParseTuple(args, "i|s:getservbyport", &port, &proto))
63346350
return NULL;
@@ -6344,7 +6360,11 @@ socket_getservbyport(PyObject *self, PyObject *args)
63446360
}
63456361

63466362
Py_BEGIN_ALLOW_THREADS
6363+
#ifdef HAVE_GETSERVBYPORT_R
6364+
getservbyport_r(htons((short)port), proto, &entry, buf, sizeof(buf), &sp);
6365+
#else
63476366
sp = getservbyport(htons((short)port), proto);
6367+
#endif
63486368
Py_END_ALLOW_THREADS
63496369
if (sp == NULL) {
63506370
PyErr_SetString(PyExc_OSError, "port/proto not found");
@@ -6361,7 +6381,7 @@ The optional protocol name, if given, should be 'tcp' or 'udp',\n\
63616381
otherwise any protocol will match.");
63626382
#endif
63636383

6364-
#ifdef HAVE_GETPROTOBYNAME
6384+
#if defined(HAVE_GETPROTOBYNAME_R) || defined (HAVE_GETPROTOBYNAME)
63656385
/* Python interface to getprotobyname(name).
63666386
This only returns the protocol number, since the other info is
63676387
already known or not useful (like the list of aliases). */
@@ -6371,11 +6391,21 @@ static PyObject *
63716391
socket_getprotobyname(PyObject *self, PyObject *args)
63726392
{
63736393
const char *name;
6394+
#ifdef HAVE_GETPROTOBYNAME_R
6395+
struct protoent entry;
6396+
/* TODO: The man page says 1024 is usually enough, start with that and
6397+
retry if insufficient? */
6398+
char buf[16384];
6399+
#endif
63746400
struct protoent *sp;
63756401
if (!PyArg_ParseTuple(args, "s:getprotobyname", &name))
63766402
return NULL;
63776403
Py_BEGIN_ALLOW_THREADS
6404+
#ifdef HAVE_GETPROTOBYNAME_R
6405+
getprotobyname_r(name, &entry, buf, sizeof(buf), &sp);
6406+
#else
63786407
sp = getprotobyname(name);
6408+
#endif
63796409
Py_END_ALLOW_THREADS
63806410
if (sp == NULL) {
63816411
PyErr_SetString(PyExc_OSError, "protocol not found");
@@ -7428,15 +7458,15 @@ static PyMethodDef socket_methods[] = {
74287458
{"sethostname", socket_sethostname,
74297459
METH_VARARGS, sethostname_doc},
74307460
#endif
7431-
#ifdef HAVE_GETSERVBYNAME
7461+
#if defined(HAVE_GETSERVBYNAME_R) || defined (HAVE_GETSERVBYNAME)
74327462
{"getservbyname", socket_getservbyname,
74337463
METH_VARARGS, getservbyname_doc},
74347464
#endif
7435-
#ifdef HAVE_GETSERVBYPORT
7465+
#if defined(HAVE_GETSERVBYPORT_R) || defined (HAVE_GETSERVBYPORT)
74367466
{"getservbyport", socket_getservbyport,
74377467
METH_VARARGS, getservbyport_doc},
74387468
#endif
7439-
#ifdef HAVE_GETPROTOBYNAME
7469+
#if defined (HAVE_GETPROTOBYNAME_R) || defined (HAVE_GETPROTOBYNAME)
74407470
{"getprotobyname", socket_getprotobyname,
74417471
METH_VARARGS, getprotobyname_doc},
74427472
#endif

configure

Lines changed: 123 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5395,6 +5395,9 @@ PY_CHECK_NETDB_FUNC([getservbyport])
53955395
PY_CHECK_NETDB_FUNC([gethostbyname])
53965396
PY_CHECK_NETDB_FUNC([gethostbyaddr])
53975397
PY_CHECK_NETDB_FUNC([getprotobyname])
5398+
PY_CHECK_NETDB_FUNC([getservbyname_r])
5399+
PY_CHECK_NETDB_FUNC([getservbyport_r])
5400+
PY_CHECK_NETDB_FUNC([getprotobyname_r])
53985401

53995402
dnl PY_CHECK_SOCKET_FUNC(FUNCTION)
54005403
AC_DEFUN([PY_CHECK_SOCKET_FUNC], [PY_CHECK_FUNC([$1], [

pyconfig.h.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,9 @@
557557
/* Define if you have the 'getprotobyname' function. */
558558
#undef HAVE_GETPROTOBYNAME
559559

560+
/* Define if you have the 'getprotobyname_r' function. */
561+
#undef HAVE_GETPROTOBYNAME_R
562+
560563
/* Define to 1 if you have the 'getpwent' function. */
561564
#undef HAVE_GETPWENT
562565

@@ -587,9 +590,15 @@
587590
/* Define if you have the 'getservbyname' function. */
588591
#undef HAVE_GETSERVBYNAME
589592

593+
/* Define if you have the 'getservbyname_r' function. */
594+
#undef HAVE_GETSERVBYNAME_R
595+
590596
/* Define if you have the 'getservbyport' function. */
591597
#undef HAVE_GETSERVBYPORT
592598

599+
/* Define if you have the 'getservbyport_r' function. */
600+
#undef HAVE_GETSERVBYPORT_R
601+
593602
/* Define to 1 if you have the 'getsid' function. */
594603
#undef HAVE_GETSID
595604

0 commit comments

Comments
 (0)