1

I'm talking about the overflow flag that is used in some architectures like x86: https://en.wikipedia.org/wiki/Overflow_flag

  1. why aren't operating systems using this overflow flag to stop integer overflows?

  2. what is the usage of this overflow flag then?

  3. how does the CPU know whether we used int x or unsigned x in our program? If it doesn't then how can this flag know about any overflow? (if it only sees whether the addition of two values has become more than 32 bit, then wouldn't we only need the carry bit to tell if the addition could fit in one register or not?)

OneAndOnly
  • 388
  • 2
  • 10
  • An integer overflow might be a legitimate operation and the intended behavior of the application. It is not always a security issue. – A. Hersean Apr 26 '19 at 12:12
  • @A.Hersean so what is the usage of overflow flag in CPU? and how can adding two positive numbers and turning them into a unknown negative number be a legit operation?! – OneAndOnly Apr 26 '19 at 12:22
  • 1
    *turning them into a unknown negative number*: as far as I know, integer overflow will return a known negative number. There's no integrated RNG involved. – ThoriumBR Apr 26 '19 at 12:47
  • 1
    @ThoriumBR but who would use that? adding two large positive numbers and calculating what the negative result would be?! (yes i know you can technically calculate what the result would be) – OneAndOnly Apr 26 '19 at 12:50
  • You must understand binary notation first. There are no negative binary numbers, so computer engineers used a convention to tell when the number is negative, so in an 8-bit byte, you have 7 bits and the signal, but it's convention - the computer only sees the bits. On C you can have `unsigned char` and have all 8 bits, for example. The overflow flag is to tell you to not use the result, because some bits got carried-over to the next digit. – ThoriumBR Apr 26 '19 at 14:15
  • @ThoriumBR but thats the job of carry flag, I'm asking about the int overflow which causes adding two positive numbers to become negative ( not causing the 32 bit register to become full +1 and therefore causing a carry flag) – OneAndOnly Apr 26 '19 at 15:20
  • Please read https://forums.grsecurity.net/viewtopic.php?f=7&t=3043. – forest Apr 27 '19 at 04:10

3 Answers3

5

First, this is not an OS question but more of a compiler one. OS can only execute binary programs that contain machine instructions and that can (if they were instructed to) test the overflow flag.

So a part of the question is (IMHO): Why does not the C compilers allow to test the overflow flag?

The reason is that the language is supposed to accept (almost) any platform, and Kernighan&Ritchie decided that it would be too complex to allow to test the overflow flag from a C language program when they first designed the languages in the 1970's.

That is not really a nice gift for the programmers, because they have to use hacks to be able to know whether an operation generated an unwanted overflow (commonly test if the sign is not the expected one) without the semantics being defined in the language...

Once this is said, let us go to your questions:

  1. why aren't operating systems using this overflow flag to stop integer overflows?

    Because many programs generate overflow without errors, provided they correctly test it. They can test is either directly with assembly language, or indirectly by testing the range of the result.

  2. what is the usage of this overflow flag then?

    It is implemented at the CPU level and can be used by assembly language routines.

  3. how does the CPU know whether we used int x or unsigned x in our program? If it doesn't then how can this flag know about any overflow? (if it only sees whether the addition of two values has become more than 32 bit, then wouldn't we only need the carry bit to tell if the addition could fit in one register or not?)

    It just does not. For the addition, the instruction set reference says:

    The ADD instruction performs integer addition. It evaluates the result for both signed and unsigned integer operands and sets the CF and OF flags to indicate a carry (overflow) in the signed or unsigned result, respectively. The SF flag indicates the sign of the signed result.

    That means that it is up to the (assembly language) programmers to know whether they wanted signed or unsigned operations and to test the appropriate flag.

Serge Ballesta
  • 25,636
  • 4
  • 42
  • 84
4

why aren't operating systems using this overflow flag to stop integer overflows?

The operating system can't just forbid integer overflows, because sometimes it's not a bug but a feature. There is software out there which contains algorithms which actually rely on integer overflow behavior and would break if it would no longer work the way it does.

Users usually do not like it if their old software doesn't work anymore after an operating system update.

what is the usage of this overflow flag then?

The overflow- and carry-flags are relevant whenever you want to implement calculations with numbers which are larger than the register-width of the CPU. If you have a 32 bit CPU but want to do 64 bit arithmetics, then you need to perform multiple operations where the values of the overflow- and carry-flag are important for the subsequent steps. But only software developers who work in assembly actually need to keep that in mind. Any modern high-level programming languages generate the necessary code automatically. And that code relies on those flags, so this is another reason why you can't just forbid integer overflows.

Very few high-level programming languages actually expose information about whether or not integer overflows occurred.

how does the CPU knows whether we used int x or unsigned x in our program?

Because there are different CPU instructions for signed and unsigned integer operations. For example there is MUL for signed multiplication and IMUL for unsigned multiplication (with addition and subtraction of binary numbers there is actually no difference between signed and unsigned operations). When you are not writing assembly and use a higher programming language, then the compiler/interpreter is responsible for picking the correct instruction.

Philipp
  • 48,867
  • 8
  • 127
  • 157
  • 1
    +1 I'm sure you didn't mean to imply anything to the contrary, but although "_modern high-level programming languages generate the necessary code automatically._", they would normally still rely on the overflow/carry flags being set appropriately (and not being trapped by the OS) for the auto-generated code to work. – TripeHound Apr 26 '19 at 12:46
  • Thanks for answer! but why would for example adding two positive number and turning them into a negative number be a legit operation considering? and if its used for stuff like doing 64bit operation, then wouldn't just using the carry bit be enough? why use overflow flag?! – OneAndOnly Apr 26 '19 at 12:48
  • Ruby likes to use pointer-sized signed integers, but it also automatically promotes them to arbitrary-precision integers when they would have overflowed. Older versions would expose that as the numbers being either Fixnum instances or Bignum instances. Newer versions don't have separate classes, but you can still infer the information from the object_id of that number. – John Dvorak Apr 26 '19 at 12:55
  • @OneAndOnly The carry bit indicates unsigned integer overflow. The overflow bit does that for signed integers. – John Dvorak Apr 26 '19 at 12:55
  • @JohnDvorak exactly, and i can understand how unsigned overflow can be legit, but signed overflow e.g adding two positive number and getting a negative number, i don't understand what the use case is? because based on this logic even stack overflows can be used sometimes for legit reasons! – OneAndOnly Apr 26 '19 at 13:08
  • @OneAndOnly In TCP, you have 32 bits to indicate the packet's position in the stream. If you hit byte 0xffffffff, the next one is 0x00000000. How many bytes after 0xffffff56 is 0x00000145? If your recipient is confirming more bytes than you have sent, you have an issue. – John Dvorak Apr 26 '19 at 13:10
  • @JohnDvorak but that is an example of unsigned int, what about signed? what is the use case of having two positive number added together and resulted with a negative number?! – OneAndOnly Apr 26 '19 at 14:02
  • I don't think there's a specific use case for that. But your original question was why C doesn't make use of the overflow flag, and the answer to that question would be that checking for overflow after every addition would seriously impact performance. For the same reason, C doesn't check array boundaries even though it could. And if your question is why C doesn't provide access to the overflow flag, the answer would be a) because of portability and b) you can do the check without access to the overflow flag if you need it. – Out of Band Apr 27 '19 at 11:08
  • @OneAndOnly I do not think that these examples of legitimate use are in any way intended by the CPU manufacturers. There are specialized instructions for performing operations modulo some number, if you need it. The bottom line is that conditional jump is a slow instruction, so it couldn’t be used everywhere. – Andrew Morozko Apr 27 '19 at 15:13
1
  1. OS can't really check what the program is doing at the single add instruction level. This requires compiler to generate the conditional jump after every integer operation. That's a lot of additional conditions to check, integer operations are very common. This results in considerable performance loss, however some languages have chosen to take this deal: see python (converts integer to a larger type automatically) or java (throws an exception).
  2. Well, to detect overflows;) Overflow here means, that signed integer flipped its sign. So 0x7fffffff + 1, not 0xffffffff + 1 (that's Carry Flag). (See 3 about the signed and unsigned integers)
  3. It doesn't. Negative numbers are represented as two's compliment, so -1 in program turns into 0xffffffff on the processor. -1 + 1 == 0xffffffff + 0x00000001 == 0x00000000 (+ Carry flag set). In the unsigned land everything is the same: 4294967295 + 1 == 0xffffffff + 0x00000001 == 0x00000000 (+ Carry flag set). Signed and unsigned addition generates exactly the same assembly code. The distinction between signed and unsigned integers exists only in the compiler/interpreter, not in the CPU. Some instructions, like multiplication, have a signed and unsigned variant, but it’s the compiler’s decision which one to use, the cpu has no way to check whether it is given signed or unsigned integer.
Andrew Morozko
  • 1,759
  • 7
  • 10
  • Nice answer, though point 3 is somewhat misleading. There are machine language instructions to deal with signed integers, for example to extend a short signed integer to a larger bit size, and there's the sign flag etc, so the CPU does have a "notion" of negative integers (expressed in two's complement). The fact that signed and unsigned addition can be done with the same instruction is thanks to the clever representation of negative integers. – Out of Band Apr 27 '19 at 11:16
  • 1
    @Pascal Those instructions exist, but the CPU still doesn’t know the signs of the integers. It can perform signed and unsigned operations on signed and unsigned integers. The information about the integer’s type only exists inside the compiler, which (hopefully) uses correct kind of assembly instruction. Floats, for example, do have the sign represented explicitly, and arithmetic unit of the processor act differently depending on the sign. – Andrew Morozko Apr 27 '19 at 13:08
  • 2
    Yes, expressed this way, it's much clearer what you mean. I'd suggest extending the last sentence of your point 3 with the beginning of your comment for clarification. – Out of Band Apr 27 '19 at 14:37