generated from ossf/project-template
-
Notifications
You must be signed in to change notification settings - Fork 184
Adding documentation to CWE-197 as part of #531 #614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
153 changes: 153 additions & 0 deletions
153
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# CWE-197: Numeric Truncation Error | ||
|
||
Ensure to have predictable outcomes in loops by using int instead of `float` variables as a counter. | ||
|
||
Floating-point arithmetic can only represent a finite subset of real numbers [[IEEE Std 754-2019](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229)], such as `0.555....` represented by `0.5555555555555556` also discussed in [CWE-1339: Insufficient Precision or Accuracy of a Real Number](https://github.com/ossf/wg-best-practices-os-developers/tree/main/docs/Secure-Coding-Guide-for-Python/CWE-682/CWE-1339). Code examples in this rule are based on [Albing and Vossen, 2017]. | ||
|
||
Side effects of using `float` as a counter is demonstrated in `example01.py` showcasing that calculating `0.1 + 0.2` does not end up as `0.3`. | ||
|
||
[*example01.py:*](example01.py) | ||
|
||
```py | ||
""" Code Example """ | ||
|
||
value = 0.0 | ||
while value <= 1: | ||
print(f"{type(value)} {value}") | ||
value += 0.1 | ||
``` | ||
|
||
**Output of exampl01.py:** | ||
|
||
```bash | ||
<class 'float'> 0.0 | ||
<class 'float'> 0.1 | ||
<class 'float'> 0.2 | ||
<class 'float'> 0.30000000000000004 | ||
<class 'float'> 0.4 | ||
<class 'float'> 0.5 | ||
<class 'float'> 0.6 | ||
<class 'float'> 0.7 | ||
<class 'float'> 0.7999999999999999 | ||
<class 'float'> 0.8999999999999999 | ||
<class 'float'> 0.9999999999999999 | ||
``` | ||
|
||
## Non-Compliant Code Example | ||
|
||
The `noncompliant01.py` code demonstrates a side effect when a floating point counter is used. | ||
|
||
[*noncompliant01.py:*](noncompliant01.py) | ||
|
||
```py | ||
""" Non-compliant Code Example """ | ||
counter = 0.0 | ||
while counter <= 1.0: | ||
if counter == 0.8: | ||
print("we reached 0.8") | ||
break # never going to reach this | ||
counter += 0.1 | ||
``` | ||
|
||
The `noncompliant01.py` code will never print "we are at 0.8" due to lack of precision or controlled rounding. | ||
|
||
## Compliant Solution | ||
|
||
The `compliant01.py` makes use of integer as long as possible and only converts to `float` where needed. | ||
|
||
[*compliant01.py:*](compliant01.py) | ||
|
||
```py | ||
""" Compliant Code Example """ | ||
counter = 0 | ||
while counter <= 10: | ||
value = counter/10 | ||
if value == 0.8: | ||
print("we reached 0.8") | ||
break | ||
counter += 1 | ||
``` | ||
|
||
myteron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## Non-Compliant Code Example | ||
|
||
The `example02.py` code demonstrates more precision limites in floating numbers. | ||
myteron marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
[*example02.py:*](example02.py) | ||
|
||
```py | ||
""" Code Example """ | ||
print(f"{1.0 + 1e-16:.20f}") | ||
print(f"{1.0 + 1e-15:.20f}") | ||
``` | ||
|
||
**Output of example02.py:** | ||
|
||
```bash | ||
1.00000000000000000000 | ||
1.00000000000000111022 | ||
``` | ||
|
||
Below `noncompliant02.py` code tries to increment a floating-point `COUNTER` by a too small value causing an infinite loop. | ||
|
||
[*noncompliant02.py:*](noncompliant02.py) | ||
|
||
```py | ||
""" Non-compliant Code Example """ | ||
counter = 1.0 + 1e-16 | ||
target = 1.0 + 1e-15 | ||
while counter <= target: # never ends | ||
print(f"counter={counter / 10**16 :.20f}") | ||
print(f" target={target / 10**16:.20f}") | ||
counter += 1e-16 | ||
|
||
``` | ||
|
||
The code will loop forever due to missing precision in the initial calculation of `COUNTER = 1.0 + 1e-16`. | ||
|
||
## Compliant Solution | ||
|
||
Use of an `int` loop counter that is only converted to `float` when required is demonstrated in `compliant2.py`. | ||
|
||
[*compliant02.py:*](compliant02.py) | ||
|
||
```py | ||
""" Compliant Code Example """ | ||
counter = 1 | ||
target = 10 | ||
while counter <= target: | ||
print(f"counter={counter / 10**16 :.20f}") | ||
print(f" target={target / 10**16:.20f}") | ||
counter += 1 | ||
``` | ||
|
||
## Defnitions | ||
myteron marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
|Definition|Explanation|Reference| | ||
|:---|:---|:---| | ||
|Loop Counters|loop counters are variables used to control the iterations of a loop|[Loop counter - Wikipedia](https://en.wikipedia.org/wiki/For_loop#Loop_counters)| | ||
|
||
## Automated Detection | ||
|
||
|Tool|Version|Checker|Description| | ||
|:---|:---|:---|:---| | ||
|Bandit|1.7.4 on Python 3.10.4|Not Available|| | ||
|Flake8|8-4.0.1 on Python 3.10.4|Not Available|| | ||
|
||
## Related Guidelines | ||
|
||
||| | ||
|:---|:---| | ||
|[MITRE CWE](http://cwe.mitre.org/)|Pillar [CWE-664: Improper Control of a Resource Through its Lifetime (4.13) (mitre.org)](https://cwe.mitre.org/data/definitions/664.html)| | ||
|[MITRE CWE](http://cwe.mitre.org/)|Class [CWE-197: Numeric Truncation Error](https://cwe.mitre.org/data/definitions/197.html)| | ||
|[SEI CERT Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java)|[NUM09-J. Do not use floating-point variables as loop counters](https://wiki.sei.cmu.edu/confluence/display/java/NUM09-J.+Do+not+use+floating-point+variables+as+loop+counters)| | ||
|[SEI CERT C Coding Standard](https://web.archive.org/web/20220511061752/https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard)|[FLP30-C. Do not use floating-point variables as loop counters](https://web.archive.org/web/20220511061752/https://wiki.sei.cmu.edu/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters)| | ||
|[ISO/IEC TR 24772:2010]|| | ||
myteron marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
## Biblography | ||
|
||
||| | ||
|:---|:---| | ||
|[IEEE Std 754-2019](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229)|IEEE Standard for Floating-Point Arithmetic, available from: [https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229), [Last accessed June 2024] | | ||
|[Wikipedia 2024]|Repeating Decimals, available from:[https://en.wikipedia.org/wiki/Repeating_decimal](https://en.wikipedia.org/wiki/Repeating_decimal), [Last accessed August 2024] | | ||
|[Albing and Vossen, 2017]|Albin, C. and Vossen, JP (2017) 6.13 Looping with Floating Point Values. In: Bleiel, J., Brown, K. and Head, R. eds. bash Cookbook: Solutions and Examples for bash Users, 2d Edition. Sebastopol: O'Reilly Media, Inc., pp.159-160| | ||
|[Bloch 2005]|Puzzle 34, "Down for the Count", available from: [https://web.archive.org/web/20220511061752/https://wiki.sei.cmu.edu/confluence/display/java/Rule+AA.+References#RuleAA.References-Bloch05](https://web.archive.org/web/20220511061752/https://wiki.sei.cmu.edu/confluence/display/java/Rule+AA.+References#RuleAA.References-Bloch05), [Last accessed August 2024] | |
12 changes: 8 additions & 4 deletions
12
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/compliant01.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Compliant Code Example """ | ||
value = 0 | ||
while value <= 9: | ||
print(value / 9) | ||
value = value + 1 | ||
|
||
counter = 0 | ||
while counter <= 10: | ||
value = counter/10 | ||
if value == 0.8: | ||
print("we reached 0.8") | ||
break | ||
counter += 1 |
10 changes: 6 additions & 4 deletions
10
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/compliant02.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Compliant Code Example """ | ||
value = 1 | ||
while value <= 10: | ||
print(f"{value / 10 ** 14:.14f}") | ||
value = value + 1 | ||
counter = 1 | ||
target = 10 | ||
while counter <= target: | ||
print(f"counter={counter / 10**16 :.20f}") | ||
print(f" target={target / 10**16:.20f}") | ||
counter += 1 |
8 changes: 8 additions & 0 deletions
8
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/example01.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Code Example """ | ||
|
||
value = 0.0 | ||
while value <= 1: | ||
print(f"{type(value)} {value}") | ||
value += 0.1 |
5 changes: 5 additions & 0 deletions
5
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/example02.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Code Example """ | ||
print(f"{1.0 + 1e-16:.20f}") | ||
print(f"{1.0 + 1e-15:.20f}") |
10 changes: 6 additions & 4 deletions
10
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/noncompliant01.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Non-compliant Code Example """ | ||
value = float(0.0) | ||
while value <= 1: | ||
print(value) | ||
value = value + float(1.0/9.0) | ||
counter = 0.0 | ||
while counter <= 1.0: | ||
if counter == 0.8: | ||
print("we reached 0.8") | ||
break # never going to reach this | ||
counter += 0.1 |
12 changes: 7 additions & 5 deletions
12
docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/noncompliant02.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
# SPDX-FileCopyrightText: OpenSSF project contributors | ||
# SPDX-License-Identifier: MIT | ||
""" Non-compliant Code Example """ | ||
value = float(1.0) + float("1e-18") | ||
target = float(1.0) + float("1e-17") | ||
while value <= target: | ||
print(value) | ||
value = value + float("1e-18") | ||
|
||
counter = 1.0 + 1e-16 | ||
target = 1.0 + 1e-15 | ||
while counter <= target: # never ends | ||
print(f"counter={counter / 10**16 :.20f}") | ||
print(f" target={target / 10**16:.20f}") | ||
counter += 1e-16 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.