a thoughtful web.
Good ideas and conversation. No ads, no tracking.   Login or Take a Tour!
comment by lm

This is yet ANOTHER memory safety bug. Hopefully more and more people will become convinced that we need to move away from C and C++ to languages that are memory safe by default, such as Rust.

(Of course, what I'd really like to see is more provably correct code, but the tools to do that are far from being accessible to normal humans right now.)





user-inactivated  ·  2845 days ago  ·  link  ·  

    from C and C

    to languages that are memory safe by default

    the tools to do that

What does either of those mean?

goobster  ·  2845 days ago  ·  link  ·  

....aaand if you are not a programmer, and the response from lm makes no sense to you at all...

When you write software, everything gets stored in RAM. Add 2 + 2 and that result gets put into a place in RAM until you decide what to do with it. (Options include write it to disk, use it in another calculation, erase that memory space and free it up for something else to use, etc.)

In older, pre-WWW software languages, you had to map out all the memory space you were using with specific addresses. Each bit had a specific address, just like every house on your street has a specific address.

To add 2 + 2 you would add memory space AB14263 plus memory space RPX9823, and put the result into memory space XXX9999.

The problem was keeping track of every bit of memory, what was currently in it, what was free to be used, and what parts could be emptied to be used again. Every line of code you wrote made memory management more complex, and programs often died due to memory management issues.

Then along came languages like Java, that just said, "Do your calculations. I will worry about keeping the memory map accurate."

So you would write code that would say, "Java, add 2 + 2 and put the result into memory, and label that place in memory "MyResult"."

And Java would do it.

So any time you needed the result of that calculation, could simply refer to it by name, "Java, what is MyResult + 7?" And Java would give you "11".

Memory leaks are what happens when you lose track of your memory map. Malicious coders can ask for the contents of a bit of memory - for example, a 2 character space where that "11" is stored - but intentionally screw up the request and ask for the 5,000 characters beginning with the "11".

If passwords or other delicate data happen to be stored in any of those 5,000 characters, the malicious coder now has them.

Note: Practically speaking, this is not how memory leaks work, and anybody that is a programmer from the old skoool will be stretching his fingers and getting ready to flame my misrepresentation of a memory leak, and buffer overflows, and write protections, and blah blah blah. But. For the person that does not know any programming, my description demonstrates the problem in a simple and easy to understand way, without making their eyes glaze over. I am now putting my fingers in my ears and singing loudly...

lm  ·  2845 days ago  ·  link  ·  

Thanks, goobster! I have spent the last several days grading C homework submissions, and I guess I'm still stuck in 'extremely technical' mode.

lm  ·  2845 days ago  ·  link  ·  

For example, in C or C++, you can write the following:

  int array[5] = {1,2,3,4,5};

printf("%d", array[10]);

It will compile fine and probably run fine...and spit out some value from some place in memory that you "ought" not to access.

In Rust, however,

  let array = [1,2,3,4,5];

println!("{}", array[10]);

results in a runtime panic--the program quits, rather than performing an illegal memory access.

A better example here is how Rust eliminates use-after-free bugs at compile time because it has very strict semantics about who owns what. For example, in C, you could write this:

  void do_stuff(int* thing) {

// do something to thing

delete [] thing;

}

  // elsewhere

int* x = new int[5];

do_stuff(x);

printf("%d", x[0]);

That would compile and maybe it would run fine and spit out junk memory or maybe it would segfault. The equivalent rust would look like this:

  fn do_stuff(thing : [i32; 5]) {

// do something to thing

// Rust deallocates thing at the end of this function

}

  let x = [1,2,3,4,5];

do_stuff(x);

println!("{}", x[0]); // COMPILE ERROR!

This is because Rust is really really picky about who "owns" what thing, and when I call do_stuff on x, I give do_stuff ownership of x. Once I give away that ownership, I can't use x anymore since it's not mine!

For an example of where that can be a security issue, just this week Linux had a double-free bug (that easily turns into a use-after-free bug) that let any user become root.

Other languages get memory safety by using a garbage collector. I could talk your ear off about other bugs that can be caught at compile time by languages with very strong type systems (like Haskell) but I will spare you =]

The tools I have in mind are languages like Agda or Idris, which are basically proof engines that happen to also produce executable code. At this point they are not very user friendly or easy to write big programs in, though.

Devac  ·  2845 days ago  ·  link  ·  
This comment has been deleted.
lm  ·  2845 days ago  ·  link  ·  

Yep, smart pointers and "modern" (post-2011) C/C++ features are very nice, and fix at least the low hanging fruit of what's wrong with those languages. However, you have to use them religiously and your libraries also have to use them to get their benefits. Rust at least forces you and library developers to write safe code by default.

Not to say C++ is necessarily a bad language...it's probably the language I know best, so I feel justified in complaining about it, but I'll admit that it's very good at what it does (especially considering its age) and Rust isn't quite there yet as a full replacement.