From 67c3a720ba15582ea8b8d6aa97473cd7d7c02eed Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Sat, 20 Jan 2018 17:34:16 -0600 Subject: [PATCH 1/5] bpo-32502: Discard 64-bit (and other invalid) hardware addresses --- Lib/test/test_uuid.py | 20 +++++++++++++++++++ Lib/uuid.py | 17 ++++++++-------- .../2018-01-20-17-15-34.bpo-32502.OXJfn7.rst | 2 ++ 3 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-01-20-17-15-34.bpo-32502.OXJfn7.rst diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index f21bd6dfa15235..ac57f681c6f920 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -311,6 +311,26 @@ def test_getnode(self): node2 = self.uuid.getnode() self.assertEqual(node1, node2, '%012x != %012x' % (node1, node2)) + # bpo-32502: UUID1 requires a 48-bit identifier, but hardware identifiers + # need not necessarily be 48 bits (e.g., EUI-64). + def test_uuid1_eui64(self): + # Reset any cached node value + self.uuid._node = None + + # Confirm that uuid.getnode ignores hardware addresses larger than 48 + # bits + bad_getter = lambda: 1 << 48 + node = self.uuid.getnode(getters=[bad_getter]) + self.assertTrue(0 < node < (1 << 48), '%012x' % node) + + # Confirm that uuid1 doesn't fail when there's only a 64-bit hardware + # address + try: + self.uuid.uuid1(node=node) + except ValueError as e: + self.fail('uuid1 was given an invalid node ID') + + def test_uuid1(self): equal = self.assertEqual diff --git a/Lib/uuid.py b/Lib/uuid.py index b7433cb71926ac..7f8f4315c88cb1 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -656,7 +656,7 @@ def _random_getnode(): _node = None -def getnode(): +def getnode(*, getters=None): """Get the hardware address as a 48-bit positive integer. The first time this runs, it may launch a separate program, which could @@ -668,20 +668,21 @@ def getnode(): if _node is not None: return _node - if sys.platform == 'win32': - getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] - else: - getters = [_unix_getnode, _ifconfig_getnode, _ip_getnode, - _arp_getnode, _lanscan_getnode, _netstat_getnode] + if getters is None: + if sys.platform == 'win32': + getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] + else: + getters = [_unix_getnode, _ifconfig_getnode, _ip_getnode, + _arp_getnode, _lanscan_getnode, _netstat_getnode] for getter in getters + [_random_getnode]: try: _node = getter() except: continue - if _node is not None: + if (_node is not None) and (0 <= _node < (1 << 48)): return _node - assert False, '_random_getnode() returned None' + assert False, '_random_getnode() returned an invalid value' _last_timestamp = None diff --git a/Misc/NEWS.d/next/Library/2018-01-20-17-15-34.bpo-32502.OXJfn7.rst b/Misc/NEWS.d/next/Library/2018-01-20-17-15-34.bpo-32502.OXJfn7.rst new file mode 100644 index 00000000000000..8338632aa2c547 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-20-17-15-34.bpo-32502.OXJfn7.rst @@ -0,0 +1,2 @@ +uuid.uuid1 no longer raises an exception if a 64-bit hardware address is +encountered. From b5e1c70bc4cf7e3868b88758984318741e3a102b Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Tue, 23 Jan 2018 06:12:09 -0600 Subject: [PATCH 2/5] bpo-32502: Address review comments --- Lib/test/test_uuid.py | 22 +++++++++++++++------- Lib/uuid.py | 18 +++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index ac57f681c6f920..b18a65779d72bb 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -314,23 +314,31 @@ def test_getnode(self): # bpo-32502: UUID1 requires a 48-bit identifier, but hardware identifiers # need not necessarily be 48 bits (e.g., EUI-64). def test_uuid1_eui64(self): - # Reset any cached node value + # Reset any cached node value. self.uuid._node = None # Confirm that uuid.getnode ignores hardware addresses larger than 48 - # bits - bad_getter = lambda: 1 << 48 - node = self.uuid.getnode(getters=[bad_getter]) + # bits. Mock out each platform's *_getnode helper functions to return + # something just larger than 2^48 to test. This will cause uuid.getnode + # to fall back on uuid._random_getnode, which will generate a valid + # value. + too_large_getter = lambda: 1 << 48 + with unittest.mock.patch.multiple( + self.uuid, + _node_getters_win32=[too_large_getter], + _node_getters_unix=[too_large_getter], + ): + node = self.uuid.getnode() self.assertTrue(0 < node < (1 << 48), '%012x' % node) - # Confirm that uuid1 doesn't fail when there's only a 64-bit hardware - # address + # Confirm that uuid1 can use the generated node, i.e., the that + # uuid.getnode fell back on uuid._random_getnode() rather than using + # the value from too_large_getter above. try: self.uuid.uuid1(node=node) except ValueError as e: self.fail('uuid1 was given an invalid node ID') - def test_uuid1(self): equal = self.assertEqual diff --git a/Lib/uuid.py b/Lib/uuid.py index 7f8f4315c88cb1..bbed82edde7658 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -656,6 +656,11 @@ def _random_getnode(): _node = None +_node_getters_win32 = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] + +_node_getters_unix = [_unix_getnode, _ifconfig_getnode, _ip_getnode, + _arp_getnode, _lanscan_getnode, _netstat_getnode] + def getnode(*, getters=None): """Get the hardware address as a 48-bit positive integer. @@ -668,12 +673,10 @@ def getnode(*, getters=None): if _node is not None: return _node - if getters is None: - if sys.platform == 'win32': - getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] - else: - getters = [_unix_getnode, _ifconfig_getnode, _ip_getnode, - _arp_getnode, _lanscan_getnode, _netstat_getnode] + if sys.platform == 'win32': + getters = _node_getters_win32 + else: + getters = _node_getters_unix for getter in getters + [_random_getnode]: try: @@ -682,7 +685,8 @@ def getnode(*, getters=None): continue if (_node is not None) and (0 <= _node < (1 << 48)): return _node - assert False, '_random_getnode() returned an invalid value' + + assert False, '_random_getnode() returned invalid value: {}'.format(_node) _last_timestamp = None From 2c12989067b74d168c5dd2fbc7223741b0a2ae0f Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Tue, 23 Jan 2018 06:16:14 -0600 Subject: [PATCH 3/5] bpo-32502: Clarify comment in test --- Lib/test/test_uuid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index b18a65779d72bb..ad09f6950f755c 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -319,9 +319,9 @@ def test_uuid1_eui64(self): # Confirm that uuid.getnode ignores hardware addresses larger than 48 # bits. Mock out each platform's *_getnode helper functions to return - # something just larger than 2^48 to test. This will cause uuid.getnode - # to fall back on uuid._random_getnode, which will generate a valid - # value. + # something just larger than 48 bits to test. This will cause + # uuid.getnode to fall back on uuid._random_getnode, which will + # generate a valid value. too_large_getter = lambda: 1 << 48 with unittest.mock.patch.multiple( self.uuid, From 6e6d721485c3c2b4b3ad9fcaed9bc679668e699a Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Tue, 23 Jan 2018 18:06:23 -0600 Subject: [PATCH 4/5] bpo-32502: Address follow-up review comments --- Lib/test/test_uuid.py | 6 +++--- Lib/uuid.py | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index ad09f6950f755c..19d4fda2ecdd94 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -314,7 +314,6 @@ def test_getnode(self): # bpo-32502: UUID1 requires a 48-bit identifier, but hardware identifiers # need not necessarily be 48 bits (e.g., EUI-64). def test_uuid1_eui64(self): - # Reset any cached node value. self.uuid._node = None # Confirm that uuid.getnode ignores hardware addresses larger than 48 @@ -325,8 +324,9 @@ def test_uuid1_eui64(self): too_large_getter = lambda: 1 << 48 with unittest.mock.patch.multiple( self.uuid, - _node_getters_win32=[too_large_getter], - _node_getters_unix=[too_large_getter], + _node=None, # Ignore any cached node value. + _NODE_GETTERS_WIN32=[too_large_getter], + _NODE_GETTERS_UNIX=[too_large_getter], ): node = self.uuid.getnode() self.assertTrue(0 < node < (1 << 48), '%012x' % node) diff --git a/Lib/uuid.py b/Lib/uuid.py index bbed82edde7658..ef7b3b59241f4b 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -656,9 +656,9 @@ def _random_getnode(): _node = None -_node_getters_win32 = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] +_NODE_GETTERS_WIN32 = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] -_node_getters_unix = [_unix_getnode, _ifconfig_getnode, _ip_getnode, +_NODE_GETTERS_UNIX = [_unix_getnode, _ifconfig_getnode, _ip_getnode, _arp_getnode, _lanscan_getnode, _netstat_getnode] def getnode(*, getters=None): @@ -674,9 +674,9 @@ def getnode(*, getters=None): return _node if sys.platform == 'win32': - getters = _node_getters_win32 + getters = _NODE_GETTERS_WIN32 else: - getters = _node_getters_unix + getters = _NODE_GETTERS_UNIX for getter in getters + [_random_getnode]: try: @@ -685,7 +685,6 @@ def getnode(*, getters=None): continue if (_node is not None) and (0 <= _node < (1 << 48)): return _node - assert False, '_random_getnode() returned invalid value: {}'.format(_node) From d4b98dff3441c79fb7c2af9149b96f819d0c81ab Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Tue, 23 Jan 2018 18:07:44 -0600 Subject: [PATCH 5/5] bpo-32502: Actually remove the line --- Lib/test/test_uuid.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 19d4fda2ecdd94..7af1d7aec797c5 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -314,8 +314,6 @@ def test_getnode(self): # bpo-32502: UUID1 requires a 48-bit identifier, but hardware identifiers # need not necessarily be 48 bits (e.g., EUI-64). def test_uuid1_eui64(self): - self.uuid._node = None - # Confirm that uuid.getnode ignores hardware addresses larger than 48 # bits. Mock out each platform's *_getnode helper functions to return # something just larger than 48 bits to test. This will cause