<p>Many programming languages, including C89, specify that the behavior of a program in terms of sequentially executed steps, whose behavior is in turn defined in terms of the program's state when the step is executed, without regard for any program states that may have occurred in the past or might occur in the future. In such a language, once an implementation starts executing a program fragment like:</p>
<pre><code>extern unsigned short arr[65537];
unsigned test(unsigned q)
{
  unsigned short i=0;
  while (arr[i] != q)
    i++;
  if (q &lt; 65536)
    arr[q] = 2;
  return i;
}
</code></pre>
<p>program actions would be limited to reading and writing to <code>i</code> or reading the first 65536 elements of <code>arr</code> unless or until it reached a state where <code>arr[i]</code> was equal to <code>q</code>.</p>
<p>In some situations, it may be difficult for a compiler to determine whether a loop will terminate, but easy for a compiler to recognize that none of the actions within a loop interact in any way with any other program actions which would be performed after the loop executed.</p>
<p>Execution of such loops could never serve any useful purpose in cases where they terminate, and would generally not be useful in cases where they fail to terminate, and it would thus be advantageous to eliminate them. In order to avoid having to write detailed rules about when compilers may eliminate such loops, some language standards simply treat any failure of a side-effect-free loop to terminate as &quot;anything can happen&quot; Undefined Behavior.</p>
<p>This allows some useful transformations &quot;as-if&quot; rules, but also allows compilers to turn code that would have been memory safe (and thus free of Arbitrary Code Execution exploits) if executed as described, or even if loops had been <em>cleanly</em> omitted, with code that is no longer memory safe and could thus be exploited by a malefactor to execute arbitrary code.</p>
<p>Such a compiler, given the above code, might omit both the loop and the following <code>if</code> test, performing an unconditional store to <code>arr[q]</code> even if <code>q</code> was larger than 65535.</p>
<p>When processing tasks that involve input from potentially untrustworthy sources, and are required to be free of Arbitrary Code Execution exploits, what advantages are there--aside from saving a little ink in a language specification--to treating endless loops as &quot;anything can happen&quot; Undefined Behavior rather than specifying that a compiler need only treat a loop or other region of code with a <em>single statically reachable exit</em> as sequenced relative to surrounding code if <em>some individual action</em> (other than a branch) would be sequenced likewise?</p>
<p>Treating endless loops as &quot;anything can happen&quot; UB may simplify the design of compilers <em>specialized for tasks that will never receive input from untrustworthy sources</em>.</p>
<p>But for other tasks, it would seem to allow a compiler fewer optimization opportunities than would be available in a language where programmers could rely upon seemingly-side-effect-free loops that might fail to terminate having no side effects beyond possibly causing program execution to get &quot;stuck&quot;.</p>
<p>What advantages, if any, does treating endless loops as &quot;anything can happen&quot; UB, as opposed to allowing more limited reordering, offer when processing tasks involving potentially untrustworthy inputs?</p>

<optimization><undefined-behavior><loops>
Pros and cons of treating endless loops as "anything can happen" UB, versus merely allowing limited reordering
CC BY-SA 4.0
2023-11-29T00:26:02.623Z
**Comments have been [moved to chat](https://chat.stackexchange.com/rooms/149976/discussion-on-question-by-supercat-pros-and-cons-of-treating-endless-loops-as-a); please do not continue the discussion here.** Before posting a comment below this one, please review the [purposes of comments](/help/privileges/comment). Comments that do not request clarification or suggest improvements usually belong as an [answer](/help/how-to-answer), on [meta], or in [chat]. Comments continuing discussion may be removed.
lyxal
CC BY-SA 4.0
2024-07-11T20:59:50.510Z
Same as with many questions about UB. Pros: Allows more optimizations, Cons: Can cause unexpected behaviour.
CoffeeTableEspresso
CC BY-SA 4.0
2024-07-11T22:13:03.770Z
@CoffeeTableEspresso: It primarily allows optimizations in programs that are not written in such a way as to to have defined behavior for all possible inputs, i.e. programs that either would be allowed to behave in completely arbitrary fashion, including e.g. facilitating Arbitrary Code Execution Attacks, for some inputs, or thjat are erroneous in the dialect being processed.  Such optimizations may be useful in a few specialized kinds of applications, but only seem useful because they often happen to generate machine code that behaves tolerably for all inputs.
supercat
CC BY-SA 4.0
2024-07-12T18:14:07.227Z
@supercat you seem to be forgetting that there are many layers of optimizations applied to code. Just because these things don't show up directly in source code doesn't mean they're not useful for optimization.
CoffeeTableEspresso
CC BY-SA 4.0
2024-07-12T18:47:33.893Z
@CoffeeTableEspresso: If a programmer who only cares about the value produced by an expression when `int1*int2` falls within the range of `int` writes the expression as `(int)(1u*int1*int2)/int3`, an optimizer that knows that `int2` will be 30 and `int3` will be 15 would be unable to use that knowledge to simply the expression to `int1*2`, but if the programmer written the expression as simply `int1*int2/int3`, the optimizer would have been perform that optimization which, in isolation, would make the code more efficient while still meeting requirements, but there would be no way of knowing...
supercat
CC BY-SA 4.0
2024-07-12T18:50:55.057Z
...whether that optimization might end up being combined with other transforms in ways that would facilitate arbitrary code execution attacks.  The only reason the useful optimization could be applied was that the code was written in a way which would not prevent dangerous transforms.  The "efficiency gains" from optimizations are often greater in cases where they eliminate operations that are necessary to meet requirements than in cases where all required operations are retained.
supercat
CC BY-SA 4.0
2024-08-13T03:54:20.813Z
I assume you mean "*only* allowing limited reordering"?
Karl Knechtel
CC BY-SA 4.0
2024-08-13T15:04:50.007Z
@KarlKnechtel: I changed it to "merely" allowing limited reordering.  Do you think that makes it clearer?
supercat
CC BY-SA 4.0
2024-08-13T15:06:51.383Z
@KarlKnechtel: Perhaps my self-answer was a bit cynical, but I fail to see any other "pro" to such treatment, nor do I view "limit the semantics of a language so it can't express optimization problems that would be hard to solve" as being a good design objective if the end goal is supposed to be to achieve the most efficient machine code *that satisfies application requirements*.
supercat
<p>Modern compiler designs are not well equipped to handle situations where two pieces of code, if performed as written, would render each other redundant, but where omitting either would render the other essential.  It will be difficult (NP hard, actually) to determine which optimizing transforms should be performed if a decision to perform one optimizing transform might prevent an optimizer from performing what could have been a far more useful transform downstream.  It's much easier for compilers to optimize if any piece of code that would be redundant if all other parts were executed may be omitted as redundant without regard for what other parts might have also been omitted.</p>

CC BY-SA 4.0
2024-07-09T20:45:14.840Z
2024-07-09T20:45:14.840Z
supercat
<blockquote>
<p>what advantages are there--aside from saving a little ink in a language specification--to treating endless loops as &quot;anything can happen&quot; Undefined Behavior</p>
</blockquote>
<p>&quot;Little ink&quot; is not a side effect: it's the whole point. Where is it written that endless loops are undefined behaviour? In the language <em>standard</em> specification.</p>
<p>Put yourself in the shoes of the language standard <em>person</em> responsible for writing a unique standard that codifies the common denominator of several compiler behaviours, as explained by various compiler representatives that also are part of the standard committee. You ask each representant about the specific things that each compiler does. The replies are:</p>
<ul>
<li><strong>Compiler 1:</strong> We do not do any optimization. The standard must accommodate this behaviour;</li>
<li><strong>Compiler 2:</strong> We detect endless loops and optimize this to no op. Sometimes. The standard must accommodate this behaviour;</li>
<li><strong>Compiler 3:</strong> We are a decade long project, where hundred programmes worked, some long gone, and our compiler does several dozen optimization passes, some of these passes are repeated in an indeterminate, chaotic order. The compiler passes are so complex that someone actually proved the optimization passes themselves are Turing complete. The standard must accommodate this behaviour.</li>
</ul>
<p>Let's summarize this as:</p>
<div class="s-table-container"><table class="s-table">
<thead>
<tr>
<th>Compiler 1</th>
<th>Compiler 2</th>
<th>Compiler 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>No optimizations</td>
<td>We optimize to no-op, sometimes</td>
<td>We do eldritch things</td>
</tr>
</tbody>
</table></div>
<p>Now, let's rewrite this summary in a more formal language, appropriate for a published standart.</p>
<div class="s-table-container"><table class="s-table">
<thead>
<tr>
<th>Compiler 1</th>
<th>Compiler 2</th>
<th>Compiler 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Defined behaviour</td>
<td>Implementation-defined behavior</td>
<td>Indescriptible <strike>horror</strike> behaviour</td>
</tr>
</tbody>
</table></div>
<p>These are <em>not</em> consistent behaviors, yet it is <em>still your responsability</em> to write down a consistent description that encompasses all these behaviors for the standard publication.</p>
<p>Basically, you need to find the intersection of:</p>
<p><span class="math-container">$$ Know \cap Knowable \cap Unknowable $$</span></p>
<p>And that's easy. You only need to <a href="https://eel.is/c++draft/defns.undefined" rel="nofollow noreferrer">remember that</a>:</p>
<blockquote>
<p><strong>3.63 undefined behavior</strong></p>
<p>behavior for which this document imposes no requirements</p>
</blockquote>
<p>Done.</p>
<p>You always can make <em>your</em> compiler to have no undefined behaviour, and also can formalize a new standart where there is no undefined behaviour. But C/C++ standards need to have things like defined, implementation-defined and undefined behaviour because it's a result of a committee tasked with two seemly opposite objectives:</p>
<ul>
<li>To have <em>one</em> standart;</li>
<li>That codifies <em>several</em> practices.</li>
</ul>

CC BY-SA 4.0
2024-08-15T13:01:48.393Z
2024-08-15T13:01:48.393Z
André L F S Bacci
CC BY-SA 4.0
2024-08-15T14:54:48.590Z
Is the goal of the Standard to say what programmers must do to work with even the most rubbish implementations, or to say what implementations should do to avoid needless incompatibility with existing programs and practices?  If authors of #3 block the latter, then should happen is that the Standard should specify that an implementation liek #3 #3 may do *anything whatsoever* provided it predefines macro `__HOPE_FOR_THE_BEST`, and for programs which are required to actually work  to start with `#ifdef __HOPE_FOR_THE_BEST`/`#error Use a non-rubbish compiler configuration!`/`#endif`.
supercat
CC BY-SA 4.0
2024-08-15T14:59:55.357Z
Alternatively, the C++ standard could print the part about it *not describing any category of conformance for programs* in 48-point type, with a note that it passes no judgment as to whether implementations intended to be suitable for various tasks should behave usefully in more ways relevant to those tasks than mandated by the Standard, and the C Standard could fix the recursive "If the Standard says an action invokes undefined behavior...the behavior is undefined" with "...the Standard waives jurisdiction", with a footnote similar to the one described above.
supercat
CC BY-SA 4.0
2024-08-15T15:27:33.510Z
It would be nice if the authors of clang and gcc would publish documents fully and accurately describing the language they actually process, *in a manner that doesn't define the behavior of any cases they cannot be reasonably expected to process 100% reliably*.  While "hope for the best" may seem overly snarky, and might be unfair for a compiler whose authors prioritized correctness, but I don't think it unreasonable to only regard as trustworthy compilers whose maintainers respond to bug reports by either fixing, *or at least documenting*, corner cases where they generate erroneous code.
supercat
CC BY-SA 4.0
2024-08-16T05:58:38.593Z
Eh, this doesn’t feel all that convincing. There is nothing specific to loops here, the argument could be made for just about anything. One could equally justify having a completely blank standard this way. Surely there must be something *specific to loops* that justifies keeping them UB.
MisterMiyagi
CC BY-SA 4.0
2024-08-16T12:51:50.903Z
Is this how things actually played out with C or C++? My impression was the other way around: I don't recall compilers attempting to optimize out infinite loops until after the standard added language to permit it. If they had done so before, I think it would have been properly seen as a bug, as earlier standard versions had no such language. So in this case, I don't think "UB" was specified in order to accommodate a range of existing implementation behavior, but rather to deliberately grant new freedom.
Nate Eldredge
# Pros and cons of treating endless loops as "anything can happen" UB, versus merely allowing limited reordering
Posted by: username_0 on 2023-11-28
Many programming languages, including C89, specify that the behavior of a program in terms of sequentially executed steps, whose behavior is in turn defined in terms of the program's state when the step is executed, without regard for any program states that may have occurred in the past or might occur in the future. In such a language, once an implementation starts executing a program fragment like:
extern unsigned short arr[65537];
unsigned test(unsigned q)
{
  unsigned short i=0;
  while (arr[i] != q)
    i++;
  if (q < 65536)
    arr[q] = 2;
  return i;
}

program actions would be limited to reading and writing to i or reading the first 65536 elements of arr unless or until it reached a state where arr[i] was equal to q.
In some situations, it may be difficult for a compiler to determine whether a loop will terminate, but easy for a compiler to recognize that none of the actions within a loop interact in any way with any other program actions which would be performed after the loop executed.
Execution of such loops could never serve any useful purpose in cases where they terminate, and would generally not be useful in cases where they fail to terminate, and it would thus be advantageous to eliminate them. In order to avoid having to write detailed rules about when compilers may eliminate such loops, some language standards simply treat any failure of a side-effect-free loop to terminate as "anything can happen" Undefined Behavior.
This allows some useful transformations "as-if" rules, but also allows compilers to turn code that would have been memory safe (and thus free of Arbitrary Code Execution exploits) if executed as described, or even if loops had been cleanly omitted, with code that is no longer memory safe and could thus be exploited by a malefactor to execute arbitrary code.
Such a compiler, given the above code, might omit both the loop and the following if test, performing an unconditional store to arr[q] even if q was larger than 65535.
When processing tasks that involve input from potentially untrustworthy sources, and are required to be free of Arbitrary Code Execution exploits, what advantages are there--aside from saving a little ink in a language specification--to treating endless loops as "anything can happen" Undefined Behavior rather than specifying that a compiler need only treat a loop or other region of code with a single statically reachable exit as sequenced relative to surrounding code if some individual action (other than a branch) would be sequenced likewise?
Treating endless loops as "anything can happen" UB may simplify the design of compilers specialized for tasks that will never receive input from untrustworthy sources.
But for other tasks, it would seem to allow a compiler fewer optimization opportunities than would be available in a language where programmers could rely upon seemingly-side-effect-free loops that might fail to terminate having no side effects beyond possibly causing program execution to get "stuck".
What advantages, if any, does treating endless loops as "anything can happen" UB, as opposed to allowing more limited reordering, offer when processing tasks involving potentially untrustworthy inputs?
**Tags:** optimization undefined-behavior loops
> **Comments:**
> **username_1**: **Comments have been [moved to chat](https://chat.stackexchange.com/rooms/149976/discussion-on-question-by-supercat-pros-and-cons-of-treating-endless-loops-as-a); please do not continue the discussion here.** Before posting a comment below this one, please review the [purposes of comments](/help/privileges/comment). Comments that do not request clarification or suggest improvements usually belong as an [answer](/help/how-to-answer), on [meta], or in [chat]. Comments continuing discussion may be removed.  
> **username_2**: Same as with many questions about UB. Pros: Allows more optimizations, Cons: Can cause unexpected behaviour.  
> **username_0**: @CoffeeTableEspresso: It primarily allows optimizations in programs that are not written in such a way as to to have defined behavior for all possible inputs, i.e. programs that either would be allowed to behave in completely arbitrary fashion, including e.g. facilitating Arbitrary Code Execution Attacks, for some inputs, or thjat are erroneous in the dialect being processed.  Such optimizations may be useful in a few specialized kinds of applications, but only seem useful because they often happen to generate machine code that behaves tolerably for all inputs.  
> **username_2**: @supercat you seem to be forgetting that there are many layers of optimizations applied to code. Just because these things don't show up directly in source code doesn't mean they're not useful for optimization.  
> **username_0**: @CoffeeTableEspresso: If a programmer who only cares about the value produced by an expression when `int1*int2` falls within the range of `int` writes the expression as `(int)(1u*int1*int2)/int3`, an optimizer that knows that `int2` will be 30 and `int3` will be 15 would be unable to use that knowledge to simply the expression to `int1*2`, but if the programmer written the expression as simply `int1*int2/int3`, the optimizer would have been perform that optimization which, in isolation, would make the code more efficient while still meeting requirements, but there would be no way of knowing...  
> **username_0**: ...whether that optimization might end up being combined with other transforms in ways that would facilitate arbitrary code execution attacks.  The only reason the useful optimization could be applied was that the code was written in a way which would not prevent dangerous transforms.  The "efficiency gains" from optimizations are often greater in cases where they eliminate operations that are necessary to meet requirements than in cases where all required operations are retained.  
> **username_3**: I assume you mean "*only* allowing limited reordering"?  
> **username_0**: @KarlKnechtel: I changed it to "merely" allowing limited reordering.  Do you think that makes it clearer?  
> **username_0**: @KarlKnechtel: Perhaps my self-answer was a bit cynical, but I fail to see any other "pro" to such treatment, nor do I view "limit the semantics of a language so it can't express optimization problems that would be hard to solve" as being a good design objective if the end goal is supposed to be to achieve the most efficient machine code *that satisfies application requirements*.  
---
## 2 answers
### Answer by username_0 on 2024-07-09. Score: 1
Modern compiler designs are not well equipped to handle situations where two pieces of code, if performed as written, would render each other redundant, but where omitting either would render the other essential. It will be difficult (NP hard, actually) to determine which optimizing transforms should be performed if a decision to perform one optimizing transform might prevent an optimizer from performing what could have been a far more useful transform downstream. It's much easier for compilers to optimize if any piece of code that would be redundant if all other parts were executed may be omitted as redundant without regard for what other parts might have also been omitted.
---
### Answer by username_4 on 2024-08-15. Score: 1
> what advantages are there--aside from saving a little ink in a language specification--to treating endless loops as "anything can happen" Undefined Behavior

"Little ink" is not a side effect: it's the whole point. Where is it written that endless loops are undefined behaviour? In the language *standard* specification.

Put yourself in the shoes of the language standard *person* responsible for writing a unique standard that codifies the common denominator of several compiler behaviours, as explained by various compiler representatives that also are part of the standard committee. You ask each representant about the specific things that each compiler does. The replies are:

* **Compiler 1:** We do not do any optimization. The standard must accommodate this behaviour;
* **Compiler 2:** We detect endless loops and optimize this to no op. Sometimes. The standard must accommodate this behaviour;
* **Compiler 3:** We are a decade long project, where hundred programmes worked, some long gone, and our compiler does several dozen optimization passes, some of these passes are repeated in an indeterminate, chaotic order. The compiler passes are so complex that someone actually proved the optimization passes themselves are Turing complete. The standard must accommodate this behaviour.

Let's summarize this as:

| Compiler 1 | Compiler 2 | Compiler 3 |
| --- | --- | --- |
| No optimizations | We optimize to no-op, sometimes | We do eldritch things |

Now, let's rewrite this summary in a more formal language, appropriate for a published standart.

| Compiler 1 | Compiler 2 | Compiler 3 |
| --- | --- | --- |
| Defined behaviour | Implementation-defined behavior | Indescriptible horror behaviour |

These are *not* consistent behaviors, yet it is *still your responsability* to write down a consistent description that encompasses all these behaviors for the standard publication.

Basically, you need to find the intersection of:

$$ Know \cap Knowable \cap Unknowable $$

And that's easy. You only need to [remember that](https://eel.is/c++draft/defns.undefined):

> **3.63 undefined behavior**
>
> behavior for which this document imposes no requirements

Done.

You always can make *your* compiler to have no undefined behaviour, and also can formalize a new standart where there is no undefined behaviour. But C/C++ standards need to have things like defined, implementation-defined and undefined behaviour because it's a result of a committee tasked with two seemly opposite objectives:

* To have *one* standart;
* That codifies *several* practices.
> **Comments:**
> **username_0** (2024-08-15): Is the goal of the Standard to say what programmers must do to work with even the most rubbish implementations, or to say what implementations should do to avoid needless incompatibility with existing programs and practices?  If authors of #3 block the latter, then should happen is that the Standard should specify that an implementation liek #3 #3 may do *anything whatsoever* provided it predefines macro `__HOPE_FOR_THE_BEST`, and for programs which are required to actually work  to start with `#ifdef __HOPE_FOR_THE_BEST`/`#error Use a non-rubbish compiler configuration!`/`#endif`.  
> **username_0** (2024-08-15): Alternatively, the C++ standard could print the part about it *not describing any category of conformance for programs* in 48-point type, with a note that it passes no judgment as to whether implementations intended to be suitable for various tasks should behave usefully in more ways relevant to those tasks than mandated by the Standard, and the C Standard could fix the recursive "If the Standard says an action invokes undefined behavior...the behavior is undefined" with "...the Standard waives jurisdiction", with a footnote similar to the one described above.  
> **username_0** (2024-08-15): It would be nice if the authors of clang and gcc would publish documents fully and accurately describing the language they actually process, *in a manner that doesn't define the behavior of any cases they cannot be reasonably expected to process 100% reliably*.  While "hope for the best" may seem overly snarky, and might be unfair for a compiler whose authors prioritized correctness, but I don't think it unreasonable to only regard as trustworthy compilers whose maintainers respond to bug reports by either fixing, *or at least documenting*, corner cases where they generate erroneous code.  
> **username_5** (2024-08-16): Eh, this doesn’t feel all that convincing. There is nothing specific to loops here, the argument could be made for just about anything. One could equally justify having a completely blank standard this way. Surely there must be something *specific to loops* that justifies keeping them UB.  
> **username_6** (2024-08-16): Is this how things actually played out with C or C++? My impression was the other way around: I don't recall compilers attempting to optimize out infinite loops until after the standard added language to permit it. If they had done so before, I think it would have been properly seen as a bug, as earlier standard versions had no such language. So in this case, I don't think "UB" was specified in order to accommodate a range of existing implementation behavior, but rather to deliberately grant new freedom.  
---
