Well......
C strikes again.
Color me surprised.....
Google Project Zero researcher Tavis Ormandy on Thursday reported a severe flaw in Libgcrypt 1.9.0, an update to the widely used cryptographic library that was released ten days ago. Libgcrypt is a general-purpose crypto module developed for GNU Privacy Guard (GnuPG or GPG), a free software implementation of the OpenPGP …
And the modest mechanic takes whatever help they can get from a language and would prefer one that doesn't lay a minefield of bullshit bugs which are exploitable, cause crashes or otherwise delay or degrade the quality or security of the final product.
libgcrypt must be one of the most reviewed open source projects out there and if that can suffer an exploit I guarantee you that other software which doesn't receive the same level of scrutiny has plenty more.
Yes it did and the same applies - software which has been practically forensically audited and yet serious bugs are found from time to time, often the result of language constructs. If it happens there with hundreds of eyeballs on the code then absolutely guaranteed it happens everywhere else and with greater frequency.
Exactly. This is why proper people who work with high-voltage electrical systems never use insulating covers over the live components. I mean, obviously the competent mechanic knows that you never touch the bit of metal with 1kV on it, and never, ever does so: only an idiot would do that. It's also why nuclear power systems don't need elaborate fail-safe systems to prevent criticality excursions: the deeply-competent people who run them simply don't ever make that kind of mistake. Only an idiot would do that.
Here's the thing: 1% of the time we're all idiots. Really competent people might only be idiots .01% of the time. Some people are idiots all the time: there's that 'jake' guy who posts comments on The Register, and 'tfb' certainly used to be an idiot until he put his hand on a terminal with plate voltage on it – he was young so it didn't kill him, but he learned that the protection against idiocy is insulation.
And then there are the worse-than-idiots: the people who make the idiot mistake as we all occasionally do because we are human beings, but refuse to learn that human beings make idiot mistakes. Those people, well, let's not talk about them.
Good workmen use the best tools they can get, and in particular they use tools which protect them against their own occasional idiocy.
Another thread about blaming C, another pretense that there's something "better" worth the move.
People were eager to abandon ASM for C to write OS's because C was worth it. People were willing to abandon C because Rust was worth it... wait... I'm mean people were will to abandon C because C++ was wort... wait... we got Windows out of that.
Blaming C is indirectly blaming the entire industry for not creating something better that's worth moving to. Clearly we all sit here in hope, but clearly nothing is coming yet. I can't help to think that if a new language is created to emulate C but with add safety, nobody will bother with the emulation. Is there people all caught up on making a "safer" ASM derivative and then claiming it's a "better" ASM? Although being very serious here, I feel maybe someone should do this with binary systems as a whole, maybe throw our hat back in the ring with base 10 again.
agreed. Multiple languages is a good thing, writing serious code that is going to get re-use in C makes sense almost always. no-one wants libgcrypt written in Java or perl or. net. SSL exist for higher level languages but C exists for a reason. Someone has to bite the bullet and write code that can end up in Linux boxen that runs the internet. It's hard, I don't think we should bitch about the people that do it when we are probably all chatting over WiFi router that's almost entirely written in Linux friendly C. Probably most network infra runs Linux nowadays.
I don't think there is a market (yet) for and os plus network toolkit written in a higher level language even in 2021.
Because C works. It scales high and low.
No one has any alternative yet, rust is out best bet but bins are large. Memory safe languages don't scale down to the hardware consumers want. No one wants to spend £300 on a router so it can have the oomph to run Java or. NET Scala etc etc.
We all want a cheap router. and phone with a long lasting battery. and old pcs to still run useful software and raspberry pis.
Anyone who bitches about writing code in C should provide a link to the operating system alternative, since I can't find it in my version of Google.
I don't fancy waiting a second everytime I write cat for a jvm to load.
The other thing worth mentioning is that the touted alternatives often prove to be mayflies. Go on, put your hand on your heart and state now which of the new hawtness will still be the de facto standard a decade down the line.
If it's done in C, it'll still be maintained in years to come. If it's done in ${hiptrendy_this_week} it may well be more robust now, but good luck finding someone to build on it in 5 years' time.
according to the TIOBE index, C is WAY out ahead in popularity at ~17.4%, Java is just shy of 12%, followed by Python at ~11.2% and C++ at ~7.6%.
Rust is pathetically in 26th place at 0.61%. Even assembly language is doing much better than Rust, in 11th place at 1.64%.
(note these are still January numbers and so it might change around a bit when Feb numbers come out)
Anyone who bitches about writing code in C should provide a link to the operating system alternative, since I can't find it in my version of Google.
heh. so true.
there've been plenty of "code smell" and "code pattern" web pages (or even dead tree manuals) written regarding the *kinds* of things that get you into trouble with C coding, like best practices and things to avoid. I would think that reviewing and understanding the more sane and concise ones might be "step 1" in contributing to any public project _like_ gpg, OpenSSL, etc..
(And if that's not good enough, a clue-by-four and/or cat-5-o-nine-tails)
I am always surprised that nobody ever criticises the hardware.
There were many projects that came up with hardware designs that made it impossible to have a buffer overflow - and much else besides. Capabilities, for example - and to those who point out their problems may I point out that capabilities are WAY better than what we use now.
Unfortunately 40 years ago the hardware simply was too slow to implement much of this stuff.
Now we have the equivalent of a Morris Minor with a V10 engine - goes like hell but is unstable even in a straight line, generally corners dangerously and braking is just an afterthought that causes heart stoppages.
Good workmen use the best tools they can get, and in particular they use tools which protect them against their own occasional idiocy.
From the article..
Hanno Böck, a German IT journalist and hacker, expressed similar criticism and noted that the maintainers of GnuPGP don't appear to use an AddressSanitizer (or asan) in testing, which could have helped catch the bug.
You were saying? Its bit like your insulation. They could've used tools to help avoid issues, but chose not to.
I'm not denying C doesn't make it easy to make you shoot yourself in the foot. It has, however, been around long enough that there are many tools available to try to prevent that.
> It's a poor mechanic that blames his tools.
A poor mechanic is a person who uses a worn out wrench that unless held at a perfect angle is guaranteed rounds off bolt heads.
If he was a good mechanic he would invest in tools that makes it easier to do his job correctly.
> It's a poor art critic that blames the artist's brush.
A true artist could produce a masterpiece with little more then a bowl of old mashed potatoes, some wire, and a magic marker.
That doesn't mean that is something he should be using.
poorly written/reviewed C code, you mean.
last I checked, compiler warnings in llvm should spot a lot of buffer-size-related issues. It's not perfect but apparently will spot many things. As a test, I called 'strncpy' with a buffer that is too small for the size I specified in the 3rd argument, and got this warning: "warning: 'strncpy' size argument is too large; destination buffer has size 16, but size argument is 32 [-Wfortify-source]". But using 'strcpy' with a string that was too long in the 2nd argument gave NO warning.
Suffice it to say that the big problem here is "NOT using" code that checks buffer size, and hopefully if you DO use code that checks buffer size, that code warnings are paid attention to if you accidentally get it wrong.
And, of course, best practices and peer review to go with it.
The problem isn’t C - the problem is developers misusing it. Would you ban formula 1 because the average motorist can’t handle a racing car? No. You have to use the tool appropriately, and make sure that the programmer has the skills to use it.
I’d argue that that struct should contain the buffer size as well and only access for read or write via a function which checks the size before accessing the buffer. When calloc’ing (or malloc’ing) or freeing the buffer size gets updated by the function making the updates.
Would you ban the use of pure nitroglycerine in mining just because dynamite does the job effectively and is far safer? Hell, yes! Sometimes appropriate use of tools means recognising that the old tool is so dangerous that the only sensible thing to do is to use newer, safer tools.
You advocate manually extending the struct and adding manual checks every time it's used, but you haven't made an argument for why that's better than switching to a language which does that for you automatically, eliminating the entire class of errors.
This incorrect assertion is rolled out every so often. The "class of errors" is "didn't run a static and dynamic analysis tools".
The language level issue is that the C code is a little messy and should be tested with asan/ubsan.
The code level issue is ad-hoc buffer management, rather than a restartable block function. e.g. write_all() implemented in terms of write_some().
Because the buffer management is ad-hoc, an invariant got missed.
That's an argument for explicit data structures. eg struct vec { size_t m_size; size_t m_cap; char *m_data }; so manipulation of fixed sized buffers can be done without allocation safely and efficiently.
C and C++ will still work in ten years time, modern compilers just make life better.
You still need to take care in your work and be professional, only in software is that considered a burden.
"You still need to take care in your work and be professional, only in software is that considered a burden."
At the end of the day, the programmers that introduced this bug have been maintaining a complex piece of software that has been a cornerstone of internet and software distribution, allowing signing and identification, as well as encryption. It's fair to say that the are careful and professional in this work in almost every sense, except for the "getting well paid" bit.
And, yet, these bugs happen. And have happened again and again. "They should have taken more care" is not a sensible response.
At the end of the day, the programmers that introduced this bug have been maintaining a complex piece of software that has been a cornerstone of internet and software distribution, allowing signing and identification, as well as encryption. It's fair to say that the are careful and professional in this work in almost every sense, except for the "getting well paid" bit.
I have a very little background in crypto and substantially more in build/release engineering, these communities are different.
The woefully underfunded project, could use some support from release engineering, for example using the tools that I and others suggest.
You don't see to be aware why this is done in C, why crypto is done in C generally.
Your putative solution is to prohibit bugs "And, yet, these bugs happen. And have happened again and again. "They should have taken more care" is not a sensible response."
I reject your attempt to gatekeep "a sensible response".
An entire industry seems to think C and C++ are worth keeping. You're going to need to do better than unsupported assertion, and pretending that bugs, or under resourced projects are intrinsic to C.
So perhaps some evidence might be useful at this point, perhaps you could rewrite in your language, and show how you can meet the same performance or portability requirements, and then lets compare for clarity and safety.
That's a "sensible response".
These projects were done in C because there are lots of C programmers, it's fast, and when these projects were started, it was the obvious choice. Some of these are still key points: there still lots of C programmers; others are not so: C is fast, but no faster than Rust or Go. If Rust and Go continue their climb in usage, the availability of knowledge will probably change.
Is it worth moving? A whole sale re-write, almost certainly not. Whether it could be done incrementally, and whether that would be useful, I don't know. It would certainly require a substantial investment and that is perhaps the bigger issue. I am not and would not advocate wholesale porting to Rust of anything.
Could it make better use of tools? Maybe, but I doubt that the developers have not thought of using these tools. It maybe that the code is old enough that the tools work poorly, either missing problems or more likely giving a high false positive rate. For a piece of software as old and critical as GPG, changing code to avoid these kind of false positives would not strike me as good practice either. Perhaps you could try them on GPG?
In the end, though, C is just a tool. It is and will remain a useful tool, but it's not the only one and it has serious flaws. Ignoring this and blaming people for their lack of skill is and remains not sensible.
Sorry you lost me when you started throwing around stuff like "C is fast, but no faster than Rust or Go."
May a I recommend a profiler, perhaps start with finding out what valgrind does, what any of the tools do, before digging any deeper.
Hint C is very well supported by the tools. And the reason for C's usage in Crypto is it's easier to audit the generated assembly code.
Rust and Golang, please.
Rust compiles to an intermediate representation which is then compiled down to assembly with LLVM. It also has good support for using the C ABI, or compiling down to the C ABI.
So, yes, it works with valgrind, mostly useful for when working with C built libraries. Yes, it works with profilers. I don't know about the ease of auditing generated assembly, but as it is LLVM which does this and is not rust specific, I would guess that it should be similar.
In my use of these tools, I have found them less slick than using over C, but functional, mostly either because they are harder to launch or because you had to do some name munging. This was last year, though; Rust is quite a fast moving target in this way.
Go I don't know about, as I've not used it other than playing.
Stop lumping C and C++ in the same boat. C++ is a vastly different language today that provides language features and a standard library that completely mitigates against the sorts of bug that crop up in C code time and time again.
It might not be perfect, and it’s still possible to write bad code in C++, but that doesn’t mean it’s not a million times easier in C.
Sure, it has much better type safety that C, but it still contains a lot of cases where undefined-behaviour can be invoked. Once that happens, all (behavioural) bets are off. Some of these behaviours are very difficult to spot during code review, and even static analysis is unable to catch all of them due to decidability issue.
So, whilst it may be true that C++ "completely mitigates against the sorts of bug that crop up in C code"*, it introduces another set of undesirable behaviours that have the potential to be equally as difficult to detect and prevent.
* Is there a citation available that supports that claim?
The one thing that I found that C++ helped a lot with (in windows coding) was the ability to manage GDI handles automatically, freeing them when no longer needed. This also assumes that you're not abusing exception handling and that object unwinding functions properly if you do.
Otherwise, my C++ code nearly always looks a LOT like C code. Personally, I think it becomes more maintainable that way. Properly designed templates and operator overloads can help, too.
(but if your C++ code throws exceptions and requires try/catch everywhere, you're doing it wrong)
One thing that I believe Micros~1 got mostly right is the COM object for OLE 2.0 . It kinda demands C++ and, by design, helps to prevent memory leakage and similar things. You could make _THAT_ a qualified "citation" for mitigating SOME of the things that shared objects and object lifetime issues might otherwise cause.
I've never been in a serious car accident but I still think seat belts and air bags are a good idea. Just because it's possible to write secure code in C if you know what you're doing doesn't mean one shouldn't need a pretty good reason to reject languages which make mistakes like that more difficult, especially in code as sensitive as crypto. There's a big difference between "possible to do it right" and "you have to go out of your way to do it wrong." Even if you don't want to go all aboard the rust train, modern c++ gets you most of the benefits while being somewhat easier to transition to incrementally. If you want to stick to C (and there are reasons why you might, want to for library code like that) then it behooves you to write comprehensive tests and use static and dynamic analysis tools. And you would be doing that even in a safer language like rust.
but you haven't made an argument for why that's better than switching to a language which does that for you automatically, eliminating the entire class of errors. - title was too long..
Neither have you.
You confidently assert, and then stand on high, saying refute me.
Prove your case, because every single time it boils down to "I don't know C or C++ well enough to pretend I know the language, like I can pretend I know another language with training wheels".
This bug could be in any language as the point is that it needs to digest a block at a time, the most commonly exploited bug is input validation failure, and that is typically in "safer" dynamic languages.
So please, provide some evidence that your contention is remotely true.
Prove your case, because every single time it boils down to "I don't know C or C++ well enough to pretend I know the language, like I can pretend I know another language with training wheels".
Here's the thing: I learnt C on a PDP-11. I learned C with a compiler which still knew about =+
and int x 1
. Later, on a bigger, if not altogether better, machine I persuaded a version of cfront
(better not ask where it came from, I suspect it was not completely legal), and taught myself the lnguage it supported, at least until the keyboard became so covered with vomit I had to stop. I have forgotten more C (which is almost all of it now) than you ever knew.
But, you see, I'm not an idiot: I may not be smart, but I'm not an idiot. And so when it became feasible to write programs in languages which gave you a little more protection than C at some tiny cost in performance (often zero) and size (usually not quite zero), I moved on. After all many of those languages are much more pleasant to write in than C ever was.
Unfortunately, as has become very apparent, most people are, in fact, idiots: they're incapable of learning, even after their thousandth finger is removed due to the mistakes we all make, that perhaps the tools they use are dangerous and they should be using safer ones.
Entertainingly, some of these people now argue that the solution to the problem is to use tools which will examine programs written in languages with no protection and exercise them in various ways in order to find some – but never all, not even in theory of the problems that a language with more safety features would simply have ruled out. These are people who should simply not be allowed near computers.
So in other words your experience doesn't really extend past c99 into c11.
Congratulations on " I have forgotten more C (which is almost all of it now) than you ever knew."
That's a decent straw man, lets leave that to burn in the field.
The fact remains, a blanket ban on the main alternative to assembly language is daft.
The fact remains, you've offered no proof, and an insult in it's place.
Try again.
I advocate running static and dynamic analysis tools. I measure my software, with profiles.
I use every tool I can find to help me. That's a language invariant approach.
The tools differ from language to language, but the use of static and dynamic analysis tools is professional practise.
"I have forgotten more C (which is almost all of it now) than you ever knew."
I've been using C on and off since September(ish) of 1974, when it first got to Berkeley with UNIX. Your statement, if true, suggests that you no longer know anything at all about C. This is reflected in your commentary on the subject.
The ad hominems you seem to delight in (this time implying that everybody other than yourself is an idiot) suggests that continuing to read your reams of nonsense in support of your fool's errand of a crusade is counter productive.
But tfb is 100% correct.
Any tool which requires other tools to assure that the first tool you used did the job properly... is completely and utterly NOT a good tool. Stating that it's the users job to oversee and correct fundamental construction errors in the tool - the fact that the tool has almost no see facto structural security - is making excuses for the tool.
Human beings created tools to empower and simplify their workflow. We use pliers to multiply - empower - our grip force. We use dishwashers to simplify our lives. If the dishwasher does such a miserable job that you must hand-rewash every single plate after the cycle, you don't say "Well, I'll keep it because it's here".
If you have a tiny lawnmower, then clear out 4x your property space, do you keep expending 4x the effort on the new land or purchase a new tool that is suited properly for the job??
You replace it. Period.
You never, ever excuse the tool for failing to properly perform for the user, if used in its intended style.
If C has bad security then it should be either fixed in the tool - C constructs or within the complier - or replaced. Again, period. A user should NOT have to constantly remember the inner failings of the tool and then work around said failures, on a never-ending basis, to compensate for the fact that your automated tool isn't doing what you need it to.
It's making excuses for an out-of-date tool that you are either too cheap, or too lazy, to replace with the proper, modern one, that helps rather than hinders you.
You seem to be wanting perfection from software.
So C is to save me from writing assembler, it's doing sterling service at that.
If you want to write more high level code that is more likely to be correct by construction, it's not that difficult in C.
#include <stdio.h>
#include <stddef.h>
#include <string.h>
typedef struct vec_tag {
size_t m_len;
size_t m_cap;
char * m_buf;
} vec_t;
#define vec_used(v) ((v).m_len)
#define vec_capacity(v) ((v).m_cap)
#define vec_begin(v) (&((v).m_buf[0]))
#define vec_end(v) (&((v).m_buf[vec_used(v)]))
#define vec_free(v) (vec_capacity(v) - vec_used(v))
#define vec_clear(v) do { (v).m_len = 0; }while(0)
#define vec_push_back(v,c) do {\
size_t t = (v).m_len; \
t += (vec_free(v) > 1); \
(v).m_len = ((t != vec_capacity(v)) \
? (*vec_end(v) = (c), t) : t) ; \
} while(0)
#define vec_copy_back(v,b,e) do {\
size_t t = (v).m_len; \
size_t l = ((e)-(b)); \
t = ((vec_free(v) > l) ? t+l : t); \
(v).m_len = ((l < vec_free(v)) \
? (memcpy(vec_begin(v),b,l), t) : t) ; \
} while(0)
int main(int argc, char ** argv){
char local_buf[13] = {0};
vec_t buf_vec = (vec_t){0,sizeof(local_buf),&local_buf[0]};
vec_push_back(buf_vec,'h');
vec_push_back(buf_vec,'e');
vec_push_back(buf_vec,'l');
vec_push_back(buf_vec,'l');
vec_push_back(buf_vec,'o');
vec_push_back(buf_vec,' ');
vec_push_back(buf_vec,'w');
vec_push_back(buf_vec,'o');
vec_push_back(buf_vec,'r');
vec_push_back(buf_vec,'l');
vec_push_back(buf_vec,'d');
printf("vec contents: %.*s\n",((int)vec_used(buf_vec)),vec_begin(buf_vec));
int n = argc;
while(--n >0){
char * val = argv[n];
size_t sz = strlen(val);
vec_t argument = (vec_t){sz,sz,val};
vec_clear(buf_vec);
printf("vec contents: %.*s\t | ",((int)vec_used(argument)),vec_begin(argument));
vec_copy_back(buf_vec,vec_begin(argument),vec_end(argument));
printf("vec contents: %.*s\n",((int)vec_used(buf_vec)),vec_begin(buf_vec));
}
return 0;
}
C has a place, I would prefer C++ in most cases, but C99 through C11 are easier to write correct code than k&R and C89.
C has a well defined and simple ABI that is well used by other languages to provide extensions.
The C ABI is not going away, C is not going away, and what do you proposed for where C++ is too big.
Or a new compiler backend hasn't yet been brought up?
I want to be able to have fine control over memory layout, so I can do bit twiddly things cleanly
typedef struct {
unsigned char flag:1;
unsigned int :0;
unsigned char nibble:4;
unsigned int :0;
unsigned char munch:2;
unsigned int :0;
unsigned int mouthful:18;
unsigned int :0;
} someStructure;
What is your alternative to masks and shifts here in someother language?
Finally, in C or C++, the code lasts, in other languages, the code rots.. YMMV
but if it matters do it in C++ or C.
"Any tool which requires other tools to assure that the first tool you used did the job properly... is completely and utterly NOT a good tool."
You have obviously never spent any time in a machine shop. Walk into any automotive machine shop and ask them how many tools they use to verify that the tools used to linebore and deck an engine block did, in fact, do their jobs correctly. The answer might surprise you.
Next step: How many more tools are used to verify that other tooling produced the correct size of crank and rod bearings & pistons and rings ... and that they are gapped/spaced properly.
Etc.
I have spent time wrenching my own machines.
What everyone is suggesting is a your favorite torque wrench. Which you love in terms of design but can't hit a setting for its life, so after using it you need to grab ANOTHER torque wrench, one that is actually accurate, to tighten to the actual spec.
I actually work for a manufacturer. Sure, we use micrometers every single day; I have mine, sitting right next to me, and it gets use constantly throughout the day. But that is to DOUBLE CONFIRM that the specs were met, not to readjust the machining at time of manufacturing because the cast / wax / mill / 3D print / design / mould was off from the start. If that's the case...we stop using the source and wonder why things went wrong in the first place.
One of our 3D printers can't be depended upon to make a print that accurately translates into the finished product. What do we do with it? Use it for early prototypes to examine (only); we never use it for final production, we can't depend upon the output.
So depending upon C when the output can't necessarily be depended upon unless you worry over every little detail, rather than switch to a tool that is designed and performs as expected...is STUPID. It's just BLIND STUBBORNNESS in not wanting to switch because you have too much internal resistance to the idea.
If C creates compact code, then as someone else said reserve it for when that is the highest importance. But to stick with it for everyday tasks, where the finished product is expected to perform under difficult and possibly unknown and cooked conditions, is just being thickheaded.
C is arguably the perfect tool for its job. The Unix way is to layer and pipe tools and this has proven to be a better approach than monolithic solutions for many use cases.
You might be able to argue that c is the wrong tool for job x, but there is a huge weight of evidence that c is a good tool.
c is great, gcc is great too, I would recommend make, and automake, and an ide that bootstraps automake, and entr to know when to run the build, and Ci and and and.
Let's extend your car analogy. The owners of a certain car keep suffering horrific injuries because the car lacks safety equipment, crumple zones, airbags etc.
Then some wag says "oh it's not the car, but the way it was driven". And then the same thing keeps happening and happening and happening.
That is C. You could force developers to pair program, code review each other, run static analysis tools, constraint themselves to MISRA C and a bunch of other stuff that might mitigate the risks but it will not eliminate problems that are inherent to the language.
And what the hell are we doing mitigating risks in the language when we could just switch languages? About the only reason to write in C is because you have no other viable option. If you do have an option then the alternative is probably a far superior one.
Thumb up because I like your analogy. The thing is that sometimes the only alternative to C is Assembly language. Use C when speed is of prime importance, or when you are resource constrained - otherwise there may be better alternatives. Use the right tool for the job. Not every problem needs a hammer or a screwdriver.
Sometimes but not always. Something like a crypto library could be written in Rust instead and have C bindings. The performance would be the same (potentially better) and there would no errors to do with memory corruption, leaks, data races, buffer overflows etc.
And the further up the layers you go, the reason for using C or C++ diminish to practically zero because of the profusion of other languages.
The thing is if you intend to drive a car at speed into a brick wall, you are the problem not the car.
Most use of C can be replaced with C++ that's the majority case.
The rest boils down to small systems, kernel usage, or places where hardware control is desirable.
Outside of those cases, new hardware is easiest to bring up a C compiler to bootstrap the rest of the stack.
Now, there is a decent argument for requiring a reason why C, when C++ is available for that use case.
And if C++ is available, could a scripting language be used, with native libraries instead.
None of that means you don't need to know what you are doing, and be careful not to drive into walls at speed.
Would you ban formula 1 because the average motorist can’t handle a racing car?
No, but I certainly would mandate that Formula 1 cars have absolutely heroic safety precautions so drivers don't die. And when drivers do die I would conduct extensive investigations as to why they died, and start mandating new safety precautions which would prevent that happening. And this process would work pretty well: three drivers were killed in 1955: between 2000 and 2012 none were.
In other words I would modify the tools that drivers use to race to make them as safe as they possibly could be: the same way people modify the tools – programming languages – people use to write programs to make them as safe as they possibly could ... oh, no, sorry, they don't, do they?
While we're on the F1 argument. All F1 drivers need a Super License. The same is not true of programmers. I play with C and C++ but I don't know enough to produce safe working applications, so I don't.
Unfortunately when it comes to programming (open-source or not), the resources are often constrained too much to ensure a completely safe outcome. In this particular case, the issue was identified quickly and mitigated and I think we should be thankful that there are people out there donating their time to this effort to keep the rest of us in check (and piss off the NSA who probably also found it and thought they had a new exploit).
The problem is exactly C and those using it. They believe it was a language born perfect, inscribed by some god on stone tables, and as such it doesn't need to be modified - ever.
Unluckily the world changed a lot from 1970, and C baldy needs an overhaul to become far safer to use. Otherwise or people keep on chasing dangerous bugs that could be instead removed at the language/compiler level, or they stop using C and move to safer languages, that can still be as fast as C, without all the risks.
I write and read a lot of code in different languages at $JOB.
A lot of the time, the answer to the problem is "don't do that"
So don't use C if you can use C++.
So don't use an On^2 algorithm if a better one exists.
So don't sit on an open socket for days and expect the other side will still be there.
C is fine, using explicit data structures covers quite a lot of the issues people have.
Every sizable C project ends up defining these data structures, C++ gives you them out of the box.
The idea it's "unchanged from 70s" is just daft.
I don't remember seeing this sort of code in my K&R
#include <threads.h>
#include <stdio.h>
int run(void *arg)
{
return printf("Hello world of C11 threads.");
}
int main(int argc, const char *argv[])
{
thrd_t thread;
int result;
thrd_create(&thread, run, NULL);
thrd_join(&thread, &result);
printf("Printf in thread returned %d at the end\n", result);
}
Yes, they did basically standardize pthreads
wtf? I wouldn't be surprised if systemd is doing DNS these days but isn't DNSSEC a server-to-server thing not a client to server thing? If so wtf is systemd doing with it?
on the topic of DNSSEC I came across this blog a while back and found it informative, rips into DNSSEC https://sockpuppet.org/blog/2015/01/15/against-dnssec/
"In fact, it does nothing for any of the “last mile” of DNS lookups: the link between software and DNS servers. It’s a server-to-server protocol."
Been running DNS myself since about 1997(both hosting authoritative BIND9 servers as well as hosting domains with Dyn in the last decade or so), though no DNSSEC.
There is nothing preventing you from using DNSSEC on your Clients as well.
And besides Split Horizon DNS, Captive Portals and other manipulation of DNS lookups everything should work just fine.
You'll only get problems with the above when using DNSSEC signed domains, which is unavoidable and the whole point of DNSSEC.
Also DNSSEC is not a server-to-server protocol (or even a protocol) at all. It's a standard for signing DNS records. The domain owner and the owners of all dns levels above them will sign their respective records and publish the signatures in DNS. Any dns resolver can verify these signatures or simply pass the associated queries along for someone else to verify them.
What you are going WTF about is similar to saying "Doesn't GNU produce Bash? Why would they need a entire crypt library?!"
> isn't DNSSEC a server-to-server thing not a client to server thing?
It's a peer to peer thing. Client computers need to be able to validate DNS using DNSSEC for DNSSEC to have any point at all.
Systemd is initd, but it's also a entire project that involves lots of different programs and daemons. One of those programs is systemd-resolved. It provides local DNS resolver features for the OS and can be used by other programs through localhost DNS look-ups, resolve/nss, and via d-bus.
It's very much optional. Most distributions don't have it enabled by default.
That's not what that phrase means.
The state invariants are those defining the ad-hoc data structure in use here. A vector with cap fixed at 128.
The loop essentially implements pop_front on vector. The bug is that you can overwrite the 128 byte buffer, then the int, then you have a function pointer that be will used.
In the jargon, you have "an attacker controlled address". The "state invariant" is amount of free space is len-(sizeof(buf)/sizeof(buf[0])) >= 0 .
That is enforced "far" from where the state is manipulated, so "distant state invariant".
I prefer ad-hoc data structure, same idea different focus.
In well designed code you would keep the state that needs to obey some condition close to (e.g. in the same file, ideally) the functions which manipulate that state or depend on those conditions being true, so that a reader can more easily verify that no one is doing something that violates the assumptions (which are ideally documented in comments). If you have stuff modifying that state from all over the place, without going through some common functions which verify that what they're trying to do is ok, the odds of accidentally making a mistake go up substantially.
This is just another instance of the problems the community had with OpenSSL back a couple of years ago. Lone or understaffed volunteers maintaining legacy code that over time became more and more of a rats nest, that the current devs have been propping up while their pleas for help fall on deaf ears. A lesson not learned apparently.
So instead of pretending this is "another" one off case, the open source community should try to tackle the underlying issues head on. Short term support the projects which form the primary attack surface of open source software, build up the team an infrastructure to start auditing some of this stuff, and last (but only because it will take the longest) some of these projects should be re-implemented in a more modern development environment.
We may nostalgically cling to ancient Perl scripts and ANSI C code. Lets face it though, just for things that are only local processes, and with no dangerous user or attacker controlled inputs. We need to move stuff that has an attack surface to a more appropriate platform.
This code base looks like it's overdue for a deep cleaning, but even then, the next commit could easily introduce a new set of vulnerabilities. Even good programmers make bad bugs, and they are often harder to find. So moving to a more robust platform, including both the language and code profiling tools is the only sane option.
A Rust library for parts of gpg already exists. It's called sequoia-pgp and while it does not replace gpg, it implements quite a few of the important features in a compatible way.
It should be possible to write a wrapper for it that emulates gpg usage sufficiently to replace it for most cases. Of course extending sequoia and writing a wrapper might be more work than your suggested solution.
Link: https://sequoia-pgp.org/
In Rust there is "unsafe" code - just because it is all in one place does not really make it safer or necessarily easier. Rust is not the answer to everything and using Rust doesn't mean your code is OK. As in C, Rust has its share of bad coders too.
Maybe I just got a bad taste of Rust when I saw all these damn crates being assimilated when I built the project. I still have to verify all those crates when I certify my code. With the custom standard C library I have to use with some of my clients; that library has been already certified by the clients that use it. In some circles, Rust still has a ways to go yet.
Typical arrogant moron response.
This buffer overflow would have been flagged either by libasan or by Valgrind. This one is particularly easy to detect, since the size of the buffer being overwritten is known at compile-time. The buffer
array is statically allocated.
The fact that it slipped through into a release tells me this version of libgcrypt has never been Valgrind'ed, or libasan'ed. Because Real Men Don't Debug. Debuggin' is for Pussies.
So, no. The one who needs to get back in their lane and learn quite a bit more about developer workflows is the one who didn't do the required testing.
To me, this doesn't sound arrogant, but someone who is bit pissed off that after years of maintaining a piece of software, they only get any attention when they get something wrong. Then loads of people jump up and tell them how they could do it better; but no one actually helps them to make it better.
"Just decrypting some data can overflow a heap buffer with attacker controlled data, no verification or signature is validated before the vulnerability occurs," explains Werner Koch, principal developer of GnuPGP in the security advisory. "...Exploiting this bug is simple and thus immediate action for 1.9.0 users is required."
A basic ingredient* for a nouvelle haute cuisine dish of a mega meta data 0day exploit for export/import into FCUKd systems with failed exclusive executive SCADA Administrations in worm ridden Cabinet Offices. And quite the colossal pick me up to revive and excite the jaded palate it is too.
Bland it certainly aint. Overpowering it certainly can be, thus the advisory to use wisely and/or sparingly, for just like too much of anything, IT can all too easily certainly kill you.
And as one of those dual use type items which can either be a blessing or a curse, certainly a weapon not to be trifled with.
* ... just some cryptic data which may or may not have been also further encrypted/dissected and reassembled
>Valsorda points out that the vulnerability was introduced in an effort to mitigate timing side channel attacks,
Side channel mitigation is probably the hardest, most thankless and most boring job imaginable. It can destroy any structure to code, and all the old rules about how to create and maintain good code fly out the window.
We need some new science here. Sadly it's my bet that AI in some form or other will beat us to it.
Having programmed on an IBM mainframe, under the easy-to-use MTS operating system, I know that buffer overflows were basically unknown in that world.
But this was because of how I/O worked on that system, being record-oriented instead of character-oriented. It was not because the languages we used had array bounds checking or similar features that come with an overhead cost.
If it were possible to fix these frequent issues by going from C to a language that is a viable alternative to C, because like C, it is compiled and doesn't include built-in features involving significant overhead, such as array bounds-checking, that would be great. But I think that this is not the case, and changing to another language means changing to something like C# or Swift.
Maybe, while waiting for change in the operating systems, we could compromise by putting bounds-checking where it is needed: in the I/O library, rather than in the whole language?
I am so tired of people defending vulnerabilities like this by saying that it's not C's fault and, basically, people should be better programmers.
There is mountains of evidence that even the very best programmers cannot use C safely. It's not just "inexperienced" or "underskilled" people using a tool that's too sharp for them. Maybe it's true that a bad workman blames his tools, but it's equally true that every workman blames a bad tool.
As Tony Hoare often said: the designer of a language is responsible for the mistakes frequently made by its users. And C is notorious for the mistakes it routinely lures its users into making.
There are plenty of vulnerabilities in other languages. SQL injections, anything with eval(string). Cross site scripts. Un escaped html. Timing attacks.
To be fair, they should add proper array bound checking as an option in C (along with a proper string library). Other than that there is nothing special about C compared to other languages...
Only 14% of open source projects are written in C.
(That surprised me. I thought it would be higher.)
https://www.techrepublic.com/article/the-10-programming-languages-developers-use-most-in-open-source-projects/
Yet nearly half of all vulnerabilities discovered in open source software are in C code.
https://www.whitesourcesoftware.com/most-secure-programming-languages/
C is not fit for purpose anymore. (If it ever was.)
I used to program in C for my first jobs back in the early 90s. I used to think it was great. I resisted moving to C++ for a long time - mostly due to misguided performance concerns. It wasn’t until ‘99 that I finally switched 100%. I’ve been using C++ ever since. I still have to deal with C code from time to time in the form of open source libraries, etc, But I will almost never ever write pure C code again*. I am always amazed that people still use C despite the lack of type safety and the constant need to reinvent the wheel for even the most basic of tasks.
* The SDK for the Oso Memory Profiler (a side project of mine) is written in C. The one redeeming feature of C is its simplistic ABI that makes it easier to call from other languages. I really wish the C++ committee would provide something similar instead of wasting their time trying to turn C++ into Javascript.
I believe that C was basically created to write UNIX. Compared to writing an OS largely in assembler it must have been a joy! Essentially as fast/compact and largely CPU portable.
In fact C has often been called the universal assembler!
But part of the underlying unsafe aspect of C is the power to do things with addresses (both data an execution start points) that you need to manipulate in an OS, but start to become very risky within an end user program/library.
It is easy in cases like this to say "Oh that would not happen in $LANGUAGE" but it is avoiding the elephant in the room. That code was written in C, to rewrite it in a new language and port support to all systems using it is not a tasks anyone is willing to fund/undertake. So the arguments are academic, the blaming is a pointless distraction. The least-worst option that is open is to improve the development and testing process to use the tools already available for static and dynamic code analysis and fix the warnings.
This is the struct in question:
struct gcry_md_block_ctx
{
char buffer[128];
int stuff;
function_ptr func;
}
.....but would not a simple change to the struct obviate the problematic buffer overflow problem completely? And this change could be implemented as a "programming standard" something like this: Always put buffers at the end of any struct. Of course, other standards would also help, such as a buffer size check, as mentioned elsewhere. Both of these suggestions are "programming standards", and I realise that they only mitigate issues with C, and do not eliminate the issues.
Suggestion:
struct gcry_md_block_ctx
{
function_ptr func;
int stuff;
char buffer[128];
}
But the thing about 'oh shit' ball is it can be picked up by any of a thousand lints and replaced by something that is less likely to go wrong. ISTR using preprocessor macros to make things safe.
Belts braces and a belly button stud for this sort of thing.
I read the whole set of comment and I saw a single reference to timing attack
(of maybe 2 if you assume the sentence about probing generated assembly was, too)
Now, I have a question for people proposing others languages:
Is there a way to make sure a function can be guaranteed to execute in a constant time (that is: same time whatever input on same machine) ?
If one wants to be sure, same question could be asked to C users.
Guaranteed? No -- think of different CPU, bus, cache, pipelines, and other speeds, not to mention different compiler optimisation techniques. (I suspect that you are well aware of this.) A lot of work has gone into this with different languages. Implementing crypto is not easy yet so many think that they can do it.
Quote: "Implementing crypto is not easy"
*
Almost certainly true. But why is the discussion about "crypto" ALWAYS in terms of prime numbers, large numbers and public key management....and C programming?
*
Link: https://en.wikipedia.org/wiki/Beale_ciphers
*
This is an example of a book cipher, where two out of three documents have still not been deciphered after more than a century. The commonly held view in the anorak/crypto community is that book ciphers are crap.
*
So......here's a short message enciphered in a private (i.e. crap) book cipher -- and it's about 1000 lines of (gasp!) C. Someone here can tell everyone what it says!!
*
2vKTKpGXotwduHmHe7YFGDYdYLgb2DIt8nwZ6Nwt
QPqdKxaPANuPsri5sJgJODQpqp4JMH23YlebsnGv
IhWfMLAVMvsvQxS18Nwl6haD4FKtu1iHGXqN2leN
KJ0defw9KzybqfMRKtCZkxgRYNkLilofQVAF8po9
C9aPglyVsXot630TKdUtc7oJW9oj8R8pc1kdwr85
8pkVafOj0JiJsxiroDUX2ZGfwBK3MFCvoDq1KdoB
mBWn014Nsxm1cnMbA54rS54RmjmfufgBkTc9c9s1
gRQL43oTGHQPGnI3wDOp8pArozmrMxstMDuFC7AJ
GjyxAzYb4r8FI3GD0pcf0NYbM9Stw9C1GfqNUroH
qbUHQZA5Gp2VA7OZeTE9g7qputKpSHIfexGt8l0V
GlYR
*
My guess - the programmer in question is probably C++ capable and understands clean design - s/he would have rewritten the whole module well if that had been a perceived option - in C++ or even Rust given sufficient time to learn it well.
However, there was a time deadline, and perhaps the surrounding code was a crusty bids nest of opaque interdependencies the programmer was not intimate with. It's even possible they got the job because others better suited didn't want to do it.
That's all hypothetical, but it is most likely that there are far more important factors than choice of programming language - and language probably wasn't even a choice for the person handling the "quick fix".
C is a specific tool for righting low level code
C is very good at its job
C is too often used not for its job,
Like the Transco engineer usining the Nokia 501 as a hammer, it works, but its not what its designed for.
C is a precision tool for manipulating low-level resources, not something to be used for everyday stuff.
Those complaining that C should be more memory safe, are the same as those complaining that their nokia hammer doesnt apply much force, or their thimble doesnt carry much water.