Live data from Hacker News

Getting Past C

blog.ntpsec.org

291–300 of 504 posts

Re: Getting Past C

#291

Earlier quoted context omitted.

One thing that I've heard might be a difference, but haven't confirmed yet: Rust's lack of move constructors. So you have a vector, it's full, you push one more. It has to reallocate. How do you copy all of the elements over to the new allocation? In Rust, it's a straight memcpy of T * n bytes. But due to move constructors in C++, IIRC they must be moved one at a time. Again, I haven't actually dug into this; maybe s…

Well, for trivially copyable types[1] the reallocation can be a straight memcpy. For the rest, I don't know that having or not having a move constructor is the important distinction; it will be preferred over the copy constructor if it is declared as not throwing exceptions, but either way some constructor of the object must be called if it exists (though it might be inlined and optimized away). I imagine Rust does s…

Thanks for elaborating on this!

> copying bytes if the underlying type has the `Copy` trait and calling some actual code if not,

It does not. Moves and copies are both "memcopy these bytes", the only difference is if you can use the previous copy or not. (This is also, of course, subject to the optimizer, which may elide the copy.)

> either way some constructor of the object must be called if it exists (though it might be inlined and optimized away).

Yeah, this is what I was getting at; this has to happen in C++, but not in Rust. You are right to point out that this only matters for things that aren't trivially copyable.

Re: Getting Past C

#292

Earlier quoted context omitted.

> Can anybody make a strong case to me as to why are buffer overflows considered an issue in C when it takes like 10 minutes to write and test an array implementation that prevents that from ever happening? The CVE database. Just because you 'can' write such an array implementation doesn't mean you will, doesn't mean your third party libs will, doesn't mean any of your legacy code uses it, and certainly doesn't mean…

> I ask this in bad faith: I encourage you to share a single nontrivial codebase which actually creates the abstraction you've described and religiously adheres to using it throughout. As to why this is in bad faith: I'm definining "nontrivial" here to mean using 3rd party APIs - which will operate on C style arrays, not your project specific safe wrappers - and thus by definition won't be "religiously" sticking to s…

>I work on a C codebase that does this

And more power to you. Note the beginning of the parent comment, however:

> Just because you 'can' write such an array implementation doesn't mean you will

So yes, even if the codebase you work on does have these 'mythical', hard-to-achieve properties, that doesn't mean that most or even many C codebases will.

Good engineering entails observing what problems actually occur and working to fix those. Memory safety issues do commonly occur in C codebases. Regardless of whether the fix in C is simple or even trivial, programmers aren't doing it. So, Rust has some value because it forces the programmer to produce code that is largely free from this type of issue.

Enforcing norms like 'be more disciplined when writing C' or 'stop using external libraries' is much harder than simply using a different language.

Re: Getting Past C

#293

Earlier quoted context omitted.

One thing that I've heard might be a difference, but haven't confirmed yet: Rust's lack of move constructors. So you have a vector, it's full, you push one more. It has to reallocate. How do you copy all of the elements over to the new allocation? In Rust, it's a straight memcpy of T * n bytes. But due to move constructors in C++, IIRC they must be moved one at a time. Again, I haven't actually dug into this; maybe s…

Well, for trivially copyable types[1] the reallocation can be a straight memcpy. For the rest, I don't know that having or not having a move constructor is the important distinction; it will be preferred over the copy constructor if it is declared as not throwing exceptions, but either way some constructor of the object must be called if it exists (though it might be inlined and optimized away). I imagine Rust does s…

No, all moves in rust are memcpy if not optimized out entirely. Rust has affine types so moves don't need to "invalidate" the source value at runtime, the compiler just doesn't allow you to use the source variable after a move.

Rust's answer to copy ctors is Clone, which is always explicitly called. Variable use in rust is a move. Trivially copyable types (Copy) will be copied without compile-time invalidating the old type.

Re: Getting Past C

#294

Earlier quoted context omitted.

Obviously, one can program C to do anything, and write all the provably safe abstractions wished. But, that's not really the point. The point is that doing such is not the default. It requires engagement and knowledge of the programmer, especially on distributed projects with loose communication, such as many open source projects. And it only takes one programmer mistake to bring the whole house of cards down. Why al…

Why allow programmers to make mistakes? For a philosophical counterpoint: Why allow anyone to do anything that might possibly be incorrect, harmful, or otherwise perceived by some to be negative? I've looked at a lot of the talk surrounding "safe/secure languages", "safe/secure programming", etc., and yet every time I've heard people preach about the benefits, I feel like I just vehemently disagree. At a very deep an…

"UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things."

Re: Getting Past C

#295
post #128

Earlier quoted context omitted.

The lack of generics means your array implementation is either going to either: - be implemented with macros and token pasting, and result in a ton of mental overhead because you'll have a pile of types like array_foo for an array of `foo`s, and array_bar for an array of `bar`s, along with a pile of corresponding `foo * array_foo_get(array_foo, size_t)` and `bar * array_bar_get(array_bar, size_t)` functions. - or, ha…

C macros-faking-generics really aren't that bad (your "unsigned char" case is really easy to solve - use another macro). It's a bit goofy having different types floating around like "array_of_int", "array_of_float", but you can create them in a single line when needed, and once you create them, they work, and efficiently. They're not an ideal solution by any stretch, but it's not the nightmare scenario you envision w…

I would be interested in the specifics of the spaces thing (knowing more about ones tools is good) but "10 minutes" to handle all the various issues and bugs that will inevitably pop up from wrangling macros?

Re: Getting Past C

#296

Earlier quoted context omitted.

Well, for trivially copyable types[1] the reallocation can be a straight memcpy. For the rest, I don't know that having or not having a move constructor is the important distinction; it will be preferred over the copy constructor if it is declared as not throwing exceptions, but either way some constructor of the object must be called if it exists (though it might be inlined and optimized away). I imagine Rust does s…

Thanks for elaborating on this! > copying bytes if the underlying type has the `Copy` trait and calling some actual code if not, It does not. Moves and copies are both "memcopy these bytes", the only difference is if you can use the previous copy or not. (This is also, of course, subject to the optimizer, which may elide the copy.) > either way some constructor of the object must be called if it exists (though it mig…

Interesting. How does Rust handle types that want to do interesting things when copied, like bookkeeping or updating internal pointers? Maybe you just can't, which would preclude some kinds of intrusive data structures, or owning non-copyable mutexes for example. Does `drop()` get called on objects that have been copied from?

Re: Getting Past C

#297

After reading this post the idea of a C-to-C translator that injects bound checking, etc. comes to mind. Such translator could be used by OS distributions to provide safety in the least intrusive way and possibly completely automatically for many C codebases they have in their repositories. Translating into Go or Rust, on the other hand, cannot scale beyond some individual projects, that decide to undertake such effo…

CompSci keeps making them, even open-sourcing some, but little uptake or improvements from the FOSS crowd. Here's two of the top ones: http://sva.cs.illinois.edu/ https://github.com/jtcriswell/safecode-llvm37 https://www.cs.rutgers.edu/~santosh.nagarakatte/softbound/

As we've discussed, one issue is the big performance cost of these solutions. Anyone interested in working on an automatic C to SaferCPlusPlus[1] translator? It should address the performance issue[2]. And should be much more straightforward than these C to Rust/Go translators.

[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus

[2] https://github.com/duneroadrunner/SaferCPlusPlus-BenchmarksG...

Re: Getting Past C

#298
post #295

Earlier quoted context omitted.

C macros-faking-generics really aren't that bad (your "unsigned char" case is really easy to solve - use another macro). It's a bit goofy having different types floating around like "array_of_int", "array_of_float", but you can create them in a single line when needed, and once you create them, they work, and efficiently. They're not an ideal solution by any stretch, but it's not the nightmare scenario you envision w…

I would be interested in the specifics of the spaces thing (knowing more about ones tools is good) but "10 minutes" to handle all the various issues and bugs that will inevitably pop up from wrangling macros?

Yeah 10 minutes is quite a bit off, at least for me. The various issues and bugs that arose all came about when developing the data structures themselves - they didn't crop up in actual usage. Though my approach was to use a separate header and source file for each new data type I parameterised them by. So I had an "int_array.c" and an "int_array.h". And inside the header would just be function declarations generated by the macro. I basically didn't like the idea of dumping in a whole implementation of a data structure and all its operations with a macro every single time I wanted to use it. YMMV but I found it worked well.

Re: Getting Past C

#299

Earlier quoted context omitted.

I'm never quite sure where MIPS and SPARC are in terms of llvm support. (And does llvm support automatically mean rust support?)

LLVM is generally usable on sparc, although a significant amount of optimisation work still needs to be done, and there are some specific variants/platforms that sparc is used by that need better support.

Is it likely that work will ever be done? Oracle is, well, Oracle, and Fujitsu used ARM instead of SPARC for their new supercomputer.

Re: Getting Past C

#300

Earlier quoted context omitted.

Honestly, we need a few AI coders to replace most of the developers in the world and then this won't be an issue. Bounds checking arrays and calloc instead of malloc isn't rocket science. It's a simple formula. The problem isn't the language it's the developers.

Considering that a change in language completely solves the problem, it's hard to get on board with your thesis.

A change in language does not completely solve the problem. Heartbleed was caused by buffer re-use without zeroing in between uses. A high performance network application could very easily do the same in another language.

See: http://www.tedunangst.com/flak/post/heartbleed-in-rust

Post reply on HN