|
| 1 | +# CWE-783: Operator Precedence Logic Error |
| 2 | + |
| 3 | +Failing to understand the order of precedence in expressions that read and write to the same object can lead to unintended side effects. |
| 4 | + |
| 5 | +Python has distinct different concepts for: |
| 6 | + |
| 7 | +<table> |
| 8 | +<tr> |
| 9 | +<th> |
| 10 | +type |
| 11 | +</th> |
| 12 | +<th>examples</th> |
| 13 | +<th>Typical direction</th> |
| 14 | +<tr> |
| 15 | +<td>Assignments</td><td>to store a value such as x = 1</td><td>right-to-left.</td> |
| 16 | +</tr> |
| 17 | +<tr> |
| 18 | +<td>Expressions</td><td>3+4 or x*2</td><td>left-to-right</td> |
| 19 | +</tr> |
| 20 | +<tr> |
| 21 | +<td>Augmented assignments</td><td>a += 1</td><td>left-to-right <a href="https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statements">[Python docs 2025 - simple statements]</a></td> |
| 22 | +</tr> |
| 23 | +</table> |
| 24 | + |
| 25 | +Expressions such as `2 ** 3`, or two to the power of three, are evaluated from right to left [[python power 2025](https://docs.python.org/3/reference/expressions.html#index-59)] as demonstrated in `example01.py` |
| 26 | + |
| 27 | +_[example01.py:](example01.py)_ |
| 28 | + |
| 29 | +```py |
| 30 | +"""Code Example""" |
| 31 | + |
| 32 | +print(2**3**2) # prints 512 |
| 33 | +print((2**3) ** 2) # prints 64 |
| 34 | +print(2**9) |
| 35 | + |
| 36 | +``` |
| 37 | + |
| 38 | +The first expression would print `64` if Python would resolve from left-to-right but prints `512` as it calculates `3**2` before using its result with `2**9`. |
| 39 | +The `example02.py` behaves 'normal' for a programmer but makes no sense as a mathematical formular. |
| 40 | + |
| 41 | +_[example02.py:](example02.py)_ |
| 42 | + |
| 43 | +```py |
| 44 | +z = 2 |
| 45 | +z *= 2 + 1 |
| 46 | +print(f"z *= 2 + 1 ={z}") |
| 47 | +``` |
| 48 | + |
| 49 | +If a method changes an object’s state (has side effects) and is called multiple times within one expression, the result can be surprising and incorrect. For further info on python's order of precedence refer to The Python Language Specification , §6.16, "Evaluation Order" [[PLR 2022](https://docs.python.org/3/reference/expressions.html#evaluation-order)]. |
| 50 | + |
| 51 | +## Non-Compliant Code Example |
| 52 | + |
| 53 | +`noncompliant01.py` is expected to provide labels for numbers, but it unnecessarily obfuscates the evaluation and logic. |
| 54 | + |
| 55 | +_[noncompliant01.py](noncompliant01.py):_ |
| 56 | + |
| 57 | +```python |
| 58 | +"""Non-Compliant Code Example""" |
| 59 | + |
| 60 | + |
| 61 | +def label(number: int) -> list[str]: |
| 62 | + key = int(number < 5) # (1) small |
| 63 | + key |= ((number & 1) ^ 1) << 1 # (2) for even, 0 for odd |
| 64 | + key |= (number < 0) << 2 # (4) negative |
| 65 | + key |= (number > 0) << 3 # (8) positive |
| 66 | + |
| 67 | + parts = ( |
| 68 | + "big", # 0 |
| 69 | + "small", # 1 |
| 70 | + "even small", # 2 |
| 71 | + "even small", # 3 |
| 72 | + "neg", # 4 |
| 73 | + "neg small", # 5 |
| 74 | + "neg even small", # 6 |
| 75 | + "neg even small", # 7 |
| 76 | + "big", # 8 |
| 77 | + "big even", # 9 |
| 78 | + "neg big", # 10 |
| 79 | + "neg big even", # 11 |
| 80 | + "big", # 12 |
| 81 | + "big even", # 13 |
| 82 | + "neg big", # 14 |
| 83 | + "neg big even", # 15 |
| 84 | + ) |
| 85 | + |
| 86 | + permuted = tuple(parts[(i * 5) & 7] for i in range(8)) |
| 87 | + |
| 88 | + idx = (key * 5) & 7 |
| 89 | + return permuted[idx].split(" ") |
| 90 | + |
| 91 | + |
| 92 | +for number in range(-6, 6): |
| 93 | + print(f"{number} = {label(number)}") |
| 94 | +``` |
| 95 | + |
| 96 | +_Example output of `noncompliant01.py`:_ |
| 97 | + |
| 98 | +```bash |
| 99 | +-6 = ['neg', 'even', 'small'] |
| 100 | +-5 = ['neg', 'small'] |
| 101 | +-4 = ['neg', 'even', 'small'] |
| 102 | +-3 = ['neg', 'small'] |
| 103 | +-2 = ['neg', 'even', 'small'] |
| 104 | +-1 = ['neg', 'small'] |
| 105 | +0 = ['even', 'small'] |
| 106 | +1 = ['small'] |
| 107 | +2 = ['even', 'small'] |
| 108 | +3 = ['small'] |
| 109 | +4 = ['even', 'small'] |
| 110 | +5 = ['big'] |
| 111 | +``` |
| 112 | + |
| 113 | +The `noncompliant01.py` does respond with the correct output. Extending the `noncompliant01.py` to also a label `postive` or `zero` numbers would be challenging. |
| 114 | + |
| 115 | +## Compliant Solution |
| 116 | + |
| 117 | +This compliant solution, uses equivalent logic and performs at most one write operation per expression, which makes the code easier to understand and maintain. |
| 118 | + |
| 119 | +_[compliant01.py](compliant01.py):_ |
| 120 | + |
| 121 | +```python |
| 122 | +"""Compliant Code Example""" |
| 123 | + |
| 124 | + |
| 125 | +def label(number: int) -> list[str]: |
| 126 | + labels = [] |
| 127 | + if number < 0: |
| 128 | + labels.append("neg") |
| 129 | + if number % 2 == 0: |
| 130 | + labels.append("even") |
| 131 | + if number < 5: |
| 132 | + labels.append("small") |
| 133 | + if number >= 5: |
| 134 | + labels.append("big") |
| 135 | + return labels |
| 136 | + |
| 137 | + |
| 138 | +for number in range(-6, 6): |
| 139 | + print(f"{number} = {label(number)}") |
| 140 | + |
| 141 | +``` |
| 142 | + |
| 143 | +## Automated Detection |
| 144 | + |
| 145 | +<table> |
| 146 | + <hr> |
| 147 | + <td>Tool</td> |
| 148 | + <td>Version</td> |
| 149 | + <td>Checker</td> |
| 150 | + <td>Description</td> |
| 151 | + </hr> |
| 152 | + <tr> |
| 153 | + <td>Bandit</td> |
| 154 | + <td>1.7.4 on Python 3.10.4</td> |
| 155 | + <td>Not Available</td> |
| 156 | + <td></td> |
| 157 | + </tr> |
| 158 | +</table> |
| 159 | + |
| 160 | +## Related Guidelines |
| 161 | + |
| 162 | +<table> |
| 163 | + <tr> |
| 164 | + <td><a href="http://cwe.mitre.org/">MITRE CWE</a></td> |
| 165 | + <td>Pillar: <a href="https://cwe.mitre.org/data/definitions/691.html"> [CWE-691: Insufficient Control Flow Management]</a></td> |
| 166 | + </tr> |
| 167 | + <tr> |
| 168 | + <td><a href="http://cwe.mitre.org/">MITRE CWE</a></td> |
| 169 | + <td>Base <a href="https://cwe.mitre.org/data/definitions/783.html">CWE-783: Operator Precedence Logic Error</a></td> |
| 170 | + </tr> |
| 171 | + <tr> |
| 172 | + <td><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></td> |
| 173 | + <td><a href="https://wiki.sei.cmu.edu/confluence/display/java/EXP05-J.+Do+not+follow+a+write+by+a+subsequent+write+or+read+of+the+same+object+within+an+expression"></a>EXP05-J. Do not follow a write by a subsequent write or read of the same object within an expression</td> |
| 174 | + </tr> |
| 175 | + <tr> |
| 176 | + <td><a href="https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard">CERT C Coding Standard</a></td> |
| 177 | + <td><a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP30-C.+Do+not+depend+on+the+order+of+evaluation+for+side+effects">EXP30-C. Do not depend on the order of evaluation for side effects</a></td> |
| 178 | + </tr> |
| 179 | + <tr> |
| 180 | + <td><a href="https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046682">SEI CERT C++ Coding Standard</a></td> |
| 181 | + <td><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP50-CPP.+Do+not+depend+on+the+order+of+evaluation+for+side+effects">EXP50-CPP. Do not depend on the order of evaluation for side effects</a></td> |
| 182 | + </tr> |
| 183 | +</table> |
| 184 | + |
| 185 | +## Bibliography |
| 186 | + |
| 187 | +<table> |
| 188 | + <tr> |
| 189 | + <td>[Python docs 2025 - simple statements]</td> |
| 190 | + <td>7.2.1. Augmented assignment statements [online]. Available from: <a href="https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statements">https://docs.python.org/3/reference/simple_stmts.html?highlight=augmented%20assignment%20operators#augmented-assignment-statement</a>, [Accessed 19 September 2025]</td> |
| 191 | + </tr> |
| 192 | + <tr> |
| 193 | + <td>[python power 2025]</td> |
| 194 | + <td>6. Expressions [online]. Available from: <a href="https://docs.python.org/3/reference/expressions.html#index-59">https://docs.python.org/3/reference/expressions.html#index-59</a>, [Accessed 19 September 2025]</td> |
| 195 | + </tr> |
| 196 | + <tr> |
| 197 | + <td>[PLR 2022]</td> |
| 198 | + <td>6.16. Evaluation order [online]. Available from: <a href="https://docs.python.org/3/reference/expressions.html#evaluation-order">https://docs.python.org/3/reference/expressions.html#evaluation-order</a>, [Accessed 19 September 2025]</td> |
| 199 | + </tr> |
| 200 | +</table> |
0 commit comments