| 
 | 1 | +# CWE-1335: Incorrect Bitwise Shift of Integer  | 
 | 2 | + | 
 | 3 | +Ensure to know what bit-wise shift operators do in case you can not avoid them as recommended in *NUM01-J. Do not perform bitwise and arithmetic operations on the same data* [[SEI CERT JAVA 2024]](https://wiki.sei.cmu.edu/confluence/display/java/NUM01-J.+Do+not+perform+bitwise+and+arithmetic+operations+on+the+same+data) and use math instead.  | 
 | 4 | + | 
 | 5 | +A need to use bit-wise operations in Python can be due to translations or dealings with `C`, `C++` or `Java`, system libraries, raw binary data, or cryptographic algorithms.  Existing Python modules hooking into system `C` libraries for cryptographic functions or math all to avoid the need to implement bit-shifting on a Python level. Bit-shifting can have unexpected outcomes. Python's ctypes  module allows integration of `C` based system libraries into Python and direct access to C-type variables that can have different behavior than using high-level Python.  | 
 | 6 | + | 
 | 7 | +For the sake of simplicity, we only want to look at whole numbers and only understand the output of provided code examples illustrating Python's behavior.  | 
 | 8 | + | 
 | 9 | +## Bit-shifting Positive vs Negative Numbers  | 
 | 10 | + | 
 | 11 | +Python tries to avoid issues around signed numbers by storing the sign in a separate data field. Bit-shifting a whole negative number will always remain negative in Python. Positive whole numbers will remain positive in Python or zero. Shifting a positive whole number to the right will never go below zero and shifting a negative number to the left will never go above minus one. Shifting to the left behaves differently between positive and negative numbers.  | 
 | 12 | + | 
 | 13 | +[*example01.py:*](example01.py)  | 
 | 14 | + | 
 | 15 | +```py  | 
 | 16 | +# SPDX-FileCopyrightText: OpenSSF project contributors  | 
 | 17 | +# SPDX-License-Identifier: MIT  | 
 | 18 | +"""example code"""  | 
 | 19 | + | 
 | 20 | +positive_int: int = 1  | 
 | 21 | +print(f"+{positive_int} = ", end="")  | 
 | 22 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 23 | + | 
 | 24 | +negative_int: int = -1  | 
 | 25 | +print(f"{negative_int} = ", end="")  | 
 | 26 | +print(negative_int.to_bytes(negative_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 27 | +```  | 
 | 28 | + | 
 | 29 | +**Output of example01.py:**  | 
 | 30 | + | 
 | 31 | +```bash  | 
 | 32 | ++1 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'  | 
 | 33 | +-1 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'  | 
 | 34 | +```  | 
 | 35 | + | 
 | 36 | +Code `example01.py` shows that a `-1` is stored as hexadecimal `\xff`  or binary `11111111`s. Storing a decimal `-2`  ends with Hex `xFE` or binary `1110`. Storing decimal `+1` ends as binary `0001`. Knowing this allows understanding of some of the side effects.  | 
 | 37 | + | 
 | 38 | +A right shift `>>=`  on a positive number fills zeros from the left and a negative number would fill one's. A continuation of right shifts will reach a point where there is no more change in both cases.  | 
 | 39 | + | 
 | 40 | +This behaves differently for a left shift `<<=`  operation. As a positive number can reach zero it can remain at zero. A negative whole number can never reach zero and is therefore bound to stay at `-1`.  | 
 | 41 | + | 
 | 42 | +[*example02.py:*](example02.py)  | 
 | 43 | + | 
 | 44 | +```py  | 
 | 45 | +# SPDX-FileCopyrightText: OpenSSF project contributors  | 
 | 46 | +# SPDX-License-Identifier: MIT  | 
 | 47 | +"""example code"""  | 
 | 48 | + | 
 | 49 | +print("\nstay positive")  | 
 | 50 | +positive_int: int = 1  | 
 | 51 | +print(f"+{positive_int} = ", end="")  | 
 | 52 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 53 | + | 
 | 54 | +positive_int >>= 1  | 
 | 55 | +positive_int >>= 1  | 
 | 56 | +print(f"+{positive_int} = ", end="")  | 
 | 57 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 58 | + | 
 | 59 | +positive_int <<= 1  | 
 | 60 | +positive_int <<= 1  | 
 | 61 | +positive_int <<= 1000000000  | 
 | 62 | +print(f"+{positive_int} = ", end="")  | 
 | 63 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 64 | + | 
 | 65 | + | 
 | 66 | +print("\nstaying negative")  | 
 | 67 | +negative_int: int = -1  | 
 | 68 | +print(f"{negative_int} = ", end="")  | 
 | 69 | +print(negative_int.to_bytes(negative_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 70 | + | 
 | 71 | +negative_int >>= 1  | 
 | 72 | +negative_int >>= 1  | 
 | 73 | +negative_int >>= 1  | 
 | 74 | +print(f"{negative_int} = ", end="")  | 
 | 75 | +print(negative_int.to_bytes(negative_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 76 | + | 
 | 77 | +negative_int <<= 1  | 
 | 78 | +print(f"{negative_int} = ", end="")  | 
 | 79 | +print(negative_int.to_bytes(negative_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 80 | + | 
 | 81 | +negative_int <<= 1  | 
 | 82 | +print(f"{negative_int} = ", end="")  | 
 | 83 | +print(negative_int.to_bytes(negative_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 84 | + | 
 | 85 | +```  | 
 | 86 | + | 
 | 87 | +**Output of example02.py:**  | 
 | 88 | + | 
 | 89 | +```bash  | 
 | 90 | +stay positive  | 
 | 91 | ++1 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'  | 
 | 92 | ++0 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'  | 
 | 93 | ++0 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'  | 
 | 94 | + | 
 | 95 | +staying negative  | 
 | 96 | +-1 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'  | 
 | 97 | +-1 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'  | 
 | 98 | +-2 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe'  | 
 | 99 | +-4 = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc'  | 
 | 100 | +```  | 
 | 101 | + | 
 | 102 | +Code `example02.py` illustrates the different behavior of positive and negative whole number's during shifting. A continuation of shifting a positive whole number to the right and back to the left can remain at zero. This is not true for a negative whole number as they can only reach `-1`  and never zero. Moving a negative number back and forth can continue to change.  | 
 | 103 | + | 
 | 104 | +## Overflow Protection  | 
 | 105 | + | 
 | 106 | +Continuously shifting a Python `int`class variable will change the storage size while keeping the variable type as class int as demonstrated in the following code `example03.py`:  | 
 | 107 | + | 
 | 108 | +[*example03.py:*](example03.py)  | 
 | 109 | + | 
 | 110 | +```py  | 
 | 111 | +# SPDX-FileCopyrightText: OpenSSF project contributors  | 
 | 112 | +# SPDX-License-Identifier: MIT  | 
 | 113 | +"""example code"""  | 
 | 114 | + | 
 | 115 | +positive_int: int = 1  | 
 | 116 | +print(f"Before: {type(positive_int)}")  | 
 | 117 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 118 | +print("\nShifting 1000x\n")  | 
 | 119 | +positive_int <<= 1000  | 
 | 120 | +print(f"After: {type(positive_int)}")  | 
 | 121 | +print(positive_int.to_bytes(positive_int.__sizeof__(), byteorder="big", signed=True))  | 
 | 122 | +```  | 
 | 123 | + | 
 | 124 | +**Output of example03.py:**  | 
 | 125 | + | 
 | 126 | +```bash  | 
 | 127 | +Before: <class 'int'>  | 
 | 128 | +b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'  | 
 | 129 | + | 
 | 130 | +Shifting 1000x  | 
 | 131 | + | 
 | 132 | +After: <class 'int'>  | 
 | 133 | +b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'  | 
 | 134 | +```  | 
 | 135 | + | 
 | 136 | +Code `example03.py` demonstrates how Python is changing required storage in the background while keeping the class of type `int`. While this does prevent overflow in Python it can have some unexpected issues interacting with other languages. A mistake in a loop can cause Python to just continue to eat up memory while finding a natural ending in other languages.  | 
 | 137 | + | 
 | 138 | +## Non-Compliant Code Example  | 
 | 139 | + | 
 | 140 | +Method `shift_right` in `noncompliant01.py` can reach zero for positive numbers but loops forever on negative inputs.  | 
 | 141 | + | 
 | 142 | +*[noncompliant01.py](noncompliant01.py):*  | 
 | 143 | + | 
 | 144 | +```python  | 
 | 145 | +# SPDX-FileCopyrightText: OpenSSF project contributors  | 
 | 146 | +# SPDX-License-Identifier: MIT  | 
 | 147 | +"""Non-compliant Code Example."""  | 
 | 148 | +from time import sleep  | 
 | 149 | + | 
 | 150 | + | 
 | 151 | +def shift_right(value: int) -> int:  | 
 | 152 | +    while (value != 0):  | 
 | 153 | +        print(f"{value}", flush=True)  | 
 | 154 | +        value >>= 1  | 
 | 155 | +        sleep(1)  | 
 | 156 | +    return value  | 
 | 157 | + | 
 | 158 | + | 
 | 159 | +value = shift_right(10)  | 
 | 160 | +print("Returned", value)  | 
 | 161 | +shift_right(-10)  | 
 | 162 | +print("Will never reach here")  | 
 | 163 | +```  | 
 | 164 | + | 
 | 165 | +## Compliant Solution  | 
 | 166 | + | 
 | 167 | +Bit-shifting is an optimization pattern that works better for languages closer to the CPU than Python. Math in Python is better done by arithmetical functions in Python as stated by *CWE-1335: Promote readability and compatibility by using mathematical written code with arithmetic operations instead of bit-wise operations* [[OpenSSF Secure Coding in Python 2025]](https://github.com/ossf/wg-best-practices-os-developers/blob/main/docs/Secure-Coding-Guide-for-Python/CWE-682/CWE-1335/01/README.md).  | 
 | 168 | +Understanding `ctypes` or `C` requires understanding the *CERT C Coding Standard* [[SEI CERT C 2025]](https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard)and setting boundaries manually in Python.  | 
 | 169 | + | 
 | 170 | +## Automated Detection  | 
 | 171 | + | 
 | 172 | +Not available  | 
 | 173 | + | 
 | 174 | +## Related Guidelines  | 
 | 175 | + | 
 | 176 | +<table>  | 
 | 177 | +<tr>  | 
 | 178 | +<td>  | 
 | 179 | +<a href="https://github.com/ossf/wg-best-practices-os-developers/tree/main/docs/Secure-Coding-Guide-for-Python">[OpenSSF Secure Coding in Python 2025]</a>  | 
 | 180 | +</td>  | 
 | 181 | +<td>  | 
 | 182 | +<a href="https://github.com/ossf/wg-best-practices-os-developers/blob/main/docs/Secure-Coding-Guide-for-Python/CWE-682/CWE-1335/01/README.md">CWE-1335: Promote readability and compatibility by using mathematical written code with arithmetic operations instead of bit-wise operations</a>  | 
 | 183 | +</td>  | 
 | 184 | +</tr>  | 
 | 185 | +<tr>  | 
 | 186 | +<tr>  | 
 | 187 | +<td>  | 
 | 188 | +<a href="http://cwe.mitre.org/">MITRE CWE</a>  | 
 | 189 | +</td>  | 
 | 190 | +<td>  | 
 | 191 | +Pillar: <a href="https://cwe.mitre.org/data/definitions/682.html"> [CWE-682: Incorrect Calculation]</a>  | 
 | 192 | +</td>  | 
 | 193 | +</tr>  | 
 | 194 | +<tr>  | 
 | 195 | +<td>  | 
 | 196 | +<a href="http://cwe.mitre.org/">MITRE CWE</a>  | 
 | 197 | +</td>  | 
 | 198 | +<td>  | 
 | 199 | +Base: <a href="https://cwe.mitre.org/data/definitions/1335.html">[CWE-1335: Incorrect Bitwise Shift of Integer (4.12)]</a>  | 
 | 200 | +</td>  | 
 | 201 | +</tr>  | 
 | 202 | +<tr>  | 
 | 203 | +<td>  | 
 | 204 | +<a href="https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java">[SEI CERT Oracle Coding Standard for Java]</a>  | 
 | 205 | +</td>  | 
 | 206 | +<td>  | 
 | 207 | +<a href="https://wiki.sei.cmu.edu/confluence/display/java/NUM14-J.+Use+shift+operators+correctly">[NUM14-J. Use shift operators correctly]</a>  | 
 | 208 | +</td>  | 
 | 209 | +</tr>  | 
 | 210 | +<tr>  | 
 | 211 | +<td>  | 
 | 212 | +<a href="https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard">[CERT C Coding Standard]</a>  | 
 | 213 | +</td>  | 
 | 214 | +<td>  | 
 | 215 | +<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT34-C.+Do+not+shift+an+expression+by+a+negative+number+of+bits+or+by+greater+than+or+equal+to+the+number+of+bits+that+exist+in+the+operand">[INT34-C. Do not shift an expression by a negative number of bits or by greater than or equal to the number of  | 
 | 216 | +bits that exist in the operand]</a>  | 
 | 217 | +</td>  | 
 | 218 | +</tr>  | 
 | 219 | +</table>  | 
 | 220 | + | 
 | 221 | +## Bibliography  | 
 | 222 | + | 
 | 223 | +<table>  | 
 | 224 | +<tr>  | 
 | 225 | +<td>  | 
 | 226 | +[SEI CERT JAVA 2024]  | 
 | 227 | +</td>  | 
 | 228 | +<td>  | 
 | 229 | +NUM01-J. Do not perform bitwise and arithmetic operations on the same data [online]. Available from: <a href="https://wiki.sei.cmu.edu/confluence/display/java/NUM01-J.+Do+not+perform+bitwise+and+arithmetic+operations+on+the+same+data">https://wiki.sei.cmu.edu/confluence/display/java/NUM01-J.+Do+not+perform+bitwise+and+arithmetic+operations+on+the+same+data</a>,  [Accessed 6 May 2025]  | 
 | 230 | +</td>  | 
 | 231 | +</tr>  | 
 | 232 | +<tr>  | 
 | 233 | +<td>  | 
 | 234 | +[SEI CERT C 2025]  | 
 | 235 | +</td>  | 
 | 236 | +<td>  | 
 | 237 | +CERT C Coding Standard [online]. Available from: <a href=https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard>https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard</a> [Accessed 6 May 2025]  | 
 | 238 | +</td>  | 
 | 239 | +</tr>  | 
 | 240 | +<table>  | 
0 commit comments