Live data from Hacker News

Catch-23: The New C Standard Sets the World on Fire

queue.acm.org

261–270 of 275 posts

Re: Catch-23: The New C Standard Sets the World on Fire

#261
post #249
post #90

I actually like unreachable() a lot. What it does is that it invokes undefined behavior, that's all. It does nothing trickier than any other kind of UB. In fact, I could implement unreachable() like this: void unreachable() { (char *)0 = 1; }. Standardizing it however gives interesting options for compilers and tool writers. The best use I can find is to bound the values of the argument of a function. For example, if…

> What it does is that it invokes undefined behavior, that's all. [...] it can also be used in debug builds to trigger a crash How can it be used to trigger a crash (a specific behavior) if the behavior it invokes is undefined? Are you saying it would be defined differently for debug builds so that it doesn't invoke undefined behavior?

[deleted]

Re: Catch-23: The New C Standard Sets the World on Fire

#262
post #79
post #51

Earlier quoted context omitted.

I think the point is that if the `argc always greater than 2, permitting the compiler to optimize the entire block to just: return printf("%s: we see %s", argv[0], argv[1]); IOW, the conditional has been elided. But you're right in that the wording of the complaint doesn't match the example. The author presumably had in mind some of the more infamous NULL pointer-related optimizations, without spending the time to pu…

I interpreted the author's characterization to be about something like: 1 if (argc in which not just lines 4-6,8 go away (as you said) but also lines 1-2. It makes sense to me but I can see why the author would characterize this situation as "license to use an unreachable annotation on one code path to justify removing an entirely different code path that is not marked unreachable" . In a different world one might ex…

I find this especially surprising because line 2 may be exactly the reason why line 5 is unreachable. E.g. if puts("A") contractually throws an exception you cannot just remove it.

What am I missing in this example?

Re: Catch-23: The New C Standard Sets the World on Fire

#263
post #79

Earlier quoted context omitted.

I interpreted the author's characterization to be about something like: 1 if (argc in which not just lines 4-6,8 go away (as you said) but also lines 1-2. It makes sense to me but I can see why the author would characterize this situation as "license to use an unreachable annotation on one code path to justify removing an entirely different code path that is not marked unreachable" . In a different world one might ex…

I find this especially surprising because line 2 may be exactly the reason why line 5 is unreachable. E.g. if puts("A") contractually throws an exception you cannot just remove it. What am I missing in this example?

C does not have exceptions...

Re: Catch-23: The New C Standard Sets the World on Fire

#264
post #194

Earlier quoted context omitted.

> The security world will keep burning it seems. There is no alternative to network protocols and IPC that the stringtypes C has. You get a length and a byte array. If you trust the user, you can assume length is correct. Otherwise no.

Sure there are, as proven by distributed networking stacks not written in C. In fact Ethernet early days goes back to Mesa not C. UNIX did not invent networking, networking predates UNIX for at least a decade.

> Sure there are, as proven by distributed networking stacks not written in C.

this has nothing to do with the C language, but the structure of information. If the datatype contains a length, it has to be serialized anyway. There is no way of fixing this.

Re: Catch-23: The New C Standard Sets the World on Fire

#265
post #8

Earlier quoted context omitted.

There is actually another option. A more sophisticated type system. Let's say you had some pseudocode like this: let a = 5 let b = 12 let c = a + b The type of a would be Integer[5..5], the type of b would be Integer[12..12], the type of c would therefore be Integer[17..17]. In a more complex example: def foo(a: Integer[0..10], b: Integer[0..10]): return a + b The return type of this function would be Integer[0..20].…

> I believe this sort of thing has a name, […] https://en.wikipedia.org/wiki/Refinement_type But the concept is just a little bit over 30 years old. So don't expect it shows up in most mainstream languages before the end of the next 20 years, and don't expect it to come to the C languages ever. Meanwhile in mainstream ML-land: https://github.com/Iltotore/iron (Or for the older version of the language: https://github.…

I know C is never getting anything like this. But C is really just stuck being a very crappy ABI design language.

I do wish things like Rust had native support for stuff like this though.

And it really doesn't have to get in the way of anyone who insists on the more primitive type systems, but it would be nice to have a high level language which didn't have assembly-level (of abstraction) integer types. Why should I care that my machine works with square multiples of 8 bits at a time or care that they have specific wrapping behaviour.

Re: Catch-23: The New C Standard Sets the World on Fire

#266

Earlier quoted context omitted.

> because a is only big when b is small or some property like that Exactly, the expressiveness of the type system then (typically) becomes the obstacle: How do you express that a and b could each reach INT_MAX but their sum never exceeds INT_MAX?

Those kinds of assumptions are where you explicitly cast to a smaller ranged type with the option of an error if the sum does exceed a limit. The point of this type system is not to be able to fully encode every possible interaction between numbers in a system, but rather to remove unnecessary bounds checking in a bunch of cases and make it explicit in the few cases where you ARE actually making an assumption.

> Those kinds of assumptions are where you explicitly cast to a smaller ranged type

But how exactly do you do that? As mentioned, a and b individually can still reach INT_MAX.

I agree with your overall assessment, though. If a type system could represent (and recognize, and evaluate / automatically draw conclusions from) any possible restriction on the values of variables this would probably amount to the type system being able to carry out arbitrary mathematical proofs. The existence of such a type system seems rather unlikely.

Re: Catch-23: The New C Standard Sets the World on Fire

#267
post #8

Earlier quoted context omitted.

There is actually another option. A more sophisticated type system. Let's say you had some pseudocode like this: let a = 5 let b = 12 let c = a + b The type of a would be Integer[5..5], the type of b would be Integer[12..12], the type of c would therefore be Integer[17..17]. In a more complex example: def foo(a: Integer[0..10], b: Integer[0..10]): return a + b The return type of this function would be Integer[0..20].…

> I believe this sort of thing has a name, […] https://en.wikipedia.org/wiki/Refinement_type But the concept is just a little bit over 30 years old. So don't expect it shows up in most mainstream languages before the end of the next 20 years, and don't expect it to come to the C languages ever. Meanwhile in mainstream ML-land: https://github.com/Iltotore/iron (Or for the older version of the language: https://github.…

> But the concept is just a little bit over 30 years old. So don't expect it shows up in most mainstream languages before the end of the next 20 years, and don't expect it to come to the C languages ever.

Any specific results/papers from (refinement) type theory you hope/expect to see implemented in the next 20 years?

Re: Catch-23: The New C Standard Sets the World on Fire

#268

Earlier quoted context omitted.

Those kinds of assumptions are where you explicitly cast to a smaller ranged type with the option of an error if the sum does exceed a limit. The point of this type system is not to be able to fully encode every possible interaction between numbers in a system, but rather to remove unnecessary bounds checking in a bunch of cases and make it explicit in the few cases where you ARE actually making an assumption.

> Those kinds of assumptions are where you explicitly cast to a smaller ranged type But how exactly do you do that? As mentioned, a and b individually can still reach INT_MAX. I agree with your overall assessment, though. If a type system could represent (and recognize, and evaluate / automatically draw conclusions from) any possible restriction on the values of variables this would probably amount to the type system…

In some pretend rust. Pretend Integer exists and has the properties I described:

    fn special_sum(a: Integer, b: Integer) -> Option>
    {
        Integer::try_from(a + b)
    }
The type of the expression a+b would be Integer.

Obviously you can design a language which makes this much more ergonomic. That language can also avoid needing to use a type larger than a 32 bit wide unsigned integer to perform the operation through a special optimisation. Moreover, there's nothing stopping the type system from being able to maintain more sophisticated rules, for example there's nothing stopping a product type of two Integer having a rule applied which ensures that the sum of the two values cannot exceed 2*32-1.

Re: Catch-23: The New C Standard Sets the World on Fire

#269
post #63

Earlier quoted context omitted.

This would mean you’d have to insert a check every time you add two signed integers together, because signed overflow is UB. You’d also have to wrap every memory access with bounds checks, because OOB memory access is UB. There are also tons and tons of loop optimizations compilers do for side-effect free loops which would have to be removed completely. This is because infinite loops without side effects are UB. So i…

Another option would be to define behaviors for integer overflow and out of bounds memory access. Presumably they happen fairly often and it might be a good idea to nail down what should happen in those cases.

They don't happen that often. That's why they're bugs!

Re: Catch-23: The New C Standard Sets the World on Fire

#270

Earlier quoted context omitted.

Those things aren’t up to the language, they’re up to hardware. C is a portable language that runs on many different platforms. Some platforms might have protected memory and trap on out of bounds memory access. Other platforms have a single, flat address space where out of bounds memory access is not an error, it just reads whatever is there since your program has full access to all memory. The same goes for integer…

The problem is that here is a vicious circle. Most old computer architectures had a much more complete set of hardware exceptions, including cases like integer overflow or out-of-bounds access. In modern superscalar pipelined CPUs, implementing all the desirable hardware exceptions without reducing the performance remains possible (through speculative execution), but it is more expensive than in simple CPUs. Because…

> In modern superscalar pipelined CPUs, implementing all the desirable hardware exceptions without reducing the performance remains possible (through speculative execution), but it is more expensive than in simple CPUs.

Yeah, and that's how you get security vulnerabilities!

Post reply on HN