Live data from Hacker News

Getting Past C

blog.ntpsec.org

481–490 of 504 posts

Re: Getting Past C

#481
post #425

Earlier quoted context omitted.

You still think in C limits of possibilities. Rust could just return Option as a result for [x] syntax.

And then people would simply call .unwrap() on the return of the [] operator, leading to the same situation. It's extremely common for an array dereference to always be within the bounds, by code construction (I'm iterating over the indexes of the array, or I have an index into the array saved inside some other structure, and so on). The programmer knows it will never be out of bounds. The assert!() within the [] ope…

And for safe iterations Rust has "for .. in".

Re: Getting Past C

#482
post #450

Earlier quoted context omitted.

> What do you mean by that ? Are you saying the standard decided to make it non-destructive moves ? You can still access a value after it has been moved out. use-after-move is allowed by the compiler. It places (stdlib) types in an unspecified but valid state (I've seen C++ code reusing these types and assuming that the state after move is something in particular -- it's not). Most optimizations you can do by reusing…

> Most optimizations you can do by reusing a moved value could be done automatically by the compiler Not if constructing the object is costly. It is very well possible for an object not to have a default constructor.

Like I said, most optimizations. Such datatypes are rare and this is eclipsed by the cost of reallocing common types like vectors.

Re: Getting Past C

#483
post #348

Earlier quoted context omitted.

> there's nothing stopping the compiler from optimizing the GC out. I don't know of a single language that comes with a GC that does this, do you? > He was comparing against what you'd get if you dropped down to ye olde C or Assembly and wrote the same algorithm there, without redundant work or waste. Right. I agree with this. But basically, we are arguing over an extremely fine semantic, which is "should you even wa…

> I don't know of a single language that comes with a GC that does this, do you? Java, .NET, Go, ML and Lisp compilers. Escape analysis allows to do that, even if just in certain special cases. Plus the more one uses value types and less heap, the GC needs to work less, specially if we take languages like Modula-3 into this mix.

If you never allocate, escape analysis doesn't have anything to do.

Re: Getting Past C

#484

Earlier quoted context omitted.

> What do you mean by that ? Are you saying the standard decided to make it non-destructive moves ? You can still access a value after it has been moved out. use-after-move is allowed by the compiler. It places (stdlib) types in an unspecified but valid state (I've seen C++ code reusing these types and assuming that the state after move is something in particular -- it's not). Most optimizations you can do by reusing…

Use-after-move is allowed because of existence of value categories, if you use a return value from a function its creation is usually elided or it is moved, but you can't access that temporary without directly referencing it. When you move an existing object (by specifically saying std::move), what should happen with it? Destroying the object is not a solution because its variable might be still accessible in existin…

> When you move an existing object (by specifically saying std::move), what should happen with it?

You don't destroy it, you treat it as an actual "move", where the compiler will not consider the original variable as accessible after this point, and not allow accesses after the move. The memory it took up is free for reuse, and no destruction code is run on the side of the code doing the moving (in the case of a conditional move this gets a tiny bit more complex, but not too much). "It's variable will be still accessible in local scope" is exactly what I'm getting at; you can enforce at compile time that this isn't the case by simply disallowing access.

You shouldn't have local references active, just like how you shouldn't have local references to the contents of a vector when you push to it. You can already invalidate local references to a part of a struct when something gets moved in modern C++. Being wary of invalidating local references is an established concept in C++; this doesn't exacerbate that problem.

It's even better if you track scopes in references which gets rid of the dangling reference problem entirely but at this point you've reinvented Rust :p

To be clear, I'm talking of a completely different model that could have been used in place of the rvalue reference and move model. Making C++ have linear types now would be tough, but it could have been done before. There are different tradeoffs there, but I suspect it would have been safer.

> all variables should have a "not-a-value" state and throw exceptions on use.

You can make this a compile time error. Like I said, linear typing is a pretty well established pattern.

Re: Getting Past C

#485

Earlier quoted context omitted.

You're right, to an extent we are talking past each other. I totally understand how your code can cause a dangling pointer. But you are using a raw C array, which completely goes against what I've been poorly trying to explain in this thread. What I am advocating is something like this: https://gist.github.com/anonymous/32df4da4949a6c1067c2be8f20... so TL;DR, if you don't want to deal with copying structs, malloc the…

> if you don't want to deal with copying structs, malloc them then push them to the dynamic array So, manually manage their memory allocation, but allow dynamic allocation of the array of pointers? Sure, there are some cases where that's useful, but if you're already managing memory for the structs themselves, you can probably just manage the memory for the array at the same time. > Else if you push a struct value on…

So, manually manage their memory allocation, but allow dynamic allocation of the array of pointers?

Yes.

Sure, there are some cases where that's useful, but if you're already managing memory for the structs themselves, you can probably just manage the memory for the array at the same time.

... then you have memory bugs. As your example code clearly shows. What you're suggesting (exposing the internal backing array of a dynamic array) is completely unorthodox and fraught with potential bugs, and I doubt if rust even does this internally.

All I can suggest is that you look up how dynamic arrays are typically implemented in C. The technique I describe is almost universally followed. This is also what happens with std::vector in C++ - std::vector doesn't manage the memory of the elements themselves, just its internal backing array.

Re: Getting Past C

#486
post #379

Earlier quoted context omitted.

While I now mostly agree with you that there is more unsafe code than there should be, I still maintain that the frequency of unsafe in a deptree is usually still small enough to be practically auditable, ignoring FFI. It could/should be much less, but it's not too bad. I've done such audits a few times and it's not been too hard and taken very little time. Auditing FFI is a whole other challenge, however :(

> I still maintain that the frequency of unsafe in a deptree is usually still small enough to be practically auditable Not in binary libraries, hence why it is important to have a culture to only use unsafe if it really must be used.

Well, yeah, but you don't really download Rust binary libraries yet :)

You do have C libraries which you access through FFI. This is inevitably unsafe. We should be auditing more there. Though IMO it's still manageable, for most crates.

Re: Getting Past C

#487
> Under Linux, some SECCOMP initialization and capability dances having to do with dropping root and closing off privilege-escalation attacks as soon as possible after startup.

I was under the impression that these specific things were actually quite hard to do in Go. I believe that both setuid/setgid and seccomp_load change the current OS thread (only), and since Go multiplexes across multiple threads and gives programmers very little control over which ones are used for what goroutines, I'm not sure how you would, for example, apply a seccomp context across all threads in a Go program. setuid/setgid are currently unsupported for this reason, with the best method being "start a subprocess and pass it file descriptors" (https://github.com/golang/go/issues/1435).

I'd be interested to hear if others have found ways to actually do this reliably for all OS threads underlying a running Go process.

Re: Getting Past C

#488

> Under Linux, some SECCOMP initialization and capability dances having to do with dropping root and closing off privilege-escalation attacks as soon as possible after startup. I was under the impression that these specific things were actually quite hard to do in Go. I believe that both setuid/setgid and seccomp_load change the current OS thread (only), and since Go multiplexes across multiple threads and gives prog…

I did this once, by writing a small C program which sets up the seccomp context before exec'ing the Go binary. Unfortunately Go's runtime makes a huge number of system calls in the background, and the whitelist kept growing.

Switched to Rust and there was only had one hidden system call left, getrandom used to initialize the hashmap

Re: Getting Past C

#489

> Under Linux, some SECCOMP initialization and capability dances having to do with dropping root and closing off privilege-escalation attacks as soon as possible after startup. I was under the impression that these specific things were actually quite hard to do in Go. I believe that both setuid/setgid and seccomp_load change the current OS thread (only), and since Go multiplexes across multiple threads and gives prog…

[deleted]

Re: Getting Past C

#490
post #474

Earlier quoted context omitted.

Again, if you're disallowing platform headers and writing tooling to make sure you're never calling libc, what's the advantage of writing in C? You have all the headaches of switching to a new language, with none of the features. And, honestly, one of the features is "vibrant community of developers." Even if Go and Rust were bad languages, which they're not, they'd still be better choices than C-with-custom-in-house…

I'm not advocating use of C, just pointing out a simpler way to check dependencies. To answer your question though, the advantage of using C is certainly not the notorious bad-habits standard library. C is fun and productive exactly where there's just you and some bits and bytes to bang around. Coding in the small. Not platforms and architectures.

You can easily access external symbols in C without including a header. The external refs in an object file are the accurate info about what is referenced.
Post reply on HN