gh-132710: only use stable _uuid.generate_time_safe() to deduce MAC address (#132901)

This commit is contained in:
Bénédikt Tran 2025-05-26 10:56:31 +02:00 committed by GitHub
parent 29e8115964
commit 3bffada467
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 357 additions and 28 deletions

View file

@ -14,6 +14,7 @@ from unittest import mock
from test import support
from test.support import import_helper
from test.support.script_helper import assert_python_ok
py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
c_uuid = import_helper.import_fresh_module('uuid', fresh=['_uuid'])
@ -1217,10 +1218,37 @@ class BaseTestUUID:
class TestUUIDWithoutExtModule(BaseTestUUID, unittest.TestCase):
uuid = py_uuid
@unittest.skipUnless(c_uuid, 'requires the C _uuid module')
class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
uuid = c_uuid
def check_has_stable_libuuid_extractable_node(self):
if not self.uuid._has_stable_extractable_node:
self.skipTest("libuuid cannot deduce MAC address")
@unittest.skipUnless(os.name == 'posix', 'POSIX only')
def test_unix_getnode_from_libuuid(self):
self.check_has_stable_libuuid_extractable_node()
script = 'import uuid; print(uuid._unix_getnode())'
_, n_a, _ = assert_python_ok('-c', script)
_, n_b, _ = assert_python_ok('-c', script)
n_a, n_b = n_a.decode().strip(), n_b.decode().strip()
self.assertTrue(n_a.isdigit())
self.assertTrue(n_b.isdigit())
self.assertEqual(n_a, n_b)
@unittest.skipUnless(os.name == 'nt', 'Windows only')
def test_windows_getnode_from_libuuid(self):
self.check_has_stable_libuuid_extractable_node()
script = 'import uuid; print(uuid._windll_getnode())'
_, n_a, _ = assert_python_ok('-c', script)
_, n_b, _ = assert_python_ok('-c', script)
n_a, n_b = n_a.decode().strip(), n_b.decode().strip()
self.assertTrue(n_a.isdigit())
self.assertTrue(n_b.isdigit())
self.assertEqual(n_a, n_b)
class BaseTestInternals:
_uuid = py_uuid

View file

@ -633,22 +633,24 @@ def _netstat_getnode():
try:
import _uuid
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
_has_stable_extractable_node = _uuid.has_stable_extractable_node
_UuidCreate = getattr(_uuid, "UuidCreate", None)
except ImportError:
_uuid = None
_generate_time_safe = None
_has_stable_extractable_node = False
_UuidCreate = None
def _unix_getnode():
"""Get the hardware address on Unix using the _uuid extension module."""
if _generate_time_safe:
if _generate_time_safe and _has_stable_extractable_node:
uuid_time, _ = _generate_time_safe()
return UUID(bytes=uuid_time).node
def _windll_getnode():
"""Get the hardware address on Windows using the _uuid extension module."""
if _UuidCreate:
if _UuidCreate and _has_stable_extractable_node:
uuid_bytes = _UuidCreate()
return UUID(bytes_le=uuid_bytes).node

View file

@ -78,23 +78,47 @@ py_UuidCreate(PyObject *Py_UNUSED(context),
return NULL;
}
static int
py_windows_has_stable_node(void)
{
UUID uuid;
RPC_STATUS res;
Py_BEGIN_ALLOW_THREADS
res = UuidCreateSequential(&uuid);
Py_END_ALLOW_THREADS
return res == RPC_S_OK;
}
#endif /* MS_WINDOWS */
static int
uuid_exec(PyObject *module) {
uuid_exec(PyObject *module)
{
#define ADD_INT(NAME, VALUE) \
do { \
if (PyModule_AddIntConstant(module, (NAME), (VALUE)) < 0) { \
return -1; \
} \
} while (0)
assert(sizeof(uuid_t) == 16);
#if defined(MS_WINDOWS)
int has_uuid_generate_time_safe = 0;
ADD_INT("has_uuid_generate_time_safe", 0);
#elif defined(HAVE_UUID_GENERATE_TIME_SAFE)
int has_uuid_generate_time_safe = 1;
ADD_INT("has_uuid_generate_time_safe", 1);
#else
int has_uuid_generate_time_safe = 0;
ADD_INT("has_uuid_generate_time_safe", 0);
#endif
if (PyModule_AddIntConstant(module, "has_uuid_generate_time_safe",
has_uuid_generate_time_safe) < 0) {
return -1;
}
#if defined(MS_WINDOWS)
ADD_INT("has_stable_extractable_node", py_windows_has_stable_node());
#elif defined(HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC)
ADD_INT("has_stable_extractable_node", 1);
#else
ADD_INT("has_stable_extractable_node", 0);
#endif
#undef ADD_INT
return 0;
}

209
configure generated vendored
View file

@ -14052,6 +14052,7 @@ fi
have_uuid=missing
for ac_header in uuid.h
@ -14061,6 +14062,7 @@ if test "x$ac_cv_header_uuid_h" = xyes
then :
printf "%s\n" "#define HAVE_UUID_H 1" >>confdefs.h
for ac_func in uuid_create uuid_enc_be
do :
as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_sh"`
@ -14070,7 +14072,9 @@ then :
cat >>confdefs.h <<_ACEOF
#define `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1
_ACEOF
have_uuid=yes
have_uuid=yes
ac_cv_have_uuid_h=yes
LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""}
LIBUUID_LIBS=${LIBUUID_LIBS-""}
@ -14160,6 +14164,7 @@ if test "x$ac_cv_header_uuid_uuid_h" = xyes
then :
printf "%s\n" "#define HAVE_UUID_UUID_H 1" >>confdefs.h
ac_cv_have_uuid_uuid_h=yes
py_check_lib_save_LIBS=$LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate_time in -luuid" >&5
printf %s "checking for uuid_generate_time in -luuid... " >&6; }
@ -14257,8 +14262,9 @@ fi
printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time_safe" >&6; }
if test "x$ac_cv_lib_uuid_uuid_generate_time_safe" = xyes
then :
have_uuid=yes
printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE 1" >>confdefs.h
have_uuid=yes
ac_cv_have_uuid_generate_time_safe=yes
fi
@ -14302,6 +14308,7 @@ if test "x$ac_cv_header_uuid_uuid_h" = xyes
then :
printf "%s\n" "#define HAVE_UUID_UUID_H 1" >>confdefs.h
ac_cv_have_uuid_uuid_h=yes
py_check_lib_save_LIBS=$LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uuid_generate_time in -luuid" >&5
printf %s "checking for uuid_generate_time in -luuid... " >&6; }
@ -14399,8 +14406,9 @@ fi
printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time_safe" >&6; }
if test "x$ac_cv_lib_uuid_uuid_generate_time_safe" = xyes
then :
have_uuid=yes
printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE 1" >>confdefs.h
have_uuid=yes
ac_cv_have_uuid_generate_time_safe=yes
fi
@ -14431,10 +14439,16 @@ else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
have_uuid=yes
printf "%s\n" "#define HAVE_UUID_H 1" >>confdefs.h
printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE 1" >>confdefs.h
ac_cv_have_uuid_generate_time_safe=yes
# The uuid.h file to include may be <uuid.h> *or* <uuid/uuid.h>.
# Since pkg-config --cflags uuid may return -I/usr/include/uuid,
# it's possible to write '#include <uuid.h>' in _uuidmodule.c,
# assuming that the compiler flags are properly updated.
#
# Ideally, we should have defined HAVE_UUID_H if and only if
# #include <uuid.h> can be written, *without* assuming extra
# include path.
ac_cv_have_uuid_h=yes
fi
@ -14455,6 +14469,7 @@ if test "x$ac_cv_func_uuid_generate_time" = xyes
then :
have_uuid=yes
ac_cv_have_uuid_uuid_h=yes
LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""}
LIBUUID_LIBS=${LIBUUID_LIBS-""}
@ -14465,6 +14480,24 @@ fi
done
fi
if test "x$ac_cv_have_uuid_h" = xyes
then :
printf "%s\n" "#define HAVE_UUID_H 1" >>confdefs.h
fi
if test "x$ac_cv_have_uuid_uuid_h" = xyes
then :
printf "%s\n" "#define HAVE_UUID_UUID_H 1" >>confdefs.h
fi
if test "x$ac_cv_have_uuid_generate_time_safe" = xyes
then :
printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE 1" >>confdefs.h
fi
# gh-124228: While the libuuid library is available on NetBSD, it supports only UUID version 4.
@ -14480,6 +14513,164 @@ then :
have_uuid=no
fi
# gh-132710: The UUID node is fetched by using libuuid when possible
# and cached. While the node is constant within the same process,
# different interpreters may have different values as libuuid may
# randomize the node value if the latter cannot be deduced.
#
# Consumers may define HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC
# to indicate that libuuid is unstable and should not be relied
# upon to deduce the MAC address.
if test "$have_uuid" = "yes" -a "$HAVE_UUID_GENERATE_TIME_SAFE" = "1"
then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if uuid_generate_time_safe() node value is stable" >&5
printf %s "checking if uuid_generate_time_safe() node value is stable... " >&6; }
save_CFLAGS=$CFLAGS
save_CPPFLAGS=$CPPFLAGS
save_LDFLAGS=$LDFLAGS
save_LIBS=$LIBS
# Be sure to add the extra include path if we used pkg-config
# as HAVE_UUID_H may be set even though <uuid.h> is only reachable
# by adding extra -I flags.
#
# If the following script does not compile, we simply assume that
# libuuid is missing.
CFLAGS="$CFLAGS $LIBUUID_CFLAGS"
LIBS="$LIBS $LIBUUID_LIBS"
if test "$cross_compiling" = yes
then :
else case e in #(
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <inttypes.h> // PRIu64
#include <stdint.h> // uint64_t
#include <stdio.h> // fopen(), fclose()
#ifdef HAVE_UUID_H
#include <uuid.h>
#else
#include <uuid/uuid.h>
#endif
#define ERR 1
int main(void) {
uuid_t uuid; // unsigned char[16]
(void)uuid_generate_time_safe(uuid);
uint64_t node = 0;
for (size_t i = 0; i < 6; i++) {
node |= (uint64_t)uuid[15 - i] << (8 * i);
}
FILE *fp = fopen("conftest.out", "w");
if (fp == NULL) {
return ERR;
}
int rc = fprintf(fp, "%" PRIu64 "\n", node) >= 0;
rc |= fclose(fp);
return rc == 0 ? 0 : ERR;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"
then :
py_cv_uuid_node1=`cat conftest.out`
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
esac
fi
CFLAGS=$save_CFLAGS
CPPFLAGS=$save_CPPFLAGS
LDFLAGS=$save_LDFLAGS
LIBS=$save_LIBS
save_CFLAGS=$CFLAGS
save_CPPFLAGS=$CPPFLAGS
save_LDFLAGS=$LDFLAGS
save_LIBS=$LIBS
# Be sure to add the extra include path if we used pkg-config
# as HAVE_UUID_H may be set even though <uuid.h> is only reachable
# by adding extra -I flags.
#
# If the following script does not compile, we simply assume that
# libuuid is missing.
CFLAGS="$CFLAGS $LIBUUID_CFLAGS"
LIBS="$LIBS $LIBUUID_LIBS"
if test "$cross_compiling" = yes
then :
else case e in #(
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <inttypes.h> // PRIu64
#include <stdint.h> // uint64_t
#include <stdio.h> // fopen(), fclose()
#ifdef HAVE_UUID_H
#include <uuid.h>
#else
#include <uuid/uuid.h>
#endif
#define ERR 1
int main(void) {
uuid_t uuid; // unsigned char[16]
(void)uuid_generate_time_safe(uuid);
uint64_t node = 0;
for (size_t i = 0; i < 6; i++) {
node |= (uint64_t)uuid[15 - i] << (8 * i);
}
FILE *fp = fopen("conftest.out", "w");
if (fp == NULL) {
return ERR;
}
int rc = fprintf(fp, "%" PRIu64 "\n", node) >= 0;
rc |= fclose(fp);
return rc == 0 ? 0 : ERR;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"
then :
py_cv_uuid_node2=`cat conftest.out`
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
esac
fi
CFLAGS=$save_CFLAGS
CPPFLAGS=$save_CPPFLAGS
LDFLAGS=$save_LDFLAGS
LIBS=$save_LIBS
if test -n "$py_cv_uuid_node1" -a "$py_cv_uuid_node1" = "$py_cv_uuid_node2"
then
printf "%s\n" "#define HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: stable" >&5
printf "%s\n" "stable" >&6; }
else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unstable" >&5
printf "%s\n" "unstable" >&6; }
fi
fi
# 'Real Time' functions on Solaris
# posix4 on Solaris 2.6
# pthread (first!) on Linux

View file

@ -3740,15 +3740,17 @@ dnl check for uuid dependencies
AH_TEMPLATE([HAVE_UUID_H], [Define to 1 if you have the <uuid.h> header file.])
AH_TEMPLATE([HAVE_UUID_UUID_H], [Define to 1 if you have the <uuid/uuid.h> header file.])
AH_TEMPLATE([HAVE_UUID_GENERATE_TIME_SAFE], [Define if uuid_generate_time_safe() exists.])
AH_TEMPLATE([HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC], [Define if uuid_generate_time_safe() is able to deduce a MAC address.])
have_uuid=missing
dnl AIX provides support for RFC4122 (uuid) in libc.a starting with AIX 6.1
dnl (anno 2007). FreeBSD and OpenBSD provides support in libc as well.
dnl Little-endian FreeBSD, OpenBSD and NetBSD needs encoding into an octet
dnl stream in big-endian byte-order
AC_CHECK_HEADERS([uuid.h],
[AC_CHECK_FUNCS([uuid_create uuid_enc_be],
[have_uuid=yes
AC_CHECK_HEADERS([uuid.h], [
AC_CHECK_FUNCS([uuid_create uuid_enc_be], [
have_uuid=yes
ac_cv_have_uuid_h=yes
LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""}
LIBUUID_LIBS=${LIBUUID_LIBS-""}
])
@ -3758,19 +3760,29 @@ AS_VAR_IF([have_uuid], [missing], [
PKG_CHECK_MODULES(
[LIBUUID], [uuid >= 2.20],
[dnl linux-util's libuuid has uuid_generate_time_safe() since v2.20 (2011)
dnl and provides <uuid.h>.
dnl and provides <uuid.h> assuming specific include paths are given
have_uuid=yes
AC_DEFINE([HAVE_UUID_H], [1])
AC_DEFINE([HAVE_UUID_GENERATE_TIME_SAFE], [1])
ac_cv_have_uuid_generate_time_safe=yes
# The uuid.h file to include may be <uuid.h> *or* <uuid/uuid.h>.
# Since pkg-config --cflags uuid may return -I/usr/include/uuid,
# it's possible to write '#include <uuid.h>' in _uuidmodule.c,
# assuming that the compiler flags are properly updated.
#
# Ideally, we should have defined HAVE_UUID_H if and only if
# #include <uuid.h> can be written, *without* assuming extra
# include path.
ac_cv_have_uuid_h=yes
], [
WITH_SAVE_ENV([
CPPFLAGS="$CPPFLAGS $LIBUUID_CFLAGS"
LIBS="$LIBS $LIBUUID_LIBS"
AC_CHECK_HEADERS([uuid/uuid.h], [
ac_cv_have_uuid_uuid_h=yes
PY_CHECK_LIB([uuid], [uuid_generate_time], [have_uuid=yes])
PY_CHECK_LIB([uuid], [uuid_generate_time_safe],
[have_uuid=yes
AC_DEFINE([HAVE_UUID_GENERATE_TIME_SAFE], [1]) ]) ])
PY_CHECK_LIB([uuid], [uuid_generate_time_safe], [
have_uuid=yes
ac_cv_have_uuid_generate_time_safe=yes
])])
AS_VAR_IF([have_uuid], [yes], [
LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""}
LIBUUID_LIBS=${LIBUUID_LIBS-"-luuid"}
@ -3785,12 +3797,19 @@ AS_VAR_IF([have_uuid], [missing], [
AC_CHECK_HEADERS([uuid/uuid.h], [
AC_CHECK_FUNC([uuid_generate_time], [
have_uuid=yes
ac_cv_have_uuid_uuid_h=yes
LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""}
LIBUUID_LIBS=${LIBUUID_LIBS-""}
])
])
])
AS_VAR_IF([ac_cv_have_uuid_h], [yes], [AC_DEFINE([HAVE_UUID_H], [1])])
AS_VAR_IF([ac_cv_have_uuid_uuid_h], [yes], [AC_DEFINE([HAVE_UUID_UUID_H], [1])])
AS_VAR_IF([ac_cv_have_uuid_generate_time_safe], [yes], [
AC_DEFINE([HAVE_UUID_GENERATE_TIME_SAFE], [1])
])
# gh-124228: While the libuuid library is available on NetBSD, it supports only UUID version 4.
# This restriction inhibits the proper generation of time-based UUIDs.
if test "$ac_sys_system" = "NetBSD"; then
@ -3800,6 +3819,68 @@ fi
AS_VAR_IF([have_uuid], [missing], [have_uuid=no])
# gh-132710: The UUID node is fetched by using libuuid when possible
# and cached. While the node is constant within the same process,
# different interpreters may have different values as libuuid may
# randomize the node value if the latter cannot be deduced.
#
# Consumers may define HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC
# to indicate that libuuid is unstable and should not be relied
# upon to deduce the MAC address.
AC_DEFUN([PY_EXTRACT_UUID_GENERATE_TIME_SAFE_MAC], [WITH_SAVE_ENV([
# Be sure to add the extra include path if we used pkg-config
# as HAVE_UUID_H may be set even though <uuid.h> is only reachable
# by adding extra -I flags.
#
# If the following script does not compile, we simply assume that
# libuuid is missing.
CFLAGS="$CFLAGS $LIBUUID_CFLAGS"
LIBS="$LIBS $LIBUUID_LIBS"
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <inttypes.h> // PRIu64
#include <stdint.h> // uint64_t
#include <stdio.h> // fopen(), fclose()
#ifdef HAVE_UUID_H
#include <uuid.h>
#else
#include <uuid/uuid.h>
#endif
#define ERR 1
int main(void) {
uuid_t uuid; // unsigned char[16]
(void)uuid_generate_time_safe(uuid);
uint64_t node = 0;
for (size_t i = 0; i < 6; i++) {
node |= (uint64_t)uuid[15 - i] << (8 * i);
}
FILE *fp = fopen("conftest.out", "w");
if (fp == NULL) {
return ERR;
}
int rc = fprintf(fp, "%" PRIu64 "\n", node) >= 0;
rc |= fclose(fp);
return rc == 0 ? 0 : ERR;
}]])], [
AS_VAR_SET([$1], [`cat conftest.out`])
], [], []
)])])
if test "$have_uuid" = "yes" -a "$HAVE_UUID_GENERATE_TIME_SAFE" = "1"
then
AC_MSG_CHECKING([if uuid_generate_time_safe() node value is stable])
PY_EXTRACT_UUID_GENERATE_TIME_SAFE_MAC([py_cv_uuid_node1])
PY_EXTRACT_UUID_GENERATE_TIME_SAFE_MAC([py_cv_uuid_node2])
if test -n "$py_cv_uuid_node1" -a "$py_cv_uuid_node1" = "$py_cv_uuid_node2"
then
AC_DEFINE([HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC], [1])
AC_MSG_RESULT([stable])
else
AC_MSG_RESULT([unstable])
fi
fi
# 'Real Time' functions on Solaris
# posix4 on Solaris 2.6
# pthread (first!) on Linux

View file

@ -1584,6 +1584,9 @@
/* Define if uuid_generate_time_safe() exists. */
#undef HAVE_UUID_GENERATE_TIME_SAFE
/* Define if uuid_generate_time_safe() is able to deduce a MAC address. */
#undef HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC
/* Define to 1 if you have the <uuid.h> header file. */
#undef HAVE_UUID_H