Skip to content

Conversation

jepler
Copy link
Contributor

@jepler jepler commented Jul 18, 2025

Summary

I decided to try the longlong build under the sanitizers. This exposed a pair of problems:

  • A line, already commented to explain undefined integer overflow behavior, did in fact produce a diagnostic
  • The address sanitizer produced spurious results on 32-bit x86 builds when raising exceptions.

Testing

I built the branch locally with the sanitizers until it passed.

Trade-offs and Alternatives

It's odd that the fixed version of left shift doesn't produce a diagnostic, because there is still an overflowing operation (now a multiplication, previously a shift). Is the sanitizer deliberately not checking this kind of overflow, or is recognizing a specific overflow check pattern in the following line? I was not able to find any documentation about this, and the documentation seemed to imply that -fsanitize=signed-integer-overflow was enabled by -fsanitize=undefined; explicitly enabling it made no difference.

It's unclear why adding the returns_twice qualification to nlr_push helps the sanitizer to function properly, but it's true that nlr_push should have this annotation. Hence, maybe it should be enabled whenever the compiler supports it? (any plausible gcc & clang versions, probably)

jepler added 5 commits July 18, 2025 13:39
This fixes the following two flavors of diagnostics:
```
../../py/objint_longlong.c:215:30: runtime error:
    left shift of negative value -10000000000000000
../../py/objint_longlong.c:215:30: runtime error:
    left shift of 72623859790382856 by 8 places cannot
    be represented in type 'long long int'
```

This formulation compiles to identical code (gcc 15 -m32 -O3).
In fact, the compiler is so sure the original and modified versions
are identical that building the following:
```
long long f1(long long lhs_val, long long rhs_val, bool *overflow) {
    long long result = lhs_val * (1ll << (int)rhs_val);
    *overflow = rhs_val > 0 && ((lhs_val >= 0 && result < lhs_val)
        || (lhs_val < 0 && result > lhs_val));
    return result;
}

long long f2(long long lhs_val, long long rhs_val, bool *overflow) {
    long long result = lhs_val << ((int)rhs_val);
    *overflow = rhs_val > 0 && ((lhs_val >= 0 && result < lhs_val)
        || (lhs_val < 0 && result > lhs_val));
    return result;
}
```
under `-Os` makes the body of f2 just say `jmp f1`!

Signed-off-by: Jeff Epler <jepler@gmail.com>
This diagnostic occurs when `nlr_push` is marked with the gcc/clang
attribute "returns_twice".

Signed-off-by: Jeff Epler <jepler@gmail.com>
This might affect codegen adversely in other cases,
so the attribute is only enabled in the case where the
address sanitizer is enabled.

Availabilty of the standard gcc/clang attribute syntax is assumed
when __SANITIZE_ADDRESS__ is defined.

Signed-off-by: Jeff Epler <jepler@gmail.com>
Signed-off-by: Jeff Epler <jepler@gmail.com>
Signed-off-by: Jeff Epler <jepler@gmail.com>
Copy link

codecov bot commented Jul 18, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.44%. Comparing base (17fbc5a) to head (ba3f9ca).
Report is 48 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #17709   +/-   ##
=======================================
  Coverage   98.44%   98.44%           
=======================================
  Files         171      171           
  Lines       22208    22209    +1     
=======================================
+ Hits        21863    21864    +1     
  Misses        345      345           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jepler jepler marked this pull request as draft July 18, 2025 19:03
@jepler
Copy link
Contributor Author

jepler commented Jul 18, 2025

Interestingly, I don't see these stack errors locally.

under UBSan, this test and import/import_broken.py fail.

--- /home/runner/work/micropython/micropython/tests/results/extmod_vfs_rom.py.exp	2025-07-18 18:55:56.430739171 +0000
+++ /home/runner/work/micropython/micropython/tests/results/extmod_vfs_rom.py.out	2025-07-18 18:55:56.430739171 +0000
@@ -1 +1,13 @@
-SKIP
+Traceback (most recent call last):
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 489, in <module>
+  File "/home/runner/work/micropython/micropython/lib/micropython-lib/python-stdlib/unittest/unittest/__init__.py", line 464, in main
+  File "/home/runner/work/micropython/micropython/lib/micropython-lib/python-stdlib/unittest/unittest/__init__.py", line 269, in run
+  File "/home/runner/work/micropython/micropython/lib/micropython-lib/python-stdlib/unittest/unittest/__init__.py", line 254, in run
+  File "/home/runner/work/micropython/micropython/lib/micropython-lib/python-stdlib/unittest/unittest/__init__.py", line 420, in _run_suite
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 166, in setUpClass
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 145, in make_romfs
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 136, in _make_romfs
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 115, in mkfile
+  File "/home/runner/work/micropython/micropython/tests/extmod/vfs_rom.py", line 80, in _encode_uint
+RuntimeError: maximum recursion depth exceeded

Under asan, this failure comes up:

--- /home/runner/work/micropython/micropython/tests/results/thread_thread_lock3.py.exp	2025-07-18 18:56:16.661895914 +0000
+++ /home/runner/work/micropython/micropython/tests/results/thread_thread_lock3.py.out	2025-07-18 18:56:16.661895914 +0000
@@ -8,3 +8,7 @@
 my turn: 7
 my turn: 8
 my turn: 9
+=================================================================
+==11953==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xef870270 at pc 0xf102e4eb bp 0xef870248 sp 0xef86fe18
+WRITE of size 12 at 0xef870270 thread T16777215
+TIMEOUT

If I can't resolve these, the "fixes" themselves are probably not worth taking either. Shifting to draft status.

Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Jul 23, 2025
@dpgeorge
Copy link
Member

See also #17735.

@jepler jepler closed this Jul 24, 2025
@projectgus
Copy link
Contributor

Sorry @jepler, I missed that you'd opened this PR as well as the related issue (which I did see, hence the other PR). Your idea to also run the longlong build under ASan in CI seems worthwhile.

@jepler
Copy link
Contributor Author

jepler commented Jul 29, 2025

If there's still a part of this PR that is not done by your #17735 then I'd be happy to revive this PR or start a fresh one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants