Skip to content

[libc] Fix incorrect unsigned comparison #135595

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 3 commits into from
Apr 17, 2025

Conversation

yingcong-wu
Copy link
Contributor

@yingcong-wu yingcong-wu commented Apr 14, 2025

There is a problem with such unsigned comparison pattern:

if(unsigned_a - unsigned_b > 0) { /* only NOT go here when unsigned_a==unsigned_b */ }

When unsigned_a < unsigned_b, the result will still be >0 due to underflow.
This patch fixes two of the occurrences I found.
Also remove two redundant if where its condition is guaranteed by outer if.

@llvmbot
Copy link
Member

llvmbot commented Apr 14, 2025

@llvm/pr-subscribers-libc

Author: Wu Yingcong (yingcong-wu)

Changes

There is a problem with such unsigned comparison pattern:

if(unsigned_a - unsigned_b &gt; 0) { /* only NOT go here when unsigned_a==unsigned_b */ }

When unsigned_a < unsigned_b, the result will still be &gt;0 due to underflow.
This patch fixes two of the occurrences I found.


Full diff: https://github.com/llvm/llvm-project/pull/135595.diff

1 Files Affected:

  • (modified) libc/src/stdio/printf_core/float_dec_converter.h (+2-2)
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index ee5549825a6f2..178f228527f68 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -192,7 +192,7 @@ template <WriteMode write_mode> class FloatWriter {
         RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, digits_to_write}));
       }
       RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
-      if (buffered_digits - digits_to_write > 0) {
+      if (buffered_digits > digits_to_write) {
         // Write the digits after the decimal point.
         RET_IF_RESULT_NEGATIVE(
             writer->write({block_buffer + digits_to_write,
@@ -222,7 +222,7 @@ template <WriteMode write_mode> class FloatWriter {
         RET_IF_RESULT_NEGATIVE(writer->write(MAX_BLOCK_DIGIT, digits_to_write));
       }
       RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
-      if ((BLOCK_SIZE * max_block_count) - digits_to_write > 0) {
+      if ((BLOCK_SIZE * max_block_count) > digits_to_write) {
         RET_IF_RESULT_NEGATIVE(writer->write(
             MAX_BLOCK_DIGIT, (BLOCK_SIZE * max_block_count) - digits_to_write));
       }

@yingcong-wu
Copy link
Contributor Author

The build error looks not related to my change.

/home/runner/work/llvm-project/llvm-project/build/compiler-rt/lib/fuzzer/libcxx_fuzzer_aarch64/build/include/c++/v1/__bit/popcount.h:30:10: error: use of undeclared identifier '__builtin_popcountg'
   30 |   return __builtin_popcountg(__t);

@s-barannikov
Copy link
Contributor

The build error looks not related to my change.

FYI @winner245 @mordante

@@ -192,7 +192,7 @@ template <WriteMode write_mode> class FloatWriter {
RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, digits_to_write}));
}
RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
if (buffered_digits - digits_to_write > 0) {
if (buffered_digits > digits_to_write) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer if condition guarantees that buffered_digits - digits_to_write >= 0. So underflow does not occur here. However, the proposed change enhances clarity and readability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, the if (digits_to_write > 0) statement on line 190 is redundant, as the outer if already guarantees the condition to be true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the if in line 190 is redundant and that is quite clear, but I don't think it is straightforward that the outer if also guarantees buffered_digits - digits_to_write >= 0, not to me anyway. I will remove the redundant if in line 190.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the underflow shouldn't be possible, but the proposed change improves clarity.

@yingcong-wu yingcong-wu requested a review from winner245 April 15, 2025 05:12
Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this cleanup, this change seems correct and passes the tests + some local fuzzing.

If you're interested in creating a followup, simplifying the ifs on 186 and 215 would be great. The intended behavior is checking if the number of digits that will be written is between 0 and buffered_digits/BLOCK_SIZE * max_block_count respectively. Making that explicit would make this change more clear.

@@ -192,7 +192,7 @@ template <WriteMode write_mode> class FloatWriter {
RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, digits_to_write}));
}
RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
if (buffered_digits - digits_to_write > 0) {
if (buffered_digits > digits_to_write) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the underflow shouldn't be possible, but the proposed change improves clarity.

@yingcong-wu
Copy link
Contributor Author

Hi @michaelrj-google, are those CI failures expected? Can we merge this PR in this state?

@michaelrj-google
Copy link
Contributor

The CI failures appear to be from libc++ and unrelated. This patch is fine to merge. Do you have commit access or should I merge this for you?

@yingcong-wu yingcong-wu merged commit 6b8d072 into llvm:main Apr 17, 2025
13 of 16 checks passed
@yingcong-wu
Copy link
Contributor Author

Merged, thank you all for reviewing.

var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
There is a problem with such unsigned comparison pattern:
```
if(unsigned_a - unsigned_b > 0) { /* only NOT go here when unsigned_a==unsigned_b */ }
```
When `unsigned_a` < `unsigned_b`, the result will still be `>0` due to
underflow.
This patch fixes two of the occurrences I found.
Also remove two redundant `if` where its condition is guaranteed by
outer `if`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants