From dfa0bc8d759d31e5daf4a0992ebab52b7b8487df Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 25 Apr 2025 14:55:39 -0500 Subject: [PATCH] Patch `uuid.get_node()` to use a different MAC address identification method --- cpython-unix/build-cpython.sh | 6 + .../patch-uuid-get-node-unstable.patch | 545 ++++++++++++++++++ 2 files changed, 551 insertions(+) create mode 100644 cpython-unix/patch-uuid-get-node-unstable.patch diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 68d0bde2..b2e6f9ab 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -155,6 +155,12 @@ else patch -p1 -i ${ROOT}/patch-macos-link-extension-modules.patch fi +# `libuuid` does not build with support for determining MAC addresses, which makes `uuid.get_node` +# unstable. The upstream has other methods to determine the MAC address, which we can force fallback +# to. +# See https://github.com/python/cpython/pull/132901 +patch -p1 -i ${ROOT}/patch-uuid-get-node-unstable.patch + # Also on macOS, the `python` executable is linked against libraries defined by statically # linked modules. But those libraries should only get linked into libpython, not the # executable. This behavior is kinda suspect on all platforms, as it could be adding diff --git a/cpython-unix/patch-uuid-get-node-unstable.patch b/cpython-unix/patch-uuid-get-node-unstable.patch new file mode 100644 index 00000000..599c8845 --- /dev/null +++ b/cpython-unix/patch-uuid-get-node-unstable.patch @@ -0,0 +1,545 @@ +diff --git a/Lib/uuid.py b/Lib/uuid.py +index 2c16c3f0f5a5b5..15def2d7db6432 100644 +--- a/Lib/uuid.py ++++ b/Lib/uuid.py +@@ -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 + +diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c +index c5e78b1510b5e3..c31a7e8fea5608 100644 +--- a/Modules/_uuidmodule.c ++++ b/Modules/_uuidmodule.c +@@ -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; + } + +diff --git a/configure b/configure +index decb8f2449d162..dd8ba9db2da686 100755 +--- a/configure ++++ b/configure +@@ -13978,6 +13978,7 @@ fi + + + ++ + have_uuid=missing + + for ac_header in uuid.h +@@ -13987,6 +13988,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"` +@@ -13996,7 +13998,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-""} + +@@ -14086,6 +14090,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; } +@@ -14183,8 +14188,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 + +@@ -14228,6 +14234,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; } +@@ -14325,8 +14332,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 + +@@ -14357,10 +14365,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 *or* . ++ # Since pkg-config --cflags uuid may return -I/usr/include/uuid, ++ # it's possible to write '#include ' in _uuidmodule.c, ++ # assuming that the compiler flags are properly updated. ++ # ++ # Ideally, we should have defined HAVE_UUID_H if and only if ++ # #include can be written, *without* assuming extra ++ # include path. ++ ac_cv_have_uuid_h=yes + + fi + +@@ -14381,6 +14395,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-""} + +@@ -14391,6 +14406,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. +@@ -14406,6 +14439,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 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 // PRIu64 ++ #include // uint64_t ++ #include // fopen(), fclose() ++ ++ #ifdef HAVE_UUID_H ++ #include ++ #else ++ #include ++ #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 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 // PRIu64 ++ #include // uint64_t ++ #include // fopen(), fclose() ++ ++ #ifdef HAVE_UUID_H ++ #include ++ #else ++ #include ++ #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 +diff --git a/configure.ac b/configure.ac +index 004797b5233c20..b26322c747ea96 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3710,15 +3710,17 @@ dnl check for uuid dependencies + AH_TEMPLATE([HAVE_UUID_H], [Define to 1 if you have the header file.]) + AH_TEMPLATE([HAVE_UUID_UUID_H], [Define to 1 if you have the 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-""} + ]) +@@ -3728,19 +3730,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 . ++ dnl and provides 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 *or* . ++ # Since pkg-config --cflags uuid may return -I/usr/include/uuid, ++ # it's possible to write '#include ' in _uuidmodule.c, ++ # assuming that the compiler flags are properly updated. ++ # ++ # Ideally, we should have defined HAVE_UUID_H if and only if ++ # #include 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"} +@@ -3755,12 +3767,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 +@@ -3770,6 +3789,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 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 // PRIu64 ++ #include // uint64_t ++ #include // fopen(), fclose() ++ ++ #ifdef HAVE_UUID_H ++ #include ++ #else ++ #include ++ #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 +diff --git a/pyconfig.h.in b/pyconfig.h.in +index aa086d49e90a5b..deb33500439de2 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -1572,6 +1572,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 header file. */ + #undef HAVE_UUID_H + \ No newline at end of file