The title is completely misleading.
I write a ton of C and I completely agree with the title. With 20+ years of experience. Kernel drivers and embedded system bare metal firmware. The problem with C is that in any bigger project something always slips through even the best programmers, reviewers, static analysis and unit tests. And that something can lead to disastrous crashes and security vulnerabilities.
C considered dangerous
11–20 of 66 posts
Re: C considered dangerous
#12Earlier quoted context omitted.
I agree, it's very click-baity. C is actually great and is only really dangerous because it gives the programmer so much control.
C actually gives one rather limited control over modern hardware with it's memory hierarchies and superscaler CPUs. Programming language research has also moved on a lot since the 70's, which is why we should be considering less dangerous languages (e.g. better type systems and less undefined behaviour). Languages like ATS and Rust also support explicit memory management, whilst being a whole lot safer.
Good C-compilers will most of the time take care of the superscalar CPU friendliness. When they don't, you can always drop down to the assembler level, and it'll mesh well with C.
Re: C considered dangerous
#13The title is completely misleading.
I agree, it's very click-baity. C is actually great and is only really dangerous because it gives the programmer so much control.
This doesn't actually refute the assertion that C is dangerous :)
Control and increased safety are not mutually exclusive. I'll take safe-by-default, unsafe-when-asked any day. It's not 1972 anymore.
Re: C considered dangerous
#14> He asked: why is there no argument to memcpy() to specify the maximum destination length? I'm confused by this. The third argument provides the destination length, so what good would a "maximum destination length" do? I guess he must mean that because the length is often computed, you'd need a fourth argument to ensure the length isn't greater than some sane upper bound. But you can easily fix that using an if stat…
Perhaps because the memory buffers might be of different size. Maybe memcpy_oobp (out of bounds protection) signature could be: memcpy_oobp(void* dst, size_t dst_size, void* src, size_t src_size); Then again, I guess you could just as well do: memcpy(dst, src, min(dst_size, src_size)); But having to explicitly specify both destination and source sizes might have prevented a lot of buffer overwrite bugs.
memcpy_s (void *dest, size_t destSize, const void *src, size_t count);
which is effectively equivalent to your memcpy_oobp function.However the Microsoft function also returns an error code which must be checked (because count might be larger than destSize), thus providing another way for the programmer to screw up. I'm not sure if this is better or worse than just copying the min() as in your second example. It probably depends on the situation.
Re: C considered dangerous
#15Earlier quoted context omitted.
I write a ton of C and I completely agree with the title. With 20+ years of experience. Kernel drivers and embedded system bare metal firmware. The problem with C is that in any bigger project something always slips through even the best programmers, reviewers, static analysis and unit tests. And that something can lead to disastrous crashes and security vulnerabilities.
Don't you think that with the tools we have now it's easier to control the quality of code produced (Clang memory sanitizers and so on)? I feel more at ease to ship C code today after instrumenting it than a few years ago...
That said, sometimes I'm shocked what kind of disasters get past the analyzers.
Stakes are higher than ever. It's not just about functional correctness and avoiding crashes anymore. Your code needs to be secure against outside world malicious actions. Getting rid of counterintuitive security vulnerabilities is very, very hard.
Re: C considered dangerous
#16> He asked: why is there no argument to memcpy() to specify the maximum destination length? I'm confused by this. The third argument provides the destination length, so what good would a "maximum destination length" do? I guess he must mean that because the length is often computed, you'd need a fourth argument to ensure the length isn't greater than some sane upper bound. But you can easily fix that using an if stat…
Perhaps because the memory buffers might be of different size. Maybe memcpy_oobp (out of bounds protection) signature could be: memcpy_oobp(void* dst, size_t dst_size, void* src, size_t src_size); Then again, I guess you could just as well do: memcpy(dst, src, min(dst_size, src_size)); But having to explicitly specify both destination and source sizes might have prevented a lot of buffer overwrite bugs.
A good way to prevent this is to have a buffer abstraction, where the size is a property of the type, e.g.,
typedef struct {
size_t bytes_used;
size_t capacity;
void *data;
} buf_t;
int buf_init(buf_t *buf);
void buf_cleanup(buf_t *buf);
void buf_copy(buf_t *dst, buf_t *src);
/* ... */
Of course, it doesn't prevent people from using memcpy directly.Re: C considered dangerous
#17Earlier quoted context omitted.
C actually gives one rather limited control over modern hardware with it's memory hierarchies and superscaler CPUs. Programming language research has also moved on a lot since the 70's, which is why we should be considering less dangerous languages (e.g. better type systems and less undefined behaviour). Languages like ATS and Rust also support explicit memory management, whilst being a whole lot safer.
C alone doesn't provide the control directly, but you as a programmer can absolutely leverage C to take control of the memory hierarchies by controlling your data access patterns. IOW, high locality of reference. Good C-compilers will most of the time take care of the superscalar CPU friendliness. When they don't, you can always drop down to the assembler level, and it'll mesh well with C.
Likewise most static languages defer to the compiler for CPU-specific performance optimisations and will permit foreign native calls into C or ASM where necessary. So I don't see how this is an argument in C's favour.
Re: C considered dangerous
#18Earlier quoted context omitted.
I write a ton of C and I completely agree with the title. With 20+ years of experience. Kernel drivers and embedded system bare metal firmware. The problem with C is that in any bigger project something always slips through even the best programmers, reviewers, static analysis and unit tests. And that something can lead to disastrous crashes and security vulnerabilities.
Well, I can say the same about Python, Erlang, Lua, in addition to C and C++. I believe C is not worse than these languages, only that C requires different (sometimes very different) skills and discipline.
Of course you can shoot yourself into foot with stuff like metatables in Lua and Python metaclasses and whatnot. Then again you should see some C macro messes around...
Anyways I don't like when people defend C with that age old argument it requires a clever disciplined programmer that never makes mistakes. Because either such programmers don't exist or they're very rare.
Re: C considered dangerous
#19Earlier quoted context omitted.
C alone doesn't provide the control directly, but you as a programmer can absolutely leverage C to take control of the memory hierarchies by controlling your data access patterns. IOW, high locality of reference. Good C-compilers will most of the time take care of the superscalar CPU friendliness. When they don't, you can always drop down to the assembler level, and it'll mesh well with C.
High-locality of reference can be achieved in any language that supports unboxed types, it doesn't require C (even a very high-level language like Haskell has support for this). But this is a long way from having complete control how each memory heirarchy is used. Likewise most static languages defer to the compiler for CPU-specific performance optimisations and will permit foreign native calls into C or ASM where ne…
You often also need correct alignment. Cache-line or page. Your unboxed access across two pages can cause two TLB misses, L1 misses etc. Not to mention two page faults.
Sometimes you need to ensure two (or more) buffers are NOT aligned in a particular way to avoid interfering with CPU caching mechanisms.