It's 2023 and memory overwrite bugs are not just a thing theyre still number one
theregister.com
theregister.com
And the list (top 10):
* Out-of-bounds Write
* Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
* Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
* Use After Free
* Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
* Improper Input Validation
* Out-of-bounds Read
* Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
* Cross-Site Request Forgery (CSRF)
* Unrestricted Upload of File with Dangerous Type
The good news is that you can build typesafe alternatives on top of the underlying std crap. Would be nice to have a standard <stdstruct.h> that provides common typesafe structs for things like os paths, sql strings, html strings, etc. just like <stdint.h> did for sized integers.
I’ve rolled my own typesafe wrappers fo my current C project and it’s been great so far.
I even carry a "what if" language concept in my mind (as in zero plans of implementation or anything like that, merely a thought experiment) where all names would be folded into types: in a lot of scopes you only ever have one of each. If you were to run Noah's ark you'd probably not bother remembering that you called the male penguin Frederick (or was it James?), you'd simply refer to him as the male penguin. The tools tools you'd want for dealing with all the situations where you need more than one in a scope would be just what the doctor ordered for that include semantic difference in typing thing.
> * Unrestricted Upload of File with Dangerous Type
Well, at least there are 2 without very refined standard methods to completely solve based on managed memory or strict types.
Doesn't every mature web framework prevent CSRF out-of-the-box with default middleware?
And yep, that leaves 1 of the top 10 problems not being automatically solved by one-time measures. I didn't include it because differently from managed memory and strict types, I have never seen anybody fight against the use of web frameworks (just against heavy-featured ones, but the light ones also include CSRF protection).
https://www.digitalmars.com/articles/C-biggest-mistake.html
I don't really understand the resistance to it. It does not affect existing code at all, it's simple, it's easy to implement, and its worth has been proven in D. (It's one of D's most popular features.)
My understanding is that they're currently more generally open to major security-oriented improvements than they ever have been in the past; it seems like now would be a good time for someone to put a slice/fat pointer proposal in front of them.
So what is my motivation in this, if I'm unwilling to make the effort to do a formal proposal?
I know a lot about C, having implemented a C compiler from soup to nuts, and spent much of my career working with it. I have lot of experience with it, and with array overflows, both of my own doing and debugging other peoples' code.
Very high on my list of reasons to do D was fixing that problem. I (along with Jan Knepper) developed the D array scheme. It works. It works great. It nails the problem so thoroughly that buffer overflow problems in D are very rare, because there just is little to no reason to use pointers to arrays in D. (I.e. buffer overflow problems in D happen when you don't use arrays.)
Interestingly, it actually speeds up your code. How is that magic possible? It's because 0-terminated strings need to be constantly scanned to find the length, and arrays just know their length. It reduces memory consumption too! Because you can slice any string - not necessary to reallocate it.
I've seen other extensions to C to solve the array overflow problem. They're all terrible, which is why one has to bludgeon people into using them.
My proposal is simple, works, efficient, and people take to it like a duck to water.
This problem also can only be solved at the Standards level, otherwise people will not use it.
Writing an official proposal is a significant time sink, and I have enough to do working on D.
In practice, C doesn’t have great idioms for error handling, and so a proposal here would also probably need to explain how the slice type would either reuse existing idioms or introduce a new idiom (like Rust’s use of sum types). That makes the scope much larger, which in turn might explain any hesitancy from the C WGs to accept something like this (although I’m not actually aware of any).
Now that you mention it, some method of forcing bounds checks in more limited circumstances might be nice for performance, that or a cleaner method of expressly disabling such for areas with critical performance and where the input is considered trusted or already validated.
I.e. they are not recoverable, so no need for error handling.
Many people have debated this and wanted such errors to be recoverable, but the principle in play is that the program is in an undefined state if there are such errors, and should not be allowed to continue as we wouldn't want it to launch nuclear missiles.
C’s lack of iterators also means that indexing bugs are more likely in idiomatic C programs than they are in equivalent C++ or D programs. This is another place where the standards body should improve things, but it also means that they’re somewhat constrained when it comes to slice proposals: making every index be potentially fatal is less palatable when your programming language doesn’t divert people away from indexing as much as it could.
In general my preferred way to handle OOB is an unchecked exception (like in Rust or C#), but that's not really an option C.
> Exactly equivalent to an already popular practice of passing array and its length next to each other.
Differences:
1. passing pointer and length next to each other is a convention. This means the compiler doesn't know about it.
2. you cannot turn array bounds checking on and off with a compiler switch (though we strongly recommend leaving it always on)
3. your array bounds checking convention won't be compatible with someone else's code (standardization is a big deal)
4. I've never seen it used for strings. Strings are a major beneficiary of bounds checking
5. You have to document this every time you use it.
6. it's clutter
7. there's no check that the array/length variables are not mixed up or otherwise confused with other variables
Most importantly, making a feature trivial to use makes for at least an order of magnitude increase in adoption of it. And what we want is adoption of it, right?
The proof is that array buffer overflows remain the #1 problem with C bugs.
P.S. I am always amazed at how C++ managed to get users to prefer:
std::vector<int> a;
over: int[] a;We can argue that C++ shouldn't have been that close to C, but that ship has sailed a long time ago.
Does it act like an implicit struct with standardized names for the size_t dimension and char* pointer fields?
extern void foo(size_t dim, char a[dim]);
struct vecStr {size_t dim; char a[dim];};
// Not guaranteed ABI compatibility with above
extern void foo(struct vecStr);
It would be really nice if, formally, structs mostly happened to work as expected (if ordered reasonably, behave as packed/smallest) and if there were language, not just some compiler, supported prefix attributes that enforced either packed (smallest), fastest, or if some other methods are needed later those. Possibly manually specifying a target alignment.Also at this point far too much code depends on signed integers as the default, but it would be nice if for added clarity stdint.h and similar had complimentary sintX_t equivalents to uintX_t even though sint8_t would just be a syntax alias for int8_t (preferably the reverse).
1. Introducing a new syntactic token (`..`) when there are already a lot of those in C.
2. Transforming an argument `char foo[..]` to the argument pair `const size_t foo_dim, char * const foo` leaves out a ton of existing APIs that order their arguments differently or breaks their binary compatibility.
3. It doesn’t accommodate APIs where both an element size and element count are passed, e.g. `fread(3)`.
C is a long-lived language so modifications like this have to work within a very large existing body of code. They can be made to work—blocks have worked out wonderfully, for example, by essentially saying they’re only for new APIs—but this type of extension requires a lot of consideration beyond “we could tweak the syntax and get this feature.”
2. APIs designed around pointers will not work with this proposal. But it would provide motivation for those APIs to change, and of course it will take time. Just like it took time for C function prototyping to take hold.
3. That's right, it doesn't.
> C is a long-lived language so modifications like this have to work within a very large existing body of code
It doesn't break existing code in any way. However, code would have to be modified to take advantage of it, sure. Just like _Generic.
That’s why some solutions to this involve applying attributes to function prototype arguments and variable declarations instead, so you don’t have to rewrite everything to discover and prevent bugs.
That's right.
> That’s why some solutions to this involve applying attributes to function prototype arguments and variable declarations instead, so you don’t have to rewrite everything to discover and prevent bugs.
In every case I've seen you have to rewrite the code to use bounds checking.
Note that the proposed arrays can be trivially converted to pointers, so you can still interact with pointer interfaces, and even length+pointer interfaces, without rewriting those interfaces.
I submit that the rewrite to using the proposed arrays is a lot easier than rewriting to use the explicit length+pointer.
ISO and WG14 are European organizations and thus culturally act like guilds. And you're not in it that's why.
My suggestion for improving C is add first class types and boxing and unboxing to phat pointers.
On a more cheerful note - I don't know how and when, I missed the event, and it may well be that it happened by accident - but C got the ability to access N-dim (N>1) array elements with dimensions known at run time, not only at compile time - learned about it in https://jorengarenar.github.io/blog/vla-usecases :
int n = ..., m = ...;
float (*arr)[n][m] = malloc(sizeof *arr); // `n` and `m` are tun-time variables with dimensions
if (arr) {
// (*arr)[i][j] = ...;
free(arr);
}It doesn't matter to me if C accepts the proposal to fix array overflows or not, as I don't write significant code in C any more (all my existing C code I still use was converted to D).
"Go does have undefined behaviour: if your program has a race condition, the behaviour is undefined" -- Ian Lance Taylor, https://groups.google.com/g/golang-nuts/c/MB1QmhDd_Rk
It annoys me to no end how Go could be created in the 21st century and willfully ignore so many lessons from the state of the art in language design. The more code I read and write (basically every day, by good programmers), the more disappointed I am in how much of a missed opportunity Go is.
There are other fundamental flaws in Go, but that would be off topic.
And well, like people keep pointing on that discussion thread, C and C++ undefined behavior isn't just an operation you don't know the result; it's a much deeper and crazier thing that no other language has (unless when they inherit it by sharing compiling tools).
As for "memory safety", maybe you're right on the terminology. But this does mean that in Go a data race, being UB, can do anything. There are no guarantees of no use after free, or read/write out of bounds, anymore.
If you create a data race in Go, you are in UB land. And that's one of the many many missed opportunities of Go.
The issue with system languages is that all the evidence points towards there not being space for a lot of them. So we only get one with whatever properties are most desirable; and it dries the landscape from opportunities for any other with similar features.
Thus, no, unless people decide there's some large new problem worth solving on them, we are not getting more memory-safe mainstream system languages. (But it's funny that you say sandboxing, because IMO that, probably with explicit effects is a nice thing to solve if you are trying to create the next generation system language.)
Still looking forward for when health inspection, goods certificates, car street safety regulations, finally arrive into the computing industry .
Most (all?) of the regulated industries you cannot just get into and compete; it’s expensive and needs resources small companies cannot have. These industries are already regulated; maybe the software (which is also already regulated) needs stricter controls, but we already have this. Why would we need more? If you are in an industry, healthcare for instance, and you use a library with memory issues; that falls under the regulations of the company creating the software directly for the healthcare provider(s). Why is more needed?
The little guy that wants to sell food on the street, needs to have same rules as the 5 star michelin restaurant in regards on hygine. At least in developed countries.
And yet they manage to create their business, go figure.
More is needed, because people have been educated to accept to pay for garbage software, in terms of quality.
In all other industries, most people will return on the same day products they have bought, with comparable bad quality to such software.