Slashdot Mirror


Null References, the Billion Dollar Mistake

jonr writes "'I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. In recent years, a number of program analysers like PREfix and PREfast in Microsoft have been used to check references, and give warnings if there is a risk they may be non-null. More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965.' This is an abstract from Tony Hoare Presentation on QCon. I'm raised on C-style programming languages, and have always used null pointers/references, but I am having trouble of grokking null-reference free language. Is there a good reading out there that explains this?"

19 of 612 comments (clear)

  1. null or not null, that is the question by alain94040 · · Score: 5, Interesting

    It's hard to imagine life without the null pointer! That being said, the author is not really responsible for billions of dollars of mistakes, the programmers are.

    If there is one thing I'll complain about, it's the choice of the value 0. It's almost impossible to trace it. When we do hardware debug of chips, we prefer to use a much more visible value such as 0xdeadbeef for instance. Otherwise a bad pointer will bland too much with all the uninitialized values out there.

    In assembly, null has no particular meaning. If you dereference an address, you can do it in any range you like. It's just that 0 on most machines was not a good place to store anything, since it would typically be used to boot the OS or some other critical IO function that you don't want to mess up with. Thus null was born.

    1. Re:null or not null, that is the question by Chrisq · · Score: 1, Interesting

      Though if you were designing a language you could dissallow the use of ptr as a boolean or numeric value, and force operations like

      if (! valid ptr) {
      }

    2. Re:null or not null, that is the question by fishbowl · · Score: 2, Interesting

      The C specification already requires the compiler to deal with that, and it's been the case since K&R. No matter what the implementation defines as NULL, comparing or assigning 0 in a pointer context always works.

      http://c-faq.com/null/ptrtest.html

      --
      -fb Everything not expressly forbidden is now mandatory.
    3. Re:null or not null, that is the question by cant_get_a_good_nick · · Score: 3, Interesting

      RE: malloc pattern initializer

      what's a good one for x86 and AMD64 chips? While spelunking flags for valgrind, i remembered the thought process for 68k chips. Use an A-Line trap, unimplemented so execution would stop. Also, make it odd, so a dereference would trigger a bus error.

      What's the best values for x86 debugging?

    4. Re:null or not null, that is the question by rickb928 · · Score: 2, Interesting

      The first time I saw an ethernet MAC address of 02DEADBEEF20 I went on a 20-minute snipe hunt through the switches.

      It was the /dev/net0 adapter in the standby member of a Sun cluster.

      A month later, I got the inevitable frantic voicemail from the telecom guy, asking what the '^&(*ing 02DEADBEEF20 address' was, and would I pay more attention to these things and secure our network, please and thank you. I told him it belonged to the Teleradiology project, not to worry. He accused me of being an imbecile, that wasn't a legitimate MAC address. I asked him why he hadn't brought this up in any of the weekly meetings for a month, since it was there all along. Hung up on me. I took some time to creat some interesting LAAs for the various minor servers we had, especially the Internet gateway/proxy, and the email server. He was not amused, but his boss was. And told me to stop annoying him. Something about a gun in the monkey cage, if I recall.

      I actually liked the created MAC addresses. Easier to trace. Mundane vendor-supplied MACs are just boring. Hopefully, though, other admins didn't start being creative. There are only so many clever permutations. Thank God for NAT and Token-Ring. Those were the days.

      --
      deleting the extra space after periods so i can stay relevant, yeah.
    5. Re:null or not null, that is the question by Carewolf · · Score: 2, Interesting

      You have much to learn. Don't mix how the language is supposed to work and what is actually possible.

      P *p = 0;
      P &r = *p;

      References that are NULL are the worst kind.

    6. Re:null or not null, that is the question by clone53421 · · Score: 2, Interesting

      How about 0xCC (INT 3), which is typically used as a debug breakpoint? It will halt the execution (as long as you're running the code in a debugger, which is assumed), and it's a one-byte opcode which is good since that means if you somehow jump into unallocated space, you can't jump into the middle of the instruction.

      --
      Alexander Peter Kristopeit bought his basement from his mommy for one dollar.
    7. Re:null or not null, that is the question by clone53421 · · Score: 2, Interesting

      Well, jumping into memory filled with 0xBA would repeatedly execute the instruction 0xBABABA, or MOV DX,0xBABA. I'm thinking that's probably not what GP meant by $BA, but... well, that's all I came up with.

      --
      Alexander Peter Kristopeit bought his basement from his mommy for one dollar.
    8. Re:null or not null, that is the question by jc42 · · Score: 1, Interesting

      Of course there is more than a syntatic difference between a reference and a pointer in C++.
      For one, references CANNOT be null, while pointers are allowed to be null.

      This reminds me of an embedded project that I worked on some years ago, that was mostly coded in C. For reasons having to do with the CPU and its hardware memory mapping, we found it handy to have a global variable declared as "extern unsigned char* physmem;", whose address was defined during linking to be zero. I was duly impressed by the fact that the C compiler wasn't fazed by this, and handled it correctly. I.e., the value of &physmem was numerically zero, the same as a null pointer. This allowed us to use physical memory as "just a byte array", and eliminated the need for clumsy special-purpose code for dealing with this one special byte array.

      If it's true that C++ doesn't allow this, then that excludes the use of C++ on hardware with such properties. Of course, people working at the level that we were generally wouldn't take you seriously if you suggested C++. We had enough trouble convincing some of them that the work should be done in C rather than assembly language ("as God intended", where "God" was the name of the committed that designed the hardware ;-). If C had imposed such silly restrictions, the job probably would have been done in assembly.

      --
      Those who do study history are doomed to stand helplessly by while everyone else repeats it.
  2. 20 second explanation by AKAImBatman · · Score: 4, Interesting

    I am having trouble of grokking null-reference free language.

    If you're familiar with SQL, then a simple "MyColumn NOT NULL" definition should explain it. Basically, the value can never be set to a null value. Attempting to do so is an error condition itself.

    In fact, DB design is a pretty good analogy for the concept as databases often are forced to wrestle with this issue.

    Consider for a moment how you would design a database that has absolutely NO null references. Not a one. Zip, zero, nada. Obviously the best way of accomplishing such a database is to denormalize any value that might be null. So if Address2 is optional, you would want to split Address into its own table with a parent key pointing back to the user entry. If the user has an Address2 value, there will be a row. If the user does NOT have an Address2, the row will be missing. In that way, empty result sets take the place of null values.

    In terms of programming languages, there are a varity of ways to map such a concept. Collections are a 1:1 mapping to result sets that can work. If you don't have any values in your collection, then you know that you don't have a value. Very easy. Similarly, you can be sure that none of the values passed to a function or method will ever contain a null value. Cases where you might want to pass some of the values but not all can be handled either by method overloading (e.g. Java) or by allowing a variable number of parameters. (e.g. C)

    Some pieces of programming would become slightly more difficult. For example, 'if(hashmap.get("myvalue") != null)' would not be a valid construct. You'd need to perform a check like this: 'if(hashmap.exists("myvalue")'

    Of course, the latter is the "correct" check anyway, so the theory goes that the software will be more robust and reliable.

    1. Re:20 second explanation by BigHungryJoe · · Score: 2, Interesting

      Ok, I'm far from an expert on SQL, but if NULL doesn't represent "unknown" in SQL, then why does

      select 1 from dual where 1 not in (2,3,NULL);

      return an empty set?

    2. Re:20 second explanation by vux984 · · Score: 2, Interesting

      If you put the right thing in the right field, and always in the right field, and only in the right field (so help me God) you just need some kind of template per country, e.g for Belgium: boxnumber, street housenumber [newline] postcode city.

      You should be able to find them from the IPU, or deduce them by looking foor a company in the required country. Shouldn't take you a year, if you ignore all the Bongo-Bongo Land type places.

      Even before you get to bongo-bongo land.

      For Canada your base template might be:

      Mr. Randy Jones
      Widget Manufacturing Inc.
      #101 21 Cordova Street East
      Vancouver, BC, V3H 1T3
      Canada

      But then in comes:

      Mrs. Irene Smith
      General Delivery
      Small Creek, MB, R0E 0K0
      Canada

      "General Delivery", isn't a street or a company; it means drop it off at the post office, they don't bother with mail boxes for each person here. People just come in and ask the postmaster for their mail.) Used to be very common in small rural towns, still a reality in out-of-the-way enough places.

      or another type of rural address like:

      Alice Smith
      Box 22F RR4
      Somewhere, SK, S0N 0A0
      Canada

      Where instead of the street address of the recipient you are identifying a lockbox on a rural route somewhere.

      Then you've got institutional addresses like:

      Dr. Jon Driver
      Office of the Vice President
      3100 Strand Hall
      Simon Fraser University
      8888 University Drive
      Burnaby, BC, V5A 1S6
      Canada

      which needs a bunch of space for all kinds of internal routing information.

      And we haven't gotten out of Canada yet.

      Its not so much that you can't keep adding fields and templates to accomodate these, its that every time you build a system and roll it out, users will shortly bump into an address that doesn't fit... and then they cram the information into the 'wrong fields'. And you might as well have just given them a few blank lines to fill.

      Its also a royal hassle because the address entry form in your application becomes increasingly complicated to use, and the order the system needs to know information, is usually opposite from the way people like giving it.

      Giving them multiple lines to just write what they need is simpler, and doesn't cause problems on the corner cases. I usually use a hybrid for systems used in Canada: a few generic 'address lines' and then city/province/postalcode fields. And I think even there we've run into a couple addresses that don't have cities. Although things have gotten better... the post office in their efforts to automate things have been arbitrarily assigning street addresses to things that don't really have them. (Simon Fraser University, didn't used to have a 'proper' street address, it was just "Simon Fraser University, Burnaby, BC". They added a street address "8888 University Drive" primarily to accomodate computer systems that kept choking on there not being a street address.

      But even today, you might send a message to:

      Dr. John Smith
      Faculty of Arts and Sciences
      University Hall
      Cambridge, MA, 02138
      USA

      with no street number.

    3. Re:20 second explanation by jadavis · · Score: 2, Interesting

      "try it in a CHECK constraint, and it will never fail"

      While I have the standard open, here's a reference to back up my claim above:

      A constraint is satisfied if and only if the applicable <search condition> included in its descriptor evaluates to True or Unknown.

          -- SQL 2008 Part 2: Foundation (SQL/Foundation) section 4.17.2

      And I also tried it in PostgreSQL, which generally has respect for the standard.

      So, a constraint does, indeed, treat NULL as TRUE-like.

      --
      Social scientists are inspired by theories; scientists are humbled by facts.
  3. Re:Null is NOT just a value by AKAImBatman · · Score: 1, Interesting

    wouldn't the first thing you'd do in the system API design of any non-null language be, the creation of a singleton object instance of the superclass of all objects, named 'null' ?

    Umm... no? The first thing done is usually a superclass called "Object". If you don't extend anything else, you extend Object. Depending on the language, the superclass of Object would either be self-referential or the option to obtain a superclass wouldn't exist. (The latter being the "correct" solution. See my next statement for why.)

    Null is just a value

    That's actually a problem. Null is a piece of data that represents the absence of data. The paradox here should be obvious. If the data doesn't exist, why do we create data about it not existing? If I have no apples, do I have an object that represents my lack of apples? No, I simply have no apples. At best, I might have a special container for apples. If it's empty, then I can infer that I have no apples. Just as a program can infer the absence of data through an empty collection.

    Thirdly, a similar rant can be had against non-range checking of enums in C (but then warning against it in switches (WTF?)).

    There's a lot of things wrong with C as a language. Don't try to use those as arguments. (Remember, C is more or less high-level assembly. On the scale of comp-sci it barely even rates. Its popularity stems from the excruciating slowness of computers in days gone by.)

  4. Re:Wouldn't help by nuttycom · · Score: 4, Interesting

    If you use a sane class for references that could possibly be null (like Option (aka Maybe in haskell) then your compiler will *force* you to handle the null case.

    This is where null went wrong, at least in statically typed languages: it's a hole in the type system that errors fall through into your program. When coding in Java, I make an explicit point to never return null from a method; if I have a situation where no reasonable return value might exist, I use the Option class from functionaljava.org and thus force the client to handle the possibility of the method not returning sensible data. Since Option obeys the monad laws, it's easy to chain together multiple things that might fail (with the bind or flatMap operations.)

  5. Re:The mistake was actually not having a standard by Vanders · · Score: 3, Interesting

    The problem with Pascal strings is that it's easy for a short-sighted implementer to paint themselves into a corner. It's all very well and good to say "The first two bytes in a string are used to indicate the length of the string" but then what do you do a decade from now when a 16bit string is laughably small? The benefit of NUL terminated strings is that there length is only limited by the memory available to you and yet are forward and backward compatible by decades.

  6. Null as a concept by JustNiz · · Score: 5, Interesting

    Stroustrup's "C++ Programming Language" book introduces a concept called "resource acquisition is initialisation" that was eye-opening enough to me that it forever changed the way I think about code, and also seems relevant to your point.

    The basic idea is that an object is always meant to represent something tangible. As an example, consider the design of file object that abstracts file I/O operations. As a developer, I've come across this one several times, it is normal that such objects have open and close methods, however that makes the design of the object in contradiction with Stroustrup's concept because open/close provided as methods rather than only called in the constructor/destructor means the object may be in existence yet be in a state where it is not associated with an open file. You basically have to grok that having a file object around that doesn't directly map to an open file just adds overhead to the system and is basically bad OO design in that in some sense that object is meaningless.

    Apply the same concept to a reference and you have your answer. If a reference is pointing at nothing, then what is its purpose? The only thing a NULL reference is good for is when the software design ascribes a special meaning to the value NULL. Instead of just meaning address location 0, it gets subverted to mean "variable unassigned" or the "tail node of list" or somesuch. Ascribing multiple meanings to a variable value (especially pointers/references that are only ever meant to hold memory addresses) is one example of bad programming practice known as programming by side-effect which most people agree should be avoided.

    Another point is that in most OO lanugages, references have an extra benefit of being more strongly typed than pointers, menaing that reference is guaranteed to only ever be pointing at an instantiated object of its specific type. That guarantee also gets broken when a reference can be NULL.

  7. "core constants" were around at least by 1960s by Ungrounded+Lightning · · Score: 2, Interesting

    The first OS I encountered was tape-based. And it prefilled user memory with a "core constant".

    This was a subroutine jump to an abort routine which printed the return location - which in turn told you where you had improperly jumped to and dumped all your registers, followed by the memory itself if that was authorized. (That was all the info that was left by the time the OS got control.)

    The walls of the computing center contained posters giving this value as it would appear if printed as various types of values (integer, floating point, complex, ...).

    Another machine I dealt with back then was a typesetting device using a Data General Nova and the company's homebrew OS (designed by Mark Weiser of Xerox Parc fame, based on work by Djikstra and Riddle). It had a debugger entry that could be reached by a one-word instruction which we would insert as breakpoints and also use to fill unused memory. The hex form of this was "0c0f". When the machine hit a breakpoint we said it had "coughed".

    --
    Bantam Dominique roosters crow a four-note song. Once you've heard it as "Happy BIRTHday" you can't NOT hear it that way
  8. Re:An was an even Bigger mistake: by Estanislao+Mart�nez · · Score: 2, Interesting

    Zero. The bane of all. It was the gateway math to all modern problems. It would be so much simpler with just countables. Surely the current crisis, measured in trillions would look so much better without all those zeros.

    Yeah, I know this is a joke, but I've still got to point out the following: a positional numbering system doesn't actually need a zero digit. Throw out zero, and use instead a digit 'X', whose value is ten. Then you get:

    1, 2, ..., 9, X, 11, 12, ..., 19, 1X, 21, ..., 98, 99, 9X, X1, X2, ..., X9, XX, 111, ...

    8X = 8 * X + X (ninety)

    X2 = X * X + 2 (one hundred two)