Can we make 1 == 2 true?
PythonCpythonPython InternalsPython Problem Overview
Python int
s are objects that encapsulate the actual number value. Can we mess with that value, for example setting the value of the object 1
to 2? So that 1 == 2
becomes True
?
Python Solutions
Solution 1 - Python
Yes, we can. But don't do this at home. Seriously, the 1
object is used in many places and I have no clue what this might break and what that might do to your computer. I reject all responsibility. But I found it interesting to learn about these things.
The id
function gives us the memory address and the ctypes
module lets us mess with memory:
import ctypes
ctypes.memmove(id(1) + 24, id(2) + 24, 4)
print(1 == 2)
x = 40
print(x + 1)
Output:
True
42
[Try it online!][3]. I tried it there because such sites have got to be protected from our hacking anyway.
More explanation / analysis:
The memmove
copied the value from the 2
object into the 1
object. Their size is 28 bytes each, but I skipped the first 24 bytes, because that's the object's reference count, type address, and value size, as we can view/verify as well:
import ctypes, struct, sys
x = 1
data = ctypes.string_at(id(x), 28)
ref_count, type_address, number_of_digits, lowest_digit = \
struct.unpack('qqqi', data)
print('reference count: ', ref_count, sys.getrefcount(x))
print('type address: ', type_address, id(type(x)))
print('number of digits:', number_of_digits, -(-x.bit_length() // 30))
print('lowest digit: ', lowest_digit, x % 2**30)
Output ([Try it online!][4]):
reference count: 135 138
type address: 140259718753696 140259718753696
number of digits: 1 1
lowest digit: 1 1
The reference count gets increased by the getrefcount
call, but I don't know why by 3. Anyway, ~134 things other than us reference the 1
object, and we're potentially messing all of them up, so... really don't try this at home.
The "digits" refer to how CPython stores int
s as [digits in base 230][5]. For example, x = 2 ** 3000
has 101 such digits. Output for x = 123 ** 456
for a better test:
reference count: 1 2
type address: 140078560107936 140078560107936
number of digits: 106 106
lowest digit: 970169057 970169057
[3]: https://tio.run/##NYoxDoAgEAT7e8WVEI0RpLDhNUoixcEFicHXnyRqNTOb5bseOS0rF5FInEvFrd4cToCXEwWifAUVd2U0DmjdiN3t704DcImpKoPeo@3Z0KObv7X1n9EiDw "Python 3.8 (pre-release) – Try It Online" [4]: https://tio.run/##bZDBbsMgEETvfMVeKuPIcdrkElnKn1RCBLCDmgCGtWp/vbuEpo2q@oIZ7cybJSx48e5wDHFd7S34iKBwCSY1kDBOCulcEmMznOCNaYmSfspESwPWDUIit5rPdQP7Y82i6YXykyNjnhJS62gSxbnpdjZR@F5oO1gk5eo/TcJypdR3BvQVaju5INUHr8ZxtFUDGVwzFgiIvCKGicYpA3dSBzTxxKXC7WCQlLtA1eqHM1eC70pdxlV/a9IqWcimH1epDr6HUr2r/ltny7dze7YorsYNeOE17HZweP2NKfuWjAf8@Q0amOEF9psNmdb1Cw "Python 3.8 (pre-release) – Try It Online" [5]: https://github.com/python/cpython/blob/247480a21cb165efdacc346a2d589dfc27e18283/Include/cpython/longintrepr.h#L11-L39
Solution 2 - Python
In Python 2, there's a much simpler approach - True
and False
aren't protected, so you could assign things to them.
>>> True = False
>>> (1 == 2) is True
True
Solution 3 - Python
If you'd prefer not to mess with the actual contents of cached int
or bool
objects, you can fake making 1 == 2
like so:
>>> import builtins
>>> import sys
>>>
>>> def displayhook(value):
... if value is False:
... value = True
... elif value is 1:
... value = 2
... text = repr(value)
... sys.stdout.write(text)
... sys.stdout.write('\n')
... builtins._ = value
...
<stdin>:4: SyntaxWarning: "is" with a literal. Did you mean "=="?
>>> sys.displayhook = displayhook
>>> 1
2
>>> 1 == 2
True