
C considered dangerous - johnramsden
https://lwn.net/SubscriberLink/763641/c9a04da2a33af0a3/
======
rwmj
_> 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 statement around the memcpy.

~~~
vardump
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.

~~~
rwmj
I guess so. One of the LWN comments mentions a Microsoft function memcpy_s
defined as:

    
    
        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.

~~~
lomnakkus
Using min() seems like it could be incredibly dangerous as an "implicit"
behavior, not to mention surprising.

I'd wager it'd be much better to just specify that abort() gets called in the
"overflow" case. (Given that overflow is basically never what you want
anyway.)

Yeah, it'll crash but at least it won't be suprising/undefined behavior.

~~~
rwmj
For extra fun, the Microsoft implementation of memcpy_s returns an error
instead of crashing if either of the pointers is NULL (thankfully doesn't
apply if the copy size is 0). There's a reason I don't like writing software
for Windows ...

------
deng
Thankfully, compiler warnings and static analyzers have become much better in
recent years. For instance, gcc can now warn about a missing 'break;'
mentioned in the article (you need to add a special comment like '/* fall
through */' if it's intentional). Also, clang-tidy is getting better with each
release. I highly recommend using it, although the initial configuration will
take some time, depending on the code base.

------
xroche
Alas! strlcpy and strlcat are still not present in the glibc, despite numerous
attempts, mainly for religious reasons (ie. "BSD sucks").

And yes, having something like "if (strlcat(buffer, src, sizeof(buffer) >=
sizeof(buffer)) { abort(); } " is much better than buffer overrun. But
security does not always seem to be a real concern, compared to politics.

------
yason
C is dangerous partly because assembly language is dangerous. We will always
need some layer on top of assembly that is mostly unchecked and reflects back
to how cpu instructions work. This is probably something we must live with
until we have processors with the notion of type checking.

C is dangerous partly because of swaths of undefined behaviour and loose
typing. Eliminating much of undefined behaviour either by defining the
behaviour or forcing the compiler to refuse compile undefined behaviour could
be of some help. There are still classes of undefined behaviour that cannot be
worked around but narrowing that down to a minimal set would make it easier to
deal with it. Strong typing would help build programs that won't compile
unless they are correct at least in terms of types of values.

C is dangerous partly because of the stupid standard library which isn't
necessarily a core language problem as other libraries can be used. The
standard library should be replaced with any of the sane libraries that
different projects have written for themselves to avoid using libc. It's
perfectly possible not to have memcpy() or strcpy() like minefields or
strtok() or strtol() which introduce the nice invisible access to internal
static storage, fixed by a re-entrant variant like strtok_r(), or require you
to do multiple checks to determine how the function actually failed. The
problem here is that if there are X standards, adding one to replace them all
will make it X+1 standards.

Yet, good programmers already avoid 99% of the problems by manually policing
themselves. For them, C is simple, productive, and manageable in a lot more
cases and domains than it is for the less experienced programmers.

~~~
pjmlp
Ironically other systems programming languages developed outside AT&T walls
since 1961 did not suffer from the majority of C's pain points regarding
memory corruption.

I really wish Bell Labs had been allowed to sell UNIX.

------
IshKebab
Terrible title. It's not remotely news that C is dangerous. This talk seems to
be about ways of mitigating the dangers. Why not call it "Mitigating the
dangers of C" or something else that is less of a tired cliche?

~~~
pjmlp
Because "Making C Less Dangerous" is the actual title of the talk, and
"Towards less dangerous C" is part of the agenda?

------
fithisux
The title is completely misleading.

~~~
vardump
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.

~~~
FraKtus
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...

~~~
vardump
Tooling _absolutely_ helps to reduce defects. That's why you use them.

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.

~~~
pjmlp
I would say that is why security conscious developers use them.

Sadly we are a very very tiny percentage, as proven by Herb Sutter question to
the audience at CppCon (1% of the audience answered positively), and CVE
frequent updates.

------
xvilka
Hopefully Zig [1] language will become a better alternative to C in upcoming
years. Not talking about higher level code where Rust or Go can be a better
choice.

[1] [https://ziglang.org/](https://ziglang.org/)

~~~
pjmlp
No language can become an alternative to C in the context of UNIX like OS
because no one is going to re-write them from scratch, given their symbiotic
nature.

Even if the complete userspace of Aix, HP-UX, __*BSD, GNU /Linux, OS X, iOS,
Solaris,.... gets re-writen in something else, there will always be the kernel
written in C.

Hence why improving C's lack of safety is so important to get a proper IT
stack.

------
amelius
Why don't they use valgrind?

~~~
deng
The kernel has CONFIG_HAVE_DEBUG_KMEMLEAK.

