PT1
error & exit
professor patrick baudisch
pt1
hasso-plattner institute
C
#include <stdio.h>
int main(void)
{ a character stored in an int = ~32 bits?
int c; should we not use a char?
int c does (one of) two things
1. carry the read character
while((c = getchar()) != EOF) { 2. carry a magic error value
putchar(c); that’s confusing, not good
fflush(stdout); // for repl
}
return 0;
} fame or shame?
<30sec brainstorming>
C
void SomeFunction(void) {
for (int i = 1; i < i_max; i++)
for (int j = 1; j < j_max; j++)
for (int k = 1; k < k_max; k++)
f(i,j,k);
}
how to handle stuff going wrong in f()?
<30sec brainstorming>
C
void SomeFunction(void) { five lines have changed = not good
int we_are_ok = true;
for (int i = 1; i < i_max && we_are_ok; i++)
for (int j = 1; j < j_max && we_are_ok; j++)
for (int k = 1; k < k_max && we_are_ok; k++)
we_are_ok = f(i,j,k);
}
how to handle stuff going wrong in f()?
<30sec brainstorming>
how to handle errors in general?
how have you handled errors in the past?
let’s see again later
<60sec brainstorming>
plan of attack
we want our main code to always assume successful execution
that makes the code short and clear
! we need the error case come back some other path
but how could one leave a block except using return…?
goto
reloaded
Original title of submission. Editor
(Niklaus Wirth) changed it to “Go To
Statement Considered Harmful”
well, maybe we can use it in a non-unbridled way…
"The unbridled (= uncontrolled; unconstrained) use of the go to statement has
as an immediate consequence that it becomes terribly hard to find a meaningful
set of coordinates in which to describe the process progress. ... The go to
statement as it stands is just too primitive, it is too much an invitation to make a
mess of one's program.“
[Edsger Dijkstra, Go To Statement Considered Harmful“
Communications of the ACM 11 (3): 147–148. (March 1968)
void SomeFunction(void) {
for (int i = 1; i < i_max; i++)
for (int j = 1; j < j_max; j++)
for (int k = 1; k < k_max; k++)
if (!f(i,j,k))
goto error;
error:
informUser();
cleanup(); totally, I’d say this is the
canonical use of goto
exit(1);
}
ok to use here?
<30sec brainstorming>
https://stackoverflow.com/questions/245742/examples-of-good-gotos-in-c-or-c
void SomeFunction(void) {
for (int i = 1; i < i_max; i++)
for (int j = 1; j < j_max; j++)
for (int k = 1; k < k_max; k++)
if (!f(i,j,k))
goto found; plus it’s always fun to shock
found: dogmatic people ☺
// do something
} personally, I’d still say ‘yes’
(brevity reduces risk of error)
ok to use here?
<30sec brainstorming>
but, you may rightfully argue that goto is too “powerful”, too confusing.
we would get by with something a bit more “controlled”…
…modern language thus offer a special “exception” construct for it,
some sort of “return-but-then-goto-one-statement-down” statement
try {
if (process_something() == problem)
throw SOME_ERROR;
}
catch (int e) {
printf(“Exception Nr. %d occured\n” e);
}
C
In C, you can implement try/throw/catch
using setjmp(), which a bit like label:
and longjmp(), which is a bit like goto
you cannot use goto because goto cannot leave a function
actually, setjmp()/longjmp() is how C++ implement its
terminating
the program
is it ok to terminate the program when you discover an error?
let’s first take a look on how we would do it…
<30sec brainstorming>
exit ::
terminates the calling process immediately.
void exit(int status)
#include <stdio.h> int main(void) {
#include <stdlib.h> char *str = "Hello World";
#include <assert.h> printf("lenght of %s = %d\n",
str, length(str));
return 0;
int length(char *string) }
{
int len = 0;
if (string == NULL) independent of whether it is ok to exit()
exit(1); one might argue that we should first
while (*string++) report the error to the user
len++;
return len;
}
how do you feel about his?
<30sec brainstorming>
#include <stdio.h> int main(void) {
#include <stdlib.h> char *str = "Hello World";
#include <assert.h> printf("lenght of %s = %d\n",
str, length(str));
return 0;
int length(char *string) }
{
int len = 0;
assert(string != NULL); if condition not met, program terminates with:
./main: main.c: 6: length:
while (*string++) Assertion `string != NULL’ failed.
len++; (macro from C stdlib /usr/include/assert.h)
return len;
}
assertion ::
a statement that a predicate (Boolean-valued function, a true–false expression) is expected to always be true at that point in the code. If an assertion evaluates to
false at run time, an assertion failure results, which typically causes the program to crash, or to throw an assertion exception.
#include <stdio.h> int main(void) {
#include <stdlib.h> char *str = "Hello World";
#include <assert.h> printf("lenght of %s = %d\n",
str, length(str));
return 0;
int length(char *string) }
{
int len = 0;
assert(string != NULL); if condition not met, program terminates with:
./main: main.c: 6: length:
while (*string++) Assertion `string != NULL’ failed.
len++; (macro from C stdlib /usr/include/assert.h)
return len;
}
how do you feel about his?
<30sec brainstorming>
productFunction.c C
#include <stdio.h>
aka “precondition”
int multiplyInterval (int i, int j) {
int product = 1;
assert(i <= j);
for ( ; i <= j; i++)
product *= i;
return product;
}
int main(void) {
printf("product of [%d, %d] = %d\n", 10, 15, multiplyInterval(10,15));
printf("product of [%d, %d] = %d\n", 17, 21, multiplyInterval(17,21));
return 0;
add assertion(s)
<30sec brainstorming>
}
int total = countNumberOfUsers();
if (total % 2 == 0) {
processEvenNumbers(total);
} else {
too much
assert( total % 2 == 1 ); // odd
processEvenNumbers(total);
}
add assertion(s)
<30sec brainstorming>
so what is a potential benefit of terminating the program this way?
easier to find the bug
why?
the longer it runs the harder it gets to find
<30sec brainstorming>
terminating
the program
ok, so, for what types of errors is it ok to terminate the program?
<30sec brainstorming>
what might that be?
programming by contract ::
1. Expect a certain condition to be guaranteed on entry by any client module that calls it: the routine's precondition—an obligation for the client, and a benefit for the supplier (the routine itself), as it frees it from
having to handle cases outside of the precondition.
defensive programming ::
intended to ensure the continuing function of a piece of software under unforeseen circumstances. Defensive programming practices are often used where high availability, safety or security is needed.
Overly defensive programming however introduces code to prevent errors that can't happen, but needs to be executed on runtime and to be maintained by the developers, thus increasing the runtime and
so, if you believe in programming by contract:
1. if error caused by contract breaking inform user & terminate.
assert() does this nicely
2. Otherwise, i.e., if error was caused by user or the user’s environment, be
patient and handle the error gracefully. Find a safe place to restart from
and try again.
getting back
to a safe spot
break
switch (expression) {
case constant-expression1:
statement1
break;
case constant-expression2:
statement2
break;
default: statement3
break;
}
you've already seen it
for (i=0; i<n; i++){
doSomething();
if (disaster)
break;
terminates the loop prematurely
doSomeMore();
}
// figure out whether regular cases or disaster
// then process accordingly
// ...
break ::
terminates the execution of statements
(used in switch, for, while, do)
let’s try out break in a non-error case
for (i=0; i<n; i++){
if (process(i) == done)
break; better than adding a Boolean
variable, especially for short
doSomeMoreProcessing();
loops very ok
}
// continue processing
// ...
example: removing trailing spaces/tabs/newlines
a table aka “array”
string length
int n; char s[] =”abc def g ”;
for (n = strlen(s) - 1; n >= 0; n--)
if (s[n] != ’ ’ && s[n] != ’\t’ && s[n] != ’\n’)
break;
s[n+1] = ’\0’;
// I guess we could have dumped the long conditional
// into the for (;exp;), but the above may actually
// read better
for (i=0; i<m; i++){
for (j=0; j<n; j++){
breaks only out of the
if (process(i,j) == done)
innermost loop,
break; (thus continues the loop
} around it)
}
if we really want out here,
only goto can do it
what might it do here?
<30sec brainstorming>
while (1)
statement
how about writing forever loops
do and breaking out of them?
statement
while (1) not really, these would be hard to read. Why?
for (;;) people look for terminating condition in
statement for( ; exp ; ) and while (exp)
! use of break forces them to search entire loop body ☹
<30sec brainstorming>
continue
// calculates sum of <= 10 numbers, skips negative numbers
# include <stdio.h>
int main() {
int i;
double number, sum = 0.0;
for(i=1; i <= 10; ++i) {
printf("Enter a n%d: ",i);
scanf("%lf",&number);
if(number < 0.0)
continue; // If user enters negative number, skip it
sum += number;
}
printf("Sum = %.2lf",sum);
return 0;
}
kinda like return, but within a loop
continue::
causes the next iteration of for, while, or do loop to begin
int copyAndReplaceSpacesWithUnderscores(char *dst, char *src)
{
int count = 0;
for ( ; *src != 0; src++, dst++) {
*dst = *src;
if (*src != ' ')
continue;
count++;
*dst = '_';
}
return count;
}
#include <stdio.h>
int main() {
int i;
for (i = 1; i <= 4; i++)
switch (i) {
case 1: printf("1\n");
case 2: printf("2\n");
continue;
how would one continue a switch?
case 3: printf("3\n");
case 4: printf("4\n");
! no continue in switch
}
(here this will continue the for)
return 0;
}
ok or a problem here?
<30sec brainstorming>
return
int strlen(char *string)
{
int len = 0;
if (string == NULL) "early return”
return -1; is this good style?
while (*string++ != 0)
len++;
return len;
}
<30sec brainstorming>
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
int retval = SUCCESS;
if (someCondition) {
if (name != null && name != "”) {
if (value != 0)
{
better
public int SomeFunction(bool cond1,
if (perms.allow(name) {
string name, int value, AuthInfo perms)
// Do Something {
} else { if (!someCondition)
reval = PERM_DENY; return BAD_COND;
}
if (name == null || name == "")
} else {
return BAD_NAME;
retval = BAD_VALUE;
} if (value == 0)
} else { return BAD_VALUE;
retval = BAD_NAME;
if (!perms.allow(name))
} return PERM_DENY;
} else {
retval = BAD_COND; // Do something
} return SUCCESS;
return retval; }
}
summary
do try out programming by contract for yourself:
1. if error caused by contract breaking inform user & terminate.
assert() does this nicely, by providing a message before exit().
2. otherwise, i.e., if error was caused by user or the user’s environment, be
patient and handle the error gracefully. Find a safe place to restart from
and try again. break, continue, return can be helpful here
3. in C++ (or Java, etc.) exceptions (try, throw, catch)
are the way to go for most of the above
today, we looked at jump commands.
these are inherently harder to read than
for() and while() thus use carefully.
recover from errors (and even ok to just capture special cases)
continue, break, return
use to recover from errors while deeply nested
return, goto, (and try/throw/catch = setjmp())
use for catastrophic error
assert(), exit()
end
error & exit
professor patrick baudisch
pt1
hasso-plattner institute
APPLE’S SSL/TLS GOTO FAIL BUG
static OSStatus SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool
isRsa, SSLBuffer signedParams, uint8_t *signature, UInt16 signatureLen)
{
/* ... */
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail; double line, result of paste/paste. Does this suggest goto is bad?
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
/* ... */ not really.
fail: just happens to be goto
SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx);
return err;
<30sec brainstorming>
} find the bug
https://blog.duncanworthy.me/swdev/ios/apple-ssl-goto-fail-bug/#more-559
other uses of goto
automated tools can generate efficient code using go-to.
e.g. table-operating parser, yacc