A Guide to Undefined Behavior in C and C++, Part 3

Also see Part 1 and Part 2.

A C or C++ implementation must perform side effecting operations in program order. For example, if a program executes these lines of code:

printf ("Hello\n")
printf ("world.\n");

It is impermissible for the implementation to print:

world.
Hello

This is obvious, but keep in mind that other things your program does can be freely reordered by the compiler. For example if a program increments some variables in one order:

a++;
b++;

a compiler could emit code incrementing them in the other order:

incl    b
incl    a

The optimizer performs transformations like this when they are thought to increase performance and when they do not change the program’s observable behavior. In C/C++, observable behaviors are those that are side effecting. You might object, saying that this reordering is in fact observable. Of course it is: if a and b are globals, a memory-mapped I/O device, a signal handler, an interrupt handler, or another thread could see the memory state where b but not a has been written. Nevertheless, the reordering is legal since stores to global variables are not defined as side-effecting in these languages. (Actually, just to make things confusing, stores to globals are side effecting according to the standard, but no real compiler treats them as such. See the note at the bottom of this post.)

With this background material out of the way, we can ask: Is a segfault or other crash a side effecting operation in C and C++? More specifically, when a program dies due to performing an illegal operation such as dividing by zero or dereferencing a null pointer, is this considered to be side effecting? The answer is definitely “no.”

An Example

Since crash-inducing operations are not side effecting, the compiler can reorder them with respect to other operations, just like the stores to a and b in the example above were reordered.  But do real compilers take advantage of this freedom? They do. Consider this code:

volatile int r;
int s;
void foo1 (unsigned y, unsigned z, unsigned w)
{
  r = 0;
  s = (y%z)/w;
}

foo1 first stores to r; this store is a side-effecting operation since r is volatile. Then foo1 evaluates an expression and stores the result to s. The right hand side of the second assignment has the potential to execute an operation with undefined behavior: dividing by zero. On most systems this crashes the program with a math exception.

Here’s what a recent version of GCC outputs:

foo1:
  pushl   %ebp
  xorl    %edx, %edx
  movl    %esp, %ebp
  movl    8(%ebp), %eax
  divl    12(%ebp)         <-- might crash the program
  movl    $0, r            <-- side effecting operation
  movl    %edx, %eax
  xorl    %edx, %edx
  divl    16(%ebp)
  popl    %ebp
  movl    %eax, s
  ret

Notice that a divl instruction — which may kill the program if the divisor is zero — precedes the store to r. Clang’s output is similar:

foo1:
  pushl   %ebp
  movl    %esp, %ebp
  movl    8(%ebp), %eax
  xorl    %edx, %edx
  divl    12(%ebp)         <-- might crash the program
  movl    %edx, %eax
  xorl    %edx, %edx
  divl    16(%ebp)
  movl    $0, r            <-- side effecting operation
  movl    %eax, s
  popl    %ebp
  ret

And Intel CC:

foo1:
  movl      8(%esp), %ecx
  movl      4(%esp), %eax
  xorl      %edx, %edx
  divl      %ecx              <-- might crash the program
  movl      12(%esp), %ecx
  movl      $0, r             <-- side effecting operation
  movl      %edx, %eax
  xorl      %edx, %edx
  divl      %ecx
  movl      %eax, s
  ret

In compiler speak, the problem here is that there are no dependencies between the first and second lines of code in foo1. Therefore, the compiler can do whatever kinds of reordering seem like a good idea to it. Of course, in the bigger picture, there is a dependency since the computation involving r will never get to execute if the RHS of the assignment into s crashes the program. But — as usual — the C/C++ standard creates the rules and the compiler is just playing by them.

Another Example

This C code calls an external function and then does a bit of math:

void bar (void);
int a;
void foo3 (unsigned y, unsigned z)
{
  bar();
  a = y%z;
}

Clang does the math before calling the function:

foo3:
  pushl   %ebp
  movl    %esp, %ebp
  pushl   %esi
  subl    $4, %esp
  movl    8(%ebp), %eax
  xorl    %edx, %edx
  divl    12(%ebp)         <-- might crash the program
  movl    %edx, %esi
  call    bar              <-- might be side effecting
  movl    %esi, a
  addl    $4, %esp
  popl    %esi
  popl    %ebp
  ret

To make a complete program we can add this code:

void bar (void)
{
  setlinebuf(stdout);
  printf ("hello!\n");
}

int main (void)
{
  foo3(1,0);
  return 0;
}

Now we can compile and run it:

regehr@john-home:~$ clang -O0 biz_main.c biz.c -o biz
regehr@john-home:~$ ./biz
hello!
Floating point exception
regehr@john-home:~$ clang -O1 biz_main.c biz.c -o biz
regehr@john-home:~$ ./biz
Floating point exception

As we predicted from looking at the assembly code for foo3(), when optimization is turned on the division instruction crashes the program before the unbuffered printf() can occur.

Why does this matter?

Hopefully the previous example made the relevance obvious, but here I’ll spell it out. Let’s say you’re debugging a difficult segfault:

  1. You add a printf() just before calling foo(), where foo() is a nasty piece of logic that contains some suspicious pointer operations. Of course — as we did in the example — you turn off buffering on stdout so that lines are actually pushed to the terminal before the program continues to execute.
  2. You run the code and see that the printf() is not reached before the program segfaults.
  3. You conclude that the crash is not due to foo(): it must have occurred in earlier code.

The inference in step 3 is wrong if some dangerous operation in foo() was moved in front of the printf() and then triggered a segfault. When you make an incorrect inference while debugging, you start walking down the wrong path and can waste a lot of time there.

Solutions

If you are experiencing this kind of problem, or are just paranoid about it, what should you do?  The obvious things to try are turning off optimizations or single-stepping through your code’s instructions.  For embedded system developers, neither of these may be feasible.

A solution that failed in a surprising way is adding a compiler barrier. That is, changing foo1 to read:

void foo2 (unsigned y, unsigned z, unsigned w)
{
  r = 0;
  asm volatile ("" : : : "memory");
  s = (y%z)/w;
}

The new line of code is a GCC idiom (also understood by Intel CC and Clang) which means “this inline asm, although it contains no instructions, may touch all of memory.” The effect is basically to artificially inject a lot of dependencies into the code, forcing all register values to be saved to RAM before the asm and to be reloaded afterward. This new code causes Intel CC and GCC to store to r before evaluating any of the RHS of s, but Clang goes ahead and emits this code:

foo1:
  pushl   %ebp
  movl    %esp, %ebp
  movl    8(%ebp), %eax
  xorl    %edx, %edx
  divl    12(%ebp)         <-- might crash the program
  movl    %edx, %eax
  xorl    %edx, %edx
  divl    16(%ebp)
  movl    $0, r            <-- side effecting operation
  movl    %eax, s
  popl    %ebp
  ret

I believe that Clang is operating correctly. The compiler barrier adds some artificial dependencies, but not enough of them to stop a divide instruction from moving ahead of the store-to-volatile.

The real solution to this problem — which is useful to understand, although it does us no good as long as we’re stuck with C and C++ — is to assign a semantics to exceptional situations such as divide by zero and dereferencing a null pointer. Once a language does this, it becomes possible to constrain the optimizer’s behavior with respect to reordering operations that are potentially dangerous. Java does a fine job at this.

A Technical Note on Side Effects in C and C++

The C99 standard states that:

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

In the C standard “object” means “variable.” The C++0x draft contains very similar language. The “modifying an object” clause is nonsensical because — in combination with sequence point semantics — it forbids the compiler from eliminating the redundant store when a program contains code like this:

x=0;
x=0;

No optimizing C or C++ compiler considers “modifying an object” to be a side-effecting behavior.

While we’re nitpicking, the “calling a function that does any of those operations” bit is also widely ignored, due to function inlining. The code inside the inlined function may be side effecting, but the call itself is never treated as such.

Update from August 19, 2010

CompCert is the most practical verified C compiler anyone has yet developed, it is an amazing piece of work. I spent some time trying to get it to produce object code of the kind that I’ve shown above — where a dangerous operation is moved in front of a side-effecting operation — and was unable to. Xavier Leroy, the CompCert project lead, had this to say:

Actually, you're putting your finger on a relatively deep semantic
issue.

We all agree that C compilers do not have to preserve undefined
behaviors: a source program that accesses an array out of bounds
doesn't always crash when run.  One can regret it (many security holes
would be avoided if it were so), but we sort of agree that the cost of
preserving undefined behaviors would be too high (array bound checks,
etc).

Now, a compiler that preserves defined behaviors has two choices:

1- If the source program exhibits an undefined behavior, the compiled
code can do absolutely whatever it pleases.

2- If the source program exhibits an undefined behavior, the compiled
code executes like the source program up to the undefined behavior
point, with the same observable effects, then is free to do whatever
it wants (crashing or continuing with arbitrary effects).

If I read your post correctly, you've observed choice 1 and point out
that choice 2 would be not only more intuitive, but also more helpful
for debugging.  I think I agree with you but some compiler writers
really like choice 1...

For CompCert, the high-level correctness theorems I proved are all of
the first kind above: if the source program goes wrong, nothing is
guaranteed about the behavior of the compiled code.  It happens,
however, that the stepwise simulation lemmas I build on actually prove
property 2 above: the compiled code cannot crash "earlier" than the
source code, and will produce the same pre-crash observables, then
possibly more.  So maybe I should strengthen my high-level correctness
theorems...

Concerning what's observable, I agree with you that the C standard
talks nonsense: writes to nonvolatile objects are of course not
observable, neither are function calls by themselves (only the system
calls the function might invoke).

So there we have it: CompCert is provably immune to these problems, but in a way that is perhaps hacky. In a subsequent email Xavier mentioned that he’s thinking about strengthening the top-level CompCert theorems to show this cannot happen. Cool!

Borah Peak in 30 Hours

My colleague Dave Hanscom wanted to climb Borah Peak, Idaho’s 12,668′ high point. He had climbed this mountain once before and I’d barely even heard of it. We left Salt Lake City around 1pm on Tuesday of this week. The drive up to Mackay, ID took 4.5 hours and is classic intermountain west: wide, dry valleys with mountains always in sight. We grabbed burgers in tiny Mackay (pronounced “mackey”) and proceeded 20 miles north to the Borah Peak trailhead. Just before reaching the trailhead we saw what at first looked like a road running across the foothills just above the valley floor; this turned out to be the scarp from a 1983 earthquake that increased Borah’s elevation by seven feet. The trailhead has some great campsites along a small stream. Dave set up his tent and I rolled out my bivy bag. With plenty of remaining daylight, we got our packs ready for an early start on Wednesday. Using binoculars we saw a guy pretty high on the trail; we hoped he’d make it down before dark so we could ask about trail conditions, but he was moving very slowly and we eventually gave up. The sunset was beautiful; it would have been a perfect day to be on the mountain.

My alarm went off at 5am; we made cereal and coffee and got moving by 6:00. It was around 50 degrees — nice to not need gloves at breakfast! The warm night also meant that snow up on the mountain would be soft, so we didn’t hike with axes. The forecast was for afternoon thunderstorms so we needed to get moving and keep moving. The trail starts out gently but quickly becomes steep: it gains a mile of elevation in just 3.5 miles of travel.

Dave is a strong hiker and I’m in good shape so we made pretty fast progress up to “chicken out ridge” where the class 3 scrambling begins. The scrambling had some intimidating exposure but wasn’t bad overall. We crossed a slightly pucker-inducing snow bridge and moved along quickly to Borah’s summit pyramid. Here we finally started to feel the effects of altitude and slowed down considerably, getting to the top at 10:00. We had hoped for a climb rate of 1500′ per hour and didn’t quite meet that goal, but our actual rate of 1300’/hour seemed reasonable given the altitude and scrambling problems.

We stuck around on the summit until 10:30 when the beginnings of some cloud activity made it clear that it was time to leave. The initial part of the descent was painful due to steep gravel and scree, but after that we made good progress. By noon we were down near treeline and it was definitely time to be getting off of the mountain: the storm clouds were building up in a menacing way.  By 1pm we were back at Dave’s car and just in time, we had barely closed the doors when some serious thunder started to happen and we got a bit of hail. We stopped for coffee and sandwiches in Pocatello and were home by 7pm. It was an excellent trip.

A few other groups were on the mountain. One group of four started at 3am; two summited and two turned back at chicken-out ridge. Another group of four started at 5am and they were still fairly high on the mountain when the storm began, hopefully they got off the scrambling part before the rock got wet. One guy started at 8:00 and finished probably a half-hour before we did: a fast hiker indeed.

Running an Electronic Program Committee Meeting

Computer science — at least in the areas that I work in — is conference-driven. Since journals go unread, it’s important that conference program committees make good decisions about which papers to accept. The established way to do this is to hold a program committee (PC) meeting: the entire committee holes up in a highly air conditioned meeting room for a day and argues about every paper that’s not an obvious reject (occasionally we also manage to avoid arguing about papers that are obvious accepts).

The cost of in-person PC meetings is substantial in terms of money and time. Just as a random anecdote, last January I attended the EuroSys PC meeting in Paris; this trip used up enough time and money that I couldn’t easily justify traveling to France again a few months later to go to the conference. This was a bummer since I’d have loved to go (the volcano in Iceland, however, would have stopped me from getting home in a timely fashion).

The alternative to the in-person meeting is to have a remote meeting. This post is not about comparing the merits of in-person vs. remote meetings, but rather about how to run a remote meeting: a topic that is fresh on my mind since I just ran a remote PC meeting for the Sensor Networks track at the Real Time Systems Symposium, where we evaluated 30 submissions. I’ve previously run a couple of in-person meetings so I have some basis for comparison.

The remote PC meeting could be designed to emulate an in-person meeting, for example using Skype or a conference call. I didn’t choose this option for my RTSS track for several reasons. First, it would waste people’s time: each PC member reviewed only 20% of the papers (normally this percentage would be higher, but since I had been warned to expect a large number of submissions, I invited a lot of people to be on the committee). Second, 20 different voices on a conference call is difficult; generally I’d consider five or six to be a reasonable maximum. Third, spending all day on a conference call just sounds unspeakably boring; I doubted that anyone (including me) could keep paying attention to disembodied voices for that long. I think a conference call would work fine for a small committee evaluating a small number of submissions. I’ve never tried a Skype session with anywhere close to 20 people and would appreciate stories or advice from people who have done this.

If the entire PC is not going to get together (actually or virtually) the alternative is for the group of people who reviewed each paper to discuss it and make a decision. One possibility would be to emulate the journal review process: the program chair simply reads all reviews for each paper, and makes the decision. My sense is that this would be unsatisfying for people, who generally enjoy the back and forth of arguing over papers. People like to come to a consensus. This discussion could be done in email, on the phone or Skype, in a text channel like IRC, or on a message board.

Since the conference software RTSS uses supported a decent message board, we used that. The process that I put to the PC members was that the reviewers for a paper should read all the reviews and then someone should propose a decision. From there, the discussion could proceed with others either agreeing or disagreeing. If nobody chimed in about a particular paper, I started asking leading questions: “Joe you didn’t like this paper but your review is marked low-confidence. Do you stand by the ‘reject’ recommendation?” I only had to do this in a few cases.

The only misgiving I had about this process was that it might give too much weight to the first comment about a paper. But, as it turned out, people felt free to argue with the first commenter and I think the decisions reached ended up being generally good. Of course, in-person PC meetings suffer from substantially more severe versions of this kind of unfairness where the loudest, most persuasive people can have an inordinate effect on the decisions that are made. An alternative would have been to ask a specific person (for example the most positive or most negative reviewer for a paper) to propose a course of action, but I didn’t think of this soon enough.

In principle, a conference accepts all acceptable papers and rejects the rest. In practice, however, the number of available presentation slots at the conference has a strong influence on the number of accepted papers. When the PC meeting is in-person, global constraints like this are easy to account for: once we notice that too few papers are being accepted, we can try to start making more generous decisions. When the PC meeting is fragmented into a lot of individual meetings, there is less global information and we risk accepting too few or too many papers. For whatever reasons, this didn’t happen at my track meeting and we accepted just slightly more than the expected 20%-25% of submitted papers.

I’m not claiming that a remote meeting is preferable or saying that I don’t like in-person PC meetings. It’s just that looking forward, I’m probably going to attend at most a couple of in-person meetings per year and turn down any invitations beyond that. I’d expect other rational academics to do the same. If oil prices rise significantly, the in-person meeting will die completely and the entire conference system will be in jeopardy.

In summary, I was happy with the remote PC meeting process. It puts a significantly larger burden on the program chair to help things run smoothly, and it also (potentially) gives the chair much more control over the decisions that are made. The best thing about the remote meeting is that it saved a lot of time and money for PC members, several of whom told me they wouldn’t have agreed to be on the PC if that meant attending an in-person PC meeting. I doubt that the resulting decisions about papers were worse than, or even much different than, the decisions that would have been made in person.

Michael Ernst’s advice on running a PC meeting is great, though I don’t buy his claim that the low bandwidth of the phone or a message board is a limiting factor in arriving at good decisions. A typical in-person PC meeting is actually a fairly low-bandwidth activity with quite a bit of irrelevant discussion and frittering away of time, unless the chair is very much on top of things. Also, by using a message board people can take time to compose well thought-out arguments as opposed to saying the first thing that comes to mind.

Is Attending Meetings a Signaling Behavior?

Humans and other animals spend a lot of time engaging in signaling behaviors: dressing or acting in certain ways in order to send signals to others. Some signals — a peacock’s tail or a Ferrari — are expensive precisely to show that the signaling organism can afford the cost of sending the signal. Signaling can explain aspects of human behavior where it’s not initially obvious that it applies. For example, it has been argued that social drinking serves as a signal of trustworthiness because alcohol permits individuals to give up some control over their actions in a verifiable way.

Lately I’ve been puzzling over my field’s love affair with in-person meetings, including grant proposal review panels, program committee meetings, conferences, PI meetings, site visits, and more. To be sure, face time is useful, but is it really necessary for high-caliber academics to be platinum-level frequent flyers? Does this serve our goals of performing high-impact research and teaching students? (I’m not platinum-level but I know a lot of people who are, and I’m usually some sort of “medallion” level on Delta.) The cost in terms of time and money is substantial, especially for those of us with families.

Historically, academics have communicated primarily through letters and manuscripts, supplemented by occasional travel. Why, then, do we need to travel so much when our telecommunications technology is so good? Lately I’ve been wondering if part of the explanation is that travel, because it is expensive, is a signaling behavior. “Look,” we are saying, “I have enough grant money to attend eight meetings a year, and moreover I have such an army of students working for me that the loss of personal productivity this travel entails is no big deal.”

Are academics status conscious? We are. Inside a community it is perfectly well-known whose work is deep and significant, whose is shallow, who leads the trends and who follows, and even who struck it rich with a startup and who’s in trouble for sleeping with a student. We love to talk about this stuff, and I’d also guess that we’re not above using excessive travel as a status signal.

A Guide to Undefined Behavior in C and C++, Part 2

Also see Part 1 and Part 3.

When tools like the bounds checking GCC, Purify, Valgrind, etc. first showed up, it was interesting to run a random UNIX utility under them. The output of the checker showed that these utility programs, despite working perfectly well, executed a ton of memory safety errors such as use of uninitialized data, accesses beyond the ends of arrays, etc. Just running grep or whatever would cause tens or hundreds of these errors to happen.

What was going on? Basically, incidental properties of the C/UNIX execution environment caused these errors to (often) be benign. For example, blocks returned by malloc() generally contain some padding before and/or after; the padding can soak up out-of-bounds stores, as long as they aren’t too far outside the allocated area. Was it worth eliminating these bugs? Sure. First, an execution environment with different properties, such as a malloc() for an embedded system that happens to provide less padding, could turn benign near-miss array writes into nasty heap corruption bugs. Second, the same benign bugs could probably, under different circumstances, cause a crash or corruption error even in the same execution environment. Developers generally find these kinds of arguments to be compelling and these days most UNIX programs are relatively Valgrind-clean.

Tools for finding integer undefined behaviors are less well-developed than are memory-unsafety checkers. Bad integer behaviors in C and C++ include signed overflow, divide by zero, shift-past-bitwidth, etc. These have become a more serious problem in recent years because:

  • Integer flaws are a source of serious security problems
  • C compilers have become considerably more aggressive in their exploitation of integer undefined behaviors to generate efficient code

Recently my student Peng Li implemented a checking tool for integer undefined behaviors. Using it, we have found that many programs contain these bugs. For example, more than half of the SPECINT2006 benchmarks execute integer undefined behaviors of one kind or another. In many ways the situation for integer bugs today seems similar to the situation for memory bugs around 1995. Just to be clear, integer checking tools do exist, but they do not seem to be in very widespread use and also a number of them operate on binaries, which is too late. You have to look at the source code before the compiler has had a chance to exploit — and thus eliminate — operations with undefined behavior.

The rest of this post explores a few integer undefined behaviors that we found in LLVM: a medium-sized (~800 KLOC) open source C++ code base. Of course I’m not picking on LLVM here: it’s very high-quality code. The idea is that by looking at some problems that were lurking undetected in this well-tested code, we can hopefully learn how to avoid writing these bugs in the future.

As a random note, if we consider the LLVM code to be C++0x rather than C++98, then a large number of additional shift-related undefined behaviors appear. I’ll talk about the new shift restrictions (which are identical to those in C99) in a subsequent post here.

I’ve cleaned up the tool output slightly to improve readability.

Integer Overflow #1

Error message:

UNDEFINED at <BitcodeWriter.cpp, (740:29)> :
Operator: -
Reason: Signed Subtraction Overflow
left (int64): 0
right (int64): -9223372036854775808

Code:

int64_t V = IV->getSExtValue();
if (V >= 0)
  Record.push_back(V << 1);
else
  Record.push_back((-V << 1) | 1);  <<----- bad line

In all modern C/C++ variants running on two’s complement machines, negating an int whose value is INT_MIN (or in this case, INT64_MIN) is undefined behavior. The fix is to add an explicit check for this case.

Do compilers take advantage of this undefined behavior?  They do:

[regehr@gamow ~]$ cat negate.c
int foo (int x) __attribute__ ((noinline));
int foo (int x)
{
  if (x < 0) x = -x;
  return x >= 0;
}

#include <limits.h>
#include <stdio.h>

int main (void)
{
  printf (“%d\n”, -INT_MIN);
  printf (“%d\n”, foo(INT_MIN));
  return 0;
}
[regehr@gamow ~]$ gcc -O2 negate.c -o negate
negate.c: In function ‘main’:
negate.c:13:19: warning: integer overflow in expression [-Woverflow]
[regehr@gamow ~]$ ./negate
-2147483648
1

In C compiler doublethink, -INT_MIN is both negative and non-negative. If the first true AI is coded in C or C++, I expect it to immediately deduce that freedom is slavery, love is hate, and peace is war.

Integer Overflow #2

Error message:

UNDEFINED at <InitPreprocessor.cpp, (173:39)> :
Operator: -
Reason: Signed Subtraction Overflow
left (int64): -9223372036854775808
right (int64): 1

Code:

MaxVal = (1LL << (TypeWidth – 1)) – 1;

In C/C++ it is illegal to compute the maximum signed integer value like this. There are better ways, such as creating a vector of all 1s and then clearing the high order bit.

Integer Overflow #3

Error message:

UNDEFINED at <TargetData.cpp,  (629:28)> :
Operator: *
Reason: Signed Multiplication  Overflow
left (int64): 142998016075267841
right (int64): 129

Code:

Result += arrayIdx * (int64_t)getTypeAllocSize(Ty);

Here the allocated size is plausible but the array index is way out of bounds for any conceivable array.

Shift Past Bitwidth #1

Error message:

UNDEFINED at <InstCombineCalls.cpp, (105:23)> :
Operator: <<
Reason: Unsigned Left Shift Error: Right operand is negative or is  greater than or equal to the width of the promoted left operand
left (uint32): 1
right (uint32): 63

Code:

unsigned Align = 1u << std::min(BitWidth – 1, TrailZ);

This is just an outright bug: BitWidth is set to 64 but should have been 32.

Shift Past Bitwidth #2

Error message:

UNDEFINED at <Instructions.h, (233:15)> :
Operator: <<
Reason: Signed Left Shift Error: Right operand is negative or is greater  than or equal to the width of the promoted left operand
left (int32): 1
right (int32): 32

Code:

return (1 << (getSubclassDataFromInstruction() >> 1))  >> 1;

When getSubclassDataFromInstruction() returns a value in the range 128-131, the right argument to the left shift evaluates to 32. Shifting (in either direction) by the bitwidth or higher is an error, and so this function requires that getSubclassDataFromInstruction() returns a value not larger than 127.

Summary

It is basically evil to make certain program actions wrong, but to not give developers any way to tell whether or not their code performs these actions and, if so, where. One of C’s design points was “trust the programmer.” This is fine, but there’s trust and then there’s trust. I mean, I trust my 5 year old but I still don’t let him cross a busy street by himself. Creating a large piece of safety-critical or security-critical code in C or C++ is the programming equivalent of crossing an 8-lane freeway blindfolded.

Why Be Polite?

I’m generally not extremely rude, but as Sarah and many others would be happy to tell you, I’m not all about pleasantries. Basically I’ve never seen the point of certain kinds of small talk. To make things worse, I know almost nothing about sports and have thus disqualified myself from a large proportion of male small talk. Also, I’m not that interested in people being polite to me, most of the time. During my first few months in Utah — where people are fairly polite — I was actually a little freaked out.

Lately I’ve started to better see the value of being polite, through my kids. When the three year old says at breakfast “Dad may I have a glass of water please?” I’m usually happy to get up and get the water.  On the other hand, if one of them calls out “THIRSTY!” (this actually happened one time and he’ll be lucky to live it down before leaving home) then not only do I fail to be filled with the desire to get him something to drink, but also visions of boarding school pop into my head. Military boarding school.

On my part, I try to be polite with the kids, for example when asking them to clear their places at the table. They do seem to appreciate being treated respectfully. Overall, given a group of people in close proximity who have to help each other out, life is nicer when people are at least moderately polite. Who knew?

Red Baldy

People following the “outdoors” thread on this blog will have noticed that Bill and I failed to summit on Mount Baker and also on White Baldy this year already. I’m not all about summiting, but this got on my nerves a little. Yesterday I decided to climb Red Baldy, an 11,000′ neighbor to White Baldy. Around six years ago I had failed to climb Red Baldy by its NE ridge, due to some frightening scrambling problems and also I was by myself. Just to make things confusing, Red Baldy is usually accessed from the White Pine drainage, whereas White Baldy is usually climbed from Red Pine.

This time I was a bit worried about timing: I couldn’t start before 9:30 due to dropping off kids, and had to finish before Sarah and I went out to celebrate our anniversary (nine years!). The White Pine road is notoriously long and switchbacky, and at least one trip report on the web indicated an eight-hour round trip for this peak. Luckily, whoever said this was either slow or took a different route: I made it up and down in 5.5 hours, including about an hour on top. The fast way to Red Baldy is to walk the White Pine road until it makes a final switchback towards White Pine Lake a little below 10,000′. From this switchback, ascend tundra and talus to Red Baldy’s north ridge, then follow this ridge to the summit. After leaving the trail this route is class 2 walking, making Red Baldy perhaps the 4th easiest 11,000′ peak in the Wasatch (after Hidden Peak, Sugarloaf, and Baldy).

On this hike temperatures were pleasant at the trailhead in the morning and also up high around mid-day, but it was around 85 at the trailhead when I got back there at 3pm, and then 102 by the time I got home, yikes. A few snowfields were left in upper White Pine; in my soft boots these were useless on the way up, but provided a quick way to descend a few hundred feet.