1

How can one safely call a function that might segfault without corrupting the stack or the heap?

These SO questions cover using signal handlers and setjmp.h to regain control.

Coming back to life after Segmentation Violation

Best practices for recovering from a segmentation fault

They neglect the likely memory corruption that occurs prior to a seg fault.

What strategies can be used to isolate the memory space of a function and its caller?

This is just a curiosity question, there's no specific problem I'm trying to solve. Let's just suppose we're programming something that absolutely cannot crash -- pacemaker, Mars orbiter, nuclear launch control, take your pick. We've already thoroughly unit tested all our code and formally proven its correctness. For bureaucratic reasons we have to use C++ and Linux.

I was trying to sketch this out with clone(). My idea was to run the function with as much isolation as possible and pass data back and forth by squirreling it away at the bottom of the child's stack.

Is that reasonable or is there a better way to do this?

9
  • There is no general solution to that. Critical software is not expected to crash (thanks to code reviews, formal methods, and a lot of testing). And C++ is not a language designed for such crashing recovery. Commented Feb 27, 2015 at 8:17
  • How would memory corruption occur prior to a segfault? My understanding is the segfault occurs specifically so the OS can prevent memory corruption, with the process's existence as the cost.
    – user22815
    Commented Feb 27, 2015 at 16:19
  • @Snowman The catch is that the operating system can only detect that inappropriate memory access when it occurs in a memory segment the process doesn't have write access for. What often happens is that you start erroneously writing in a segment you do have access to and the error is only caught when you cross into a segment that you don't have write access to (segfault). Commented Feb 28, 2015 at 0:43
  • @Praxeolitic the Wikipedia article on the topic agrees with my education and training: attempting to perform a restricted action results in a segfault. Not succeeding, just attempting. As in, before the damage is done.
    – user22815
    Commented Feb 28, 2015 at 0:50
  • @Snowman Read the last sentence in my comment again. Commented Feb 28, 2015 at 0:52

3 Answers 3

4

The solution is a remote procedure call. The callee must run in its own process space. How exactly you achieve that is a fairly minor detail. I'd strongly suggest not inventing the wheel yourself.

Not that you'd need this after you've "formally proven its correctness". Correct code doesn't cause segfaults.

5
  • What if our formal proof had a bug? (Our crack team also formally proved the formal proof but that too had a bug.) ;) Commented Feb 27, 2015 at 8:37
  • 4
    Well, then it wasn't formally proven after all ;)
    – MSalters
    Commented Feb 27, 2015 at 8:41
  • 1
    @MSalters it is a peril of formal methods, in that the human mind is needed to translate software behavior into theorems, which in turn makes it fallible and expensive (on the contrary, translating the binary code into theorem would make it computationally too expensive). #NoSilverBullet but nevertheless still useful.
    – rwong
    Commented Feb 28, 2015 at 23:55
  • 2
    @rwong, in the late 1970s, I was an undergraduate research assistant on Don Good's Certifiable Minicomputer Project. The project was doing research into formal verification. They were doing the work in Gypsy, a language that they developed, that was specifically designed to be robust and verifiable. Verification condition ("theorem") generation was completely mechanical and NO BIG DEAL. Commented Mar 1, 2015 at 1:53
  • 1
    @rwong (continuing previous comment) Gypsy was designed to be formally verifiable AND to be suitable for systems programming. It did not have direct hardware access primitives; those were required to be supplied using assembly language (and by definition were not verifiable). HOWEVER, comma, the VAST amount of C and C++ code out there does NOT require direct hardware access primitives, nor does it require the ability to GOTO heck in a handcart. (Gypsy did not have a GOTO statement. Nobody missed it.) Commented Mar 1, 2015 at 1:57
2

Short answer: Absolutely no guarantees can be made about a program that crashes (or in fact one that DOESN'T crash). If an application crashes, the entire program's data (and any other writable memory areas, files, databases, etc that this program MAY have touched) areas must be treated as "probably not correct".

Long answer: Since there is absolutely no telling what happens in code that doesn't "behave correctly", you can't actually know that data has not been clobbered BEFORE the segfault (or stackfault, bus error, divide by zero, overflow, etc).

I had a bug many years ago, on a system that didn't have memory protection enabled, which was caused by swapping the order of values in memset (due to calling convention mixup and not including the correct headers and memset having a non-standard calling convention in that particular compiler), which meant that instead of memset(dest, fill, size), the code was doing memset(size, fill, dest) - given that memory addresses are generally MUCH higher than the size of the thing than you want to memset, the result was that the entire memory from somewhere near zero to the point in the code which was executing memset got filled with the value fill. Debugging that wasn't easy even with an In-Circuit emulator that would tell me exactly what my last N instructions where (N being a large number like 16K or 64K) - not really helpful when the memory I executed in has been overwritten (sometimes it would actually then continue executing garbage instructions for quite some time before it become something REALLY bad and actually stopped). Of course, if this happens only at the time of 14.52.15 on Thursdays where the day is divisible by three and seven, in months that have an R in their English name and only with outside temperatures between 18 and 21 degrees centigrade, you may be spending some time figuring out why the system crashed.

Now, you can have similar scenarios in code that uses memcpy or memset with just simple math errors in the calculation of the size (e.g. subtracting a larger value from a smaller value and ending up with "negative" size - since sizes are typically unsigned, that ends up being a HUGE value. Then segfaults when it reaches the "end" of some memory-region (data, heap, stack, or whatever it may be). There is REALLY no way to guard against this sort of overwrite.

Running suspect code in a separate process, and, assuming it didn't crash, check the output from that process before accepting it. But of course, I can write a program to calculate pi with many decimals, that doesn't crash, but because I've made some mistake in it (e.g. copied the wrong formula from Wikipedias article about calculation series for famous numbers), it comes up with 2.71828182845904523536028747135266249775724709369995957496696762772407663 rather than 3.1415926... Without KNOWING what the result should be, you can't really know whether it is right or not. And of course, if you already know the result, you don't need to do anything to figure out what the answer should be.

Systems designed for high security typically have extra code to safe-guard that the result is correct - one typical approach is to have multiple teams, designing their own software and hardware, and then have a "mediator" in that collects all the "results" from each system - if they all match, good. If they don't do, raise an alarm something to indicate that something is now right. If the result of one system is "crash", then restart that system (and raise the alarm). Since the systems are designed by different teams, one hopes that any bugs are UNIQUE to each system. Add to this VERY THOROUGH testing, lots of review by people who didn't write the code, but are professionals specializedon spotting "bad" things in code.

The number of systems and the action when something goes wrong depends on the situation. In a train, you may get away with two systems and a mediator - the mediator just pulls the emergency brake when results differ and waits for the traindriver to push some buttons to reset the train computers. In an aeroplane, there is no such thing as an emergency brake, so a "majority vote" system is used to determine what is the best action (and big red flashing light saying "computer systems don't agree") - probably a good idea to go to "manual" rather than "autopilot" at that point too.

In cases of medical software (related to equipment that is directly connected to patients, not the database keeping the patient records, or the computer that people log into their favourite website on when they visit the hospital library), you also have to have medical software certification, which includes using an OS with medical certification, and code-review of the specific software by an external body whose whole purpose as an organisation is to ensure the safety of medical equipment. So a pacemaker would certainly fall into this category.

Of course, no amount of reviews, testing and so on will 100% stop safety-critical systems from giving bad results. I believe the airliner crashing a few years back on a flight from Brazil to Europe was partly down to the planes computer system giving back incorrect values, and the "mediator" was happy with that (the pilots were also making mistakes, but this was at least in part caused by being given the wrong information in the first place) - I don't believe the computers crashed, they just gave readings that were incorrect in relation to reality.

1

This question is indeed too broad to be answered by a single answer on any single StackExchange site.

You might want to visit the Information Security site to read about the practical techniques that you will want to adopt for your software. Of course, you can probably find similar techniques discussed on this site or at StackOverflow, written from a programmer's perspective.

However - don't cross-post your question there; it is too broad and the information you provided aren't pinpointing enough for anyone to give a concise answer.


There is already a large collection of literature on:

  • Software fault isolation
  • Software verification
  • Trustworthy computing
  • Virtualization
  • Sandboxing

etc.

There is also an adage to be remembered at all times: #NoSilverBullet


Given your description and your initial comments, I believe the best fit for your (hypothetical?) situation is to use Cleanroom Engineering. In other words, the condition under which code is designed and developed needs to be carefully controlled. Code that is developed outside this environment is not allowed to be introduced. Throw away your crash-causing code the first time it happens. Period. (Okay, maybe give it a three-strikes.)

It stems from the observation that it is not always possible to fix-up buggy source code, especially if that buggy source code doesn't have a clean design (or any design at all) that can be formally verified.

Formal verification is not easy to apply for arbitrary pieces of source code. To use formal verification, it is encouraged to write source code with verification in mind - often writing code in a different style than a typical programmer would.


Your hypothetical situation actually mentions three separate failure modes:

  • The code produces wrong results, either due to algorithm bugs, or corruption of algorithm's own data structures (but not outside).
  • The code corrupts some other module's data structures; as a result, its result might either be correct or wrong, but the resulting corruption would cause silent data loss, failure in an separate (possibly unrelated) module, or a crash at any time in the future.
  • The code causes a crash. The program stops executing (because there is no logical way to continue.)

The software fault isolation used by Google Portable Native Client (both versions) are interesting:

    *
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.