WTF Bites


  • Discourse touched me in a no-no place

    @Tsaukpaetra said in WTF Bites:

    @LaoC said in WTF Bites:

    it includes your “sexual activity.”

    @Tesla: Reporting on tsaukpaetra : Error: No data found.

    Better that ERROR: YIKES!


  • Notification Spam Recipient

    @dkf said in WTF Bites:

    @Tsaukpaetra said in WTF Bites:

    @LaoC said in WTF Bites:

    it includes your “sexual activity.”

    @Tesla: Reporting on tsaukpaetra : Error: No data found.

    Better that ERROR: YIKES!

    Error: YIKES. Suggestion: Remedy immediately. Printing voucher



  • Value cannot be negative: 123456789011

    Um, 12 billion is not negative. It's 2023. Your software is running on a 64-bit machine. It's handling 64-bit data elsewhere. Why is part of your code silently overflowing a 32-bit integer?



  • @HardwareGeek said in WTF Bites:

    Why is part of your code silently overflowing a 32-bit integer?

    Because integers still default to 32 bits in most programming languages?


  • Banned

    @Bulb not true anymore. These days most languages have integers default to double-precision floating point.


  • BINNED

    @Gustav and instead of overflowing they start to skip odd numbers?



  • @MrL
    Fixed Enter Mat.png

    FTFY.



  • @Gustav said in WTF Bites:

    @Bulb not true anymore. These days most languages have integers default to double-precision floating point.

    It worked for Commodore BASIC, I guess.



  • @HardwareGeek said in WTF Bites:

    silently overflowing

    That is IMHO the worst part: a silent error. Just throw some Overflow Error or what ever the fück! Do not hide the error.
    But "performance". As if that attosecond was relevant in real software, outside of special performance measurement code where it makes a difference...


  • Considered Harmful

    @BernieTheBernie said in WTF Bites:

    Just throw some Overflow Error or what ever the fück! Do not hide the error.

    If the language does not allow fine-grained control, then I agree.

    int a = Int32.MaxValue;
    int b = 1;
    int c = a + b; // unchecked(a + b) = -2147483648
    int d = checked(a + b); // OverflowException
    


  • @Applied-Mediocrity It ought to be exactly the other way round. checked for the default, unchecked only where explicitly stated (e.g. hashing algorithm).



  • @Gustav said in WTF Bites:

    @Bulb not true anymore. These days most languages have integers default to double-precision floating point.

    The one and half language that does that is maybe more popular these days, but it's still just one and half language, compared to three and half very popular languages where int is 32-bit. And on Windows in C and C++ even long is still 32-bit.


  • Considered Harmful

    @Bulb said in WTF Bites:

    And on Windows in C and C++ even long is still 32-bit.

    Size matters but hey, it's MicroSoft.



  • @BernieTheBernie said in WTF Bites:

    That is IMHO the worst part: a silent error.

    Modular arithmetic is perfectly well defined. Wrapping around from type_max to type_min and vice versa isn't a silent error, it's not an error in the first place.


  • BINNED

    @cvi said in WTF Bites:

    @BernieTheBernie said in WTF Bites:

    That is IMHO the worst part: a silent error.

    Modular arithmetic is perfectly well defined. Wrapping around from type_max to type_min and vice versa isn't a silent error, it's not an error in the first place.

    And yet you almost never want that, unless you’re explicitly dealing with modular arithmetic, like hashing algorithms he mentioned.
    The default should be checked, because most of the time it’s a bug.



  • @BernieTheBernie said in WTF Bites:

    @Applied-Mediocrity It ought to be exactly the other way round. checked for the default, unchecked only where explicitly stated (e.g. hashing algorithm).

    That's good for safety, but not performance. Especially since, on x86 and AMD64, the instruction explicitly intended for that is off-limits as it was co-opted by a processor manufacturer to create an intentional security flaw.



  • @TwelveBaud Oh, and what did I say above?

    As if that attosecond was relevant in real software, outside of special performance measurement code where it makes a difference...



  • @TwelveBaud said in WTF Bites:

    it was co-opted by a processor manufacturer to create an intentional security flaw.

    Huh?


  • Discourse touched me in a no-no place

    @Gustav said in WTF Bites:

    These days most languages have integers default to double-precision floating point.

    Ah yes, Larry Wall's mistake.



  • @LaoC said in WTF Bites:

    @Bulb said in WTF Bites:

    And on Windows in C and C++ even long is still 32-bit.

    Size matters but hey, it's MicroSoft.

    We are in the WTF bites thread and that is a pretty big bite, so let me explain. In C, the built-in integer types have always been defined as platform-specific, and libraries were expected to typedef their own aliases for any uses with specific size constraints or where specific size was needed for cross-platform compatibility.

    Unix libraries did that, and it worked fine for them¹.

    Microsoft also did. Their API always uses their own typedefs like BYTE, WORD and DWORD. But they also made the mistake of documenting, that DWORD is an unsigned long int, and somewhat liberally used that assumption in their code. Which got them fine across the 16- to 32-bit transition, because the sizes for short/int/long went from 16/16/32 to 16/32/32, and they were always using short or long, but when the 64-bit transition, that should have been 16/32/32 to 16/32/64, they has the assumption that sizeof(long) == 4 baked in too many places, so they only extended the pointer. Making them the first (or at least first common) platform where sizeof(long) < sizeof(void *)²


    ¹ Because POSIX never mentioned which type the typedefs lead to. In Linux you can even request 64 bit file size on 32 bit system just via compiler options. Unfortunately 32-bit time_t is a bit more baked in. But 64-bit systems use 64-bit type.

    ² Pointer still fits in size_t and ptrdiff_t if you really need to pass pointer values in, and C99 added the intptr_t specifically for that purpose, but some libraries occasionally assumed they can convert a pointer to long and recover it by converting back later.



  • @cvi said in WTF Bites:

    @BernieTheBernie said in WTF Bites:

    That is IMHO the worst part: a silent error.

    Modular arithmetic is perfectly well defined. Wrapping around from type_max to type_min and vice versa isn't a silent error, it's not an error in the first place.

    It is worse than error. It is Undefined Behaviour™!

    That is, in C and C++, unsigned types are explicitly defined as computing modulo 256sizeof(T), but for signed types overflowing is Undefined Behaviour™ and optimizers regularly apply ‘equivalent’ transformations that are only equivalent if the original expression wouldn't overflow.



  • @topspin said in WTF Bites:

    And yet you almost never want that, unless you’re explicitly dealing with modular arithmetic, like hashing algorithms he mentioned.
    The default should be checked, because most of the time it’s a bug.

    It's trying to fix the wrong problem, though. You almost never want it, because you're almost never dealing with the full range of integers to begin with. For example, you go out of bounds long before you overflow -- or you picked the wrong integer size to begin with.


  • Discourse touched me in a no-no place

    @Bulb said in WTF Bites:

    It is worse than error. It is Undefined Behaviour™!

    It can't be defined either, because that would stop C from being easy to implement on a (pretty much mythical) 1s complement architecture.



  • @Bulb I think the reasons for keeping signed over/under flow UB in C++ is pants on head retarded. (But I also don't use signed integers very much outside of special cases.)



  • @dkf I think of the recent C++ standards now essentially straight up requires 2s complement. I doubt C is going to follow down that route and C++ is unlikely to change their view on signed integers for a number of reasons, despite now being 2s complement.



  • @dkf said in WTF Bites:

    @Bulb said in WTF Bites:

    It is worse than error. It is Undefined Behaviour™!

    It can't be defined either, because that would stop C from being easy to implement on a (pretty much mythical) 1s complement architecture.

    My understanding is that it actually isn't mythical, because there is some relatively common industrial microcontroller that does indeed use one's complement.

    @cvi said in WTF Bites:

    @dkf I think of the recent C++ standards now essentially straight up requires 2s complement. I doubt C is going to follow down that route and C++ is unlikely to change their view on signed integers for a number of reasons, despite now being 2s complement.

    It sort-of always did. There is one operation, signed-to-unsigned conversion, that is defined as modulo 2number of bits, which is exactly what two's complement does, but requires explicit conversion in any other representation.

    @cvi said in WTF Bites:

    @Bulb I think the reasons for keeping signed over/under flow UB in C++ is pants on head retarded. (But I also don't use signed integers very much outside of special cases.)

    The main reason signed overflow is undefined is optimizations. The optimizers, of all the major C and C++ compilers, will readily assume things like a + 1 > a is always true and a + b > c iff a > c - b. Neither of those are true in the face of overflow, but apparently enough loop conditions can be simplified using those that the compiler developers really want to use them.

    And yes, we got bitten by this a couple of times when using the max/min int values for various guards.



  • @LaoC said in WTF Bites:

    @Bulb said in WTF Bites:

    And on Windows in C and C++ even long is still 32-bit.

    Size matters but hey, it's MicroSoft.

    Computer people do not have dick length contest, but integer size contests...



  • @Bulb said in WTF Bites:

    because there is some relatively common industrial microcontroller that does indeed use one's complement.

    Which one(s)?



  • @Zerosquare A former colleague mentioned some to me a couple years ago, but I don't really remember what it was.



  • @Bulb said in WTF Bites:

    The main reason signed overflow is undefined is optimizations. The optimizers, of all the major C and C++ compilers, will readily assume things like a + 1 > a is always true and a + b > c iff a > c - b. Neither of those are true in the face of overflow, but apparently enough loop conditions can be simplified using those that the compiler developers really want to use them.

    From what I remember, the source of optimizations is mostly in the face of size extensions. So, if you use an int (typically 32-bit) on a 64-bit machine, the compiler can internally just assume 64 bits or transform things entirely into pointer arithmetic. Because there is no overflow (it's UB), it doesn't have to introduce additional checks for the size. With a 32-bit unsigned, it would have to force wrapping behaviour at the 32-bit boundaries (even if that never actually happens).

    Now, if you use appropriately sized types (64 bits with 64 bit pointers), that's a moot point.

    My observation is mostly that using 64-bit types (unsigned or signed) overall results in better codegen than 32 bit types. For 32 bit, signed int is a bit better than unsigned. For 64-bit, the differences are minor.

    CUDA is a special case. 32-bit values use a single register, 64-bit two. Sticking to 32-bit can lower register pressure, so there's a reason to do that. At that point using signed integers can help.



  • @cvi I remember specifically running into the comparisons. Generic code often has conditions that cannot be satisfied with provided parameters—unless overflow occurs—so the compiler wants to get rid of them when inlining. The unfortunate side-effect is that if you do want to check for overflow, it is tricky, because the compiler will just optimize your checks to false.



  • @Zerosquare said in WTF Bites:

    Huh?

    Intel added the bound instruction for checking that a number was within a lower and an upper bound, and throwing a "bound range exceeded" exception if it wasn't. This is useful for arrays (either by index or by pointer), jump tables, and other such things. It's also useful for most of the cases where overflow would be a problem: guarding against blowing out the end of a buffer by checking whether it's being accessed past the end, and getting whether it's being accessed before the start for free.

    There's also the into instruction that threw an "overflow" exception if the overflow flag is set. Less flexible than the bound instruction, but perfect for detecting someone's doing shenanigans.

    Both of these execute very quickly in the common case (bounds are correct, no overflow), don't involve the branch predictor, and don't add any register pressure. They do require some care and handling since they call into the process' interrupt vector table, but all of that is manageable. They're not recommended for loop controls because it's relatively expensive handing the thrown exception, not least because of the IVT interaction. In kernel and drive code, however, even that expense is cheap, and well worth it.

    And then VIA fucked that up with their C3 processor.

    VIA added a special backdoor where, by executing an undefined (by Intel and AMD) instruction, you could smuggle code to a secondary processor that runs at systems management mode level and perform arbitrary actions behind the OS' back. The code you wanted to smuggle was in the x86 instruction stream as operands to bound instructions. I understand NS had a similar backdoor that taints into.

    For this and other reasons (Microsoft's and Borland's compilers never used them), AMD de-defined those instructions when designing AMD64, Intel followed suit, and even in 32-bit code any usage of them is considered virus-like behavior regardless of legitimacy.


  • Considered Harmful

    @Bulb said in WTF Bites:

    We are in the WTF bites thread and that is a pretty big bite, so let me explain. In C, the built-in integer types have always been defined as platform-specific, and libraries were expected to typedef their own aliases for any uses with specific size constraints or where specific size was needed for cross-platform compatibility.

    Unix libraries did that, and it worked fine for them¹.

    Microsoft also did. Their API always uses their own typedefs like BYTE, WORD and DWORD. But they also made the mistake of documenting, that DWORD is an unsigned long int, and somewhat liberally used that assumption in their code. Which got them fine across the 16- to 32-bit transition, because the sizes for short/int/long went from 16/16/32 to 16/32/32, and they were always using short or long, but when the 64-bit transition, that should have been 16/32/32 to 16/32/64, they has the assumption that sizeof(long) == 4 baked in too many places, so they only extended the pointer. Making them the first (or at least first common) platform where sizeof(long) < sizeof(void *)²


    ¹ Because POSIX never mentioned which type the typedefs lead to. In Linux you can even request 64 bit file size on 32 bit system just via compiler options. Unfortunately 32-bit time_t is a bit more baked in. But 64-bit systems use 64-bit type.

    ² Pointer still fits in size_t and ptrdiff_t if you really need to pass pointer values in, and C99 added the intptr_t specifically for that purpose, but some libraries occasionally assumed they can convert a pointer to long and recover it by converting back later.

    WORD salad thread is :arrows:



  • @TwelveBaud said in WTF Bites:

    And then VIA fucked that up with their C3 processor.

    VIA added a special backdoor where, by executing an undefined (by Intel and AMD) instruction, you could smuggle code to a secondary processor that runs at systems management mode level and perform arbitrary actions behind the OS' back. The code you wanted to smuggle was in the x86 instruction stream as operands to bound instructions. I understand NS had a similar backdoor that taints into.

    Interesting, but it appears that it was at least partly documented, so not really a backdoor:
    https://www.bleepingcomputer.com/news/security/backdoor-mechanism-discovered-in-via-c3-x86-processors/

    And Intel CPUs have had powerful, undocumented instructions since at least the 286 (loadall).

    @TwelveBaud said in WTF Bites:

    For this and other reasons (Microsoft's and Borland's compilers never used them)

    I think the main reasons where:

    • they needed to add new instructions
    • almost nobody (not just MS and Borland) used bound/into, so it was a prime target for elimination and opcode reuse

    Another example of seemingly-useful-but-rarely-used instructions are enter/leave. Or loop, which ended up being slower than performing the same operations manually.


  • Java Dev

    @dkf said in WTF Bites:

    @Bulb said in WTF Bites:

    It is worse than error. It is Undefined Behaviour™!

    It can't be defined either, because that would stop C from being easy to implement on a (pretty much mythical) 1s complement architecture.

    Not just one's complement, but also sign-and-magnitude.


  • BINNED

    @dkf said in WTF Bites:

    @Bulb said in WTF Bites:

    It is worse than error. It is Undefined Behaviour™!

    It can't be defined either, because that would stop C from being easy to implement on a (pretty much mythical) 1s complement architecture.

    It could still be made Implementation Defined Behavior instead of Undefined Behavior. Like a lot of other things. That would hinder those optimizations but also stop all the "I transformed your code into nasal demons" nonsense.

    And then everybody not coding for a ❄ embedded chip could assume that behavior is 2s complement, because it is.



  • @topspin That's not gonna happen. See, C++ compiler writers long for optimizations like zombies for brains.


  • Discourse touched me in a no-no place

    @Bulb said in WTF Bites:

    @topspin That's not gonna happen. See, C++ compiler writers long for optimizations like zombies for brains.

    The main optimization (at least from the perspective of C) is always to slap restrict on pointer arguments unless you specifically don't mind things aliasing. That makes a very large difference.

    As a sometime compiler author, it can be worth compiling a happy path where nothing evil happens as a separate case, with routes back to the pessimistic code if the necessary assumptions don't hold. That lets most code be very fast... at a penalty of increased code size when LTO can't prove the paths to the pessimistic code to be redundant. C compilers don't usually do this (and nor do C++ compilers) but it actually works really well as long as you aren't targeting a platform where the size of the TEXT segment matters.


  • BINNED

    @dkf C++ doesn’t (officially) have the restrict keyword. It’s also basically the only real area where FORTRAN has a (potential) performance advantage. (Even if the people who claim that “but FORTRAN is faster” don’t know that, besides some handwaving)


  • Discourse touched me in a no-no place

    @topspin It means that the compiler can assume that writes and reads don't overlap if it doesn't think that they'll definitely overlap. It's "no shenanigans" flagging.


  • BINNED

    @dkf yes, I know.



  • @Applied-Mediocrity said in WTF Bites:

    WORD salad thread

    Enjoy these two bytes! 😋



  • The default installation location for Corel PaintShop Pro is:

    C:\Program Files\
        Corel PaintShop Pro\
            Corel\
                Corel PaintShop Pro (64-bit)

    :sideways_owl:



  • Corel is an Adobe competitor. With all that implies.



  • @dkf said in WTF Bites:

    The main optimization (at least from the perspective of C) is always to slap restrict on pointer arguments unless you specifically don't mind things aliasing. That makes a very large difference.

    That's a big advantage of Rust, which effectively has restrict on all parameters everywhere. Plus another constraint that I think the backend still didn't learn to take advantage of: that if you have a pointer across a function call, and don't pass that pointer as a parameter, the function can't modify that memory, and can't touch it at all if you are modifying it.

    On the other hand Rust does not have the ‘strict aliasing rules’ kludge that C and C++ have. Since it has more accurate aliasing information via other means.


  • Java Dev

    So my mom got a set-top box for her TV. She had issues with some sort of “image appearing every 5 minutes” when watching TV. And it did happen when I was there too, because 5 minutes is short and allowed me to witness this annoyance for myself instead of just having old person badly explaining issue.

    And I recognized it as a screensaver of the DVD player variety with a bouncing logotype. So I went into the settings and discovered there was a setting for screensaver. And it had somehow set itself to activate after 5 minutes.

    So, my questions are: Why the fuck is there even an option for a screensaver on a SET-TOP BOX? And why does it (appear to) default to 5 minutes? Does the makers of the box not understand what it is and how people interact with it? Who the fuck sits down to watch TV and changes channels or otherwise interacts with such a device every couple minutes? Because it activating is based off nobody pressing a button on the remote. Watching TV? Nah, that’s obviously nobody actively using the device and therefore the screensaver must turn on. Because it is a simple model its only feature is watching TV. What else are you gonna do? Browse the settings menu?

    It does have the usual power-saving setting of auto-turnoff like modern TVs got too, which does make a bit more sense. But also a screensaver? Just, why?


  • Notification Spam Recipient

    @Atazhaia said in WTF Bites:

    got a set-top box

    Name and shame!


  • Java Dev

    @Tsaukpaetra Quick look at the cable company site and I think it's Arris VIP 4302.



  • @Atazhaia said in WTF Bites:

    But also a screensaver? Just, why?

    Maybe it was supposed to activate only when the screen contents are static. Except the coder responsible for it went "hmm, detecting changes looks complicated. So a return false; stub will do for now. :kneeling_warthog: "



  • @Gern_Blaanston said in WTF Bites:

    The default installation location for Corel PaintShop Pro is:

    C:\Program Files\
        Corel PaintShop Pro\
            Corel\
                Corel PaintShop Pro (64-bit)

    :sideways_owl:

    But that seems to miss a lot!

    C:\Program Files\\
        Corel PaintShop Pro\\
            Corel\\
                Corel PaintShop Pro (64-bit)\\
                      Current Version\\
                            Corel\\
                                   PaintShop\\
                                          Pro\\
                                                bin

Log in to reply