Defensive Programming Gone Too Far, Or Respecting Your Contracts

June 30, 2015

5 comments

In the last few months, I’ve heard this argument more than once. And whenever I have to repeat my counter-arguments, I know it is time for a blog post. So here goes, a short take on defensive programming gone too far.

Suppose you have the following, extremely boring, C++ function (the language is not of any importance for this exercise):

bool add_task(employee& emp, task const& t) {
  if (emp.task_count() > 10)
    return false;
  emp.add_task(t);
  return true;
}

And here’s the argument I’m going to be fighting against until I draw my last breath:

The function needs to be more defensive about its inputs. What if the emp object is destroyed while the function is still executing? What if the t object is destroyed before the function successfully adds it to the employee’s task list?

You can take this argument even further:

The add_task function has a serious security vulnerability caused by a race condition. An attacker can call this function with an object that is being destroyed in the middle of the function. Then, the heap space for that object could be reused for an attacker-controlled region of memory, and the employee::add_task function (which is virtual) will then end up executing arbitrary code.

Well, um, no. I’m sorry. A resounding no. Let me make it even clearer: no way.

The add_task function isn’t asynchronous. It’s only using its arguments until it returns. It is a fundamental part of a function’s contract that its arguments should remain in a valid state until the function returns. If you call a function with an argument that happens to be destroyed before the function returns, it is not the function’s fault.

If you remain unconvinced, take a look at your favorite operating system’s API or your favorite language’s standard library. Here are a couple of examples:

  • The Win32 ReadFile function takes a pointer to a buffer which it fills with data read from the file. You are not allowed to destroy the buffer before the function returns. If you do so, you trigger undefined behavior.
  • The C++ STL’s vector::push_back function takes a reference to an object to put in the vector. If you destroy the object before the function had a chance to put it in the vector, you trigger undefined behavior.

There is no conceivable way to design an API that takes into account the possibility that you are handed arguments that are surreptitiously destroyed before your function had a chance to use them. So, no, you don’t need to defend against this scenario. You need to count on your API’s users to have a shred of common sense left.

Related reading: being on the other side of the airtight hatchway.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

5 comments

  1. MottiJune 30, 2015 ב 10:54 PM

    I know this is a toy example but add_task is not thread safe, if two threads try to add a task to an employee with 10 tasks they may both succeed (also, didn’t you mean to say >= 10 having a maximum of 11 is a bit strange).

    If the function isn’t supposed to be used in multi threaded code I really don’t understand the argument that arguments may be invalidated during the function call. I remember seeing people copy COM pointers into smart pointers in the beginning of a function so they aren’t released under their feet but I haven’t heard anyone repeat this argument in the last ten years.

    BTW when (if) coroutines are added to C++ we may have to rethink this 🙂

    Reply
  2. Alois KrausJuly 1, 2015 ב 12:50 AM

    Funny. Were that device driver developers which deal with reentrant code? I would be surprised to hear that from user mode devs which usually want to get stuff done and often do not fully understand how threading works.

    Reply
  3. Pingback: Visual Studio 2015 RTM - The Daily Six Pack: July 1, 2015

  4. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1893

  5. wekempfJuly 1, 2015 ב 11:28 PM

    Let’s assume you should defend against this. Tell me how you would go about doing it? Can’t? End of discussion.

    Reply