TIL bash shell arithmetic does its operations as signed C integer arithmetic, so that overflow invokes undefined behavior. 🤡
Conversation
Notices
-
Embed this notice
Rich Felker (dalias@hachyderm.io)'s status on Monday, 23-Jun-2025 11:42:42 JST Rich Felker
- Haelwenn /элвэн/ :triskell: likes this.
-
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 11:44:27 JST Haelwenn /элвэн/ :triskell:
@dalias Well that's POSIX behavior: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_04 -
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 11:48:13 JST Haelwenn /элвэн/ :triskell:
@dalias Although float can be allowed when it wouldn't change non-overflow results but I doubt most shells switch to a float. In conversation permalink -
Embed this notice
Rich Felker (dalias@hachyderm.io)'s status on Monday, 23-Jun-2025 11:48:42 JST Rich Felker
@lanodan It's not clear to me that the shell itself is permitted to have undefined behavior (which would include allowance to execute rm -rf /) just because the arithmetic expression would as a C expression. Even if this is permitted, it's a serious QoI and security fault since arithmetic is often evaluated from untrusted inputs.
In conversation permalink -
Embed this notice
Rich Felker (dalias@hachyderm.io)'s status on Monday, 23-Jun-2025 11:49:50 JST Rich Felker
@lanodan The problem is not that it's semantically signed-integer but that it's implemented under the hood with C signed integers and no provisions for overflow checking or otherwise avoiding UB at the shell implementation layer.
In conversation permalink Haelwenn /элвэн/ :triskell: likes this. -
Embed this notice
John Regehr (regehr@mastodon.social)'s status on Monday, 23-Jun-2025 11:52:13 JST John Regehr
@dalias for this same reason it used to be possible to terminate bash by dividing by zero in a bash script
In conversation permalink -
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 11:52:25 JST Haelwenn /элвэн/ :triskell:
@dalias So like it throwing an error when an overflow happens?
Well… given it's UB shells could adopt that behavior.In conversation permalink -
Embed this notice
Rich Felker (dalias@hachyderm.io)'s status on Monday, 23-Jun-2025 11:53:23 JST Rich Felker
@lanodan No, it's just probably doing what they intended, silent wraparound. But the compiler could be "optimizing" it in fun ways or trapping and killing the shell (like if you turn on sanitizers), or whatever.
In conversation permalink -
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 11:56:52 JST Haelwenn /элвэн/ :triskell:
@dalias Yeah, would make sense for shells to avoid C's UB there, they're effectively given freedom as it is UB too but it doesn't means they have to propagate it to C. In conversation permalink -
Embed this notice
GNU Too (gnu2@gnusocial.jp)'s status on Monday, 23-Jun-2025 11:59:42 JST GNU Too
@dalias lol nice! In conversation permalink -
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 19:01:41 JST Haelwenn /элвэн/ :triskell:
@a1ba @dalias That got dropped in modern C standards In conversation permalink -
Embed this notice
:umu: :umu: (a1ba@suya.place)'s status on Monday, 23-Jun-2025 19:01:42 JST :umu: :umu:
@dalias C still pretends there are machines that aren't two's complement :( In conversation permalink -
Embed this notice
Haelwenn /элвэн/ :triskell: (lanodan@queer.hacktivis.me)'s status on Monday, 23-Jun-2025 19:11:51 JST Haelwenn /элвэн/ :triskell:
@a1ba @dalias It still entirely is like gcc/clang have -fwrap and -ftrap.
And like, you don't really need to drop one's complement to get rid of UB, you could switch it to implementation-defined or 2+ acceptable behaviors.
In conversation permalink -
Embed this notice
:umu: :umu: (a1ba@suya.place)'s status on Monday, 23-Jun-2025 19:11:52 JST :umu: :umu:
@lanodan @dalias so it's not UB anymore In conversation permalink -
Embed this notice
Rich Felker (dalias@hachyderm.io)'s status on Monday, 23-Jun-2025 21:04:15 JST Rich Felker
@a1ba No, UB is not a consequence of not assuming 2s compl.
In conversation permalink -
Embed this notice
divVerent (divverent@misskey.de)'s status on Tuesday, 24-Jun-2025 20:22:05 JST divVerent
@uis@pone.social @a1ba@suya.place @dalias@hachyderm.io @lanodan@queer.hacktivis.me I am not a source. https://google.github.io/styleguide/cppguide.html "On Unsigned Integers" is.
In conversation permalink Attachments
Haelwenn /элвэн/ :triskell: likes this. -
Embed this notice
uis (uis@pone.social)'s status on Tuesday, 24-Jun-2025 20:22:06 JST uis
@a1ba @dalias @lanodan google banned unsigned integers in their codebase to detect integer overflow with ubsan.
Source: @divVerentIn conversation permalink -
Embed this notice
divVerent (divverent@misskey.de)'s status on Tuesday, 24-Jun-2025 20:22:06 JST divVerent
@a1ba@suya.place @lanodan@queer.hacktivis.me @dalias@hachyderm.io Which is kinda sad. I actually want trapping integers by default - both signed and unsigned. Wrapping behavior should be available as an attribute - again on both.
Google - just like everyone else as it's all open source - has "some semblance of" trapping signed integers in some build configurations, but we can't have trapping unsigned integers because too much existing code depends on wrapping there (and the standard even mandates it). Like, yes, UBSan has an option for it, but have you ever tried running any real world code with it?
And yet, with having trapping unsigned int, size_t actually becomes a good and safe to use type...
Another fun problem of C++ is that some places use ssize_t (for example iterator differences, for obvious reasons). Thus, the larger range of size_t is not actually available anyway..;.In conversation permalink Haelwenn /элвэн/ :triskell: likes this.