Conversation

The C standard forbids calling a function pointer that has a mismatched type with the function. So for example, calling a function `void foo(char *)` via `void (*foo)(void *)` is undefined. This is what type-based Control Flow Integrity (CFI) enforces to mitigate exploitation.
1
That's the reality of C. People use implementations including runtime optimizations like MADV_FREE and assorted mitigations that enforce subsets of type and memory safety. There are also implementations without a fixed stack, with garbage collection, etc. It permits all this.
1
It's a large part of why people use C in the first place. It's extremely portable and permits all kinds of different implementations. It can be compiled to the JVM with GC or WebAssembly. It supports architectures with different kinds of stacks or without traditional stacks, etc.
1
When an architecture / platform introduces a change like using shadow stacks (Intel CET, LLVM ShadowCallStack) to protect control flow, it's broadly compatible, because C doesn't permit programs to make all kinds of assumptions about how the stack and function calls work.
2
And by the way, I think the vast majority of C applications / libraries compiled for Linux have _FORTIFY_SOURCE enabled. If you define indexing from one object to another and dereferencing the pointer, you're declaring that to be broken, along with tooling like ASan, etc.
1
So you can define that to be broken if you like. That doesn't mean that the spec has to be maximally permissive. It's OK to say that more secure variants of C deviate from whatever the spec says. A well-defined spec would make it easier to talk about how tools change behavior.
1
They don't deviate from the specification though. They're standard and code is required to work with them. You want to make it more difficult to make secure implementations of C, and to permit code not to be portable to them, which is a major step backwards for security.
2
No, you definitely want a lot more of that, and you've got your own very arbitrary and subjective rules for what should be allowed. You want to disallow many safety / security features that are broadly used in production today. You want to disallow many optimizations. And more.
1
You want a C that's less portable and conforms to your needs while ignoring how platforms and other people are using the language. You're want to define it as much more permissive, rather than defining it as safer, which is what I think is desperately and obviously needed.
1
Permitting type and memory unsafety is a terrible idea, and completely incompatible with how C is used in the real world. That's really not going to happen. It breaks the portability of the language far too much and forbids the safety / security features being broadly used.