33

C is a rock-solid and widespread programming language that is very popular especially in the FOSS community.

Many security-related software (such as encryption libraries) are written in C and will probably be written in C also in the future. One of the main reasons for it is the great performance and portability of C programs. But the point is that even very experienced software developers can't prevent bugs like buffer overflows. Every year quite a lot security bugs related to memory management are found in even very popular and reviewed software.

So my question to you: Is it still a good idea to write security-related software in C nowadays? Or isn't it "security by design" to choose modern languages like Rust, Go or more high-level languages like Python?

11
  • 15
    "Security by design" is not affected by language choice. Every language has their own security challenges.
    – schroeder
    Commented Mar 10, 2016 at 17:43
  • 9
    There are security applications where performance matters so little that one has the luxury of choosing a JIT language. For everything else, there's C.
    – Adam Davis
    Commented Mar 10, 2016 at 18:46
  • I think this question will be of interest if you haven't already seen it.
    – MonkeyZeus
    Commented Mar 10, 2016 at 20:16
  • 3
    @schroeder That sounds like a false equivalence. Show me all the bugs in, say, Java where a carefully crafted user input allows an attacker to execute arbitrary code in the JVM. Commented Mar 10, 2016 at 20:57
  • 1
    While I agree with most of the question, I disagree with the OP's premise that "C is a rock-solid [...] programming language". As Tom Leek's answer explains, it is one of the more brittle languages around, where the true semantics are often misunderstood (e.g. UB) and portability is a nightmare.
    – Nayuki
    Commented Mar 11, 2016 at 2:54

6 Answers 6

49

The main and almost unique reason why most software in the Linux ecosystem is written in C is Tradition. Developers see software written in C, libraries with a C-based API, and thus they use C, because that's convenient. Compilers are already there, and work well because the whole OS is written in C.

None of this says that C is good for developing robust software. In fact, C is quite terrible at it. With C, the developer must remain wary of many things at all times. C has many traps ready to be sprung on the smallest mistakes, including:

  • Unchecked array accesses, thus allowing for overflows.
  • Manual memory management, leading to use-after-free or double-free errors, and memory leaks.
  • The dreaded "undefined behaviour" that makes seemingly reasonable expressions run amok (in particular, signed operations that exceed the representable range).
  • Portability issues when going to architectures with different lengths for integer types and pointers.

What C is real good at is the following:

  • Interacting with an existing set of libraries that offer a C API. C is the lingua franca that allows interoperability between software components on many platforms.

What C is passably good at is:

  • Writing very low-level code (e.g. crypto code resistant to timing attacks through fixed memory access patterns) while trying to keep some level of portability.

My conclusion is that C is not a good idea for writing security-related software in general, and has not been so for quite some time already (at least a decade). C is still justified in some specific contexts, in particular if you target embedded platforms (not embedded as in "smartphone", rather embedded as in "smart card"). Instead of looking for reasons to move away from C, it would be more justified to look for specific reasons to keep using C.

16
  • 5
    I'd also add that C generates its own eco-system. Applications written in C use libraries written in C. My browser needs something to do the heavy lifting of SSL for instance. AFAIK you can't link in something written in another language, at least easily. Commented Mar 10, 2016 at 17:47
  • 4
    @Adjit: it depends on the context, but, as a starting point, C# is not bad. Thanks to Mono, code written in C# can run on Windows, OS X and Linux. It has automatic memory management, checks array accesses, and no undefined behaviour on integer computations.
    – Tom Leek
    Commented Mar 10, 2016 at 18:01
  • 6
    @Adjit: If you want a systems programming language (i.e.: like C, close to the metal, offering manual memory management) but with high-level features and safety guarantees, consider Rust.
    – wchargin
    Commented Mar 10, 2016 at 18:53
  • 4
    Supporting C (I'm totally biased, beware): 1) C is simple, no fancy inheritance tricks, etc, to be aware of. 2) C is flexible, enough to enforce any (secure) design 3) C is less beginners friendly, forcing the programmer to be aware of consequences and architectures 4) No language is designed to be (more) secure, e.g. default string comparison is not constant time and should not be. It is easy to make mistakes in C? Yes. Is it also easy to adopt a design to prevent them? Yes. Those unaware of security will always be, C#, Java, C, make no difference. Commented Mar 10, 2016 at 20:19
  • 11
    @MargaretBloom I disagree. Many errors that in C# and Java cause exceptions to be thrown cause subtle security bugs in C. You bring up string comparison, which is a good example of C's deficiency regarding security: numerous string functions in C's standard library can cause unintended and sometimes dangerous behavior on non-null-terminated strings. C's track record in practice, even when written by experienced developers, is troubling and it's time to put the blame where it belongs. Commented Mar 10, 2016 at 21:11
25

You can write secure code in C. Its just that the language is unsafe by default. The safety has to be tacked on manually with extra code (which of course can itself contain bugs).

For that reason, C was never really the best choice for security-critical software. It was used anyway in FOSS because free compilers for it have historically been available on pretty much every platform (by definition for every platform gcc supports).

The general advice for security-critical software, if you aren't tied to a specific language (like C) is to use Ada. That language is at a similar level of abstraction as C or C++, but defaults towards safety (eg: automatic bounds checking for all arrays) with the ability to add code to turn checks off, rather than the other way round.

In particular, there's a subset of Ada called SPARK that is designed specifically for safety and security-critical software. It can also be used for formal verification of software, if you are into that kind of thing.

5

First of all, for things such as encryption performance matters.

It's the difference between being able to serve hundreds of users or only 10 at a time. And this does bear some security relevance: if your servers are struggling, then they are easy to take out with a DOS attack.

If you ever benchmarked pure python code vs. native code, you will be surprised by how big the differences is.

Secondly, there is no Python/Java without C. Whichever "modern" language you look at, it uses a substantial amount of libraries underneath. And guess what, the majority of these are C libraries.

Now if you write a "security critical" library in such a language you have to worry about 1. problems in your own code 2. problems in the Java/Python code you use 3. problems in the underlying C code (there are frequent security updates to Java!) and 4. problems in the C libraries underneath that may change without you knowing (e.g. OS updates). If you want security relevant code, minimize dependencies.

The amount of C underneath is increasing, not decreasing. This may seem not obvious. But numpy, tensorflow, JavaFX, ... these all use a lot of C code underneath, because of performance.

Many problems could be avoided by careful engineering and verbose programming. For example the OSX "goto fail" bug was caused by programmers not adhering to the best practise of always using brackets...

if (a)
  goto fail;
  goto fail;
somethingelse

is an easy to miss error in most languages (except Python, where you would need two spaces less for the same problem) that can be avoided simply with verbosity:

if (a) {
  goto fail;
  goto fail;
}
somethingelse

It's not as if python would be very helpful at avoiding such problems (in fact, Java compilers would warn you about unreachable code - Python does not, and C compilers can if enabled by the user) ... In the end developer discipline remains a key factor.

C code usually requires much more care for writing; which is not a bad thing for quality. The main drawback is that it is slower to develop.

12
  • Actually Python does help avoiding such problems. The specific example you gave of a bug caused by a discrepancy between indentation and block structure of the code would be impossible in Python. That is because in Python the indentation determines the boundaries of code blocks. The example you gave is the poster example of why the Python way of defining blocks is useful. It is possible to come up with lots of examples of problems which would not be solved by using Python. The example you gave is not one of them.
    – kasperd
    Commented Mar 10, 2016 at 22:58
  • This particular case: yes. In general: no. Python tolerates unreachable code. Even worse, it tolerates even undefined variables in code not reached yet, so unless you have 100% code coverage with tests, you may have even such errors there, which can cause various bad problems. Commented Mar 10, 2016 at 23:01
  • 1
    Such cases usually don't arise from security relevant programming errors. It's enough (and very much recommended) to have the compiler do such basic checks, even if academia says you can still shoot yourself into the foot somehow. And you could even have compiler hints "supposed-to-be-unreachable", too. Commented Mar 10, 2016 at 23:18
  • 2
    If you read this post, you might get the impression that Java and its libraries are written in C. This is flat out wrong. Most Java compilers (in fact, most self-respecting compilers for any language) are self-hosted (the sources are written in their own language).
    – T.E.D.
    Commented Mar 11, 2016 at 2:23
  • 1
    Fact is, they are: Java Hotspot is C++ with lots of assembler inline, the usual python interpreter CPython is in C (not Jython which is in Java). Check the sources of the JVM/interpreter. Commented Mar 11, 2016 at 7:06
2

C has been used by millions of people for over 20 years. Its security flaws, such as the one with scanf() are well known and documented, and there are established and heavily tested workarounds.

A newer language, say perl 6 or python 3 has only been in use for a short time, few people are expert in them, security flaws may exist which are yet to be found, and critical review and documentation is sparse.

Both of these newer languages are much 'larger' than C with vastly more abilities, and it may take hugely more than 20 years before all possible programming constructs have been used, and we can be confident of their security.

0

It's low-level and totally unsafe by default. Ofcourse it's possible to write secure software -- but you have to be good at it. OTOH: C is low-level and you don't need to include libraries that might have security issues of their own -- this might be good. Also one shouldn't forget that a "secure" language means that there are programs involved which again, are a possible security issue.

Personally I would probably recommend using Rust as it can be sufficiently low-level and is focused on security too.

I would not use any non-native Code (ie. not Java, C#, etc.) except if you deliver you own Runtime. Same thing goes ofcourse to every Library you Link to but this can be avoided in contrast to non-native code.

All in all you should focus on least code possible, ie. make it as simple as you can and don't trust other code. In contrast though, if you can avoid it, don't trust yourself writing secure code -- it's usually no good.

2
  • Considering that OSes are written in C this would prevent you from using pretty much any program as any links into the OS would be considered bad.
    – AstroDan
    Commented Mar 11, 2016 at 0:03
  • @AstroDan Yes, of course the OS is problematic. But it's all matter of how far you want to go -- and I assume that OP didn't mean to write his own OS. You should minimize much but at some point you simply got to stop. But if you can avoid it (ie. not-using 1000 libs is easier than avoiding an OS as requirement): Go for it.
    – ljrk
    Commented Mar 11, 2016 at 0:07
-1

The reason of C's persistence is not a tradition, but a portability and very permissive API+lib. Yes, it does not thinks for you like some high-level trash, the language was made in a strong belief, that it's just a tool for implementing your idea. If the idea itself is designed poorly/with flaws, not just security flaws - no language will totally save it. Yes, some new dumb-programmer-based "products" will try to do it's best to prevent the problem, but they will not succeed, not with all of the problems. Use brain, standard, debugger and valgrind - and you will love C for what it is.

9
  • 8
    C sources are actually less portable that most every language I know. Multiplatform C programs tend to contain sections (sometimes a majority) of nigh-unreadable webs of conditionally-compiled code. Its the C compiler itself that isn't a lot of work to port, since the language is relatively simple, so pretty much every platform has one.
    – T.E.D.
    Commented Mar 10, 2016 at 20:54
  • 2
    +1 Developer's familiarity with logic, programming, and any given specific language/tool will determine the final product's security (up until you start talking about the security of a given hardware module, at least). Blaming C for something that's not its fault is just plain bad. I've written code in probably 20-30 languages in my life, and I've found consistently that each language is about as secure as you make it. As I've gained experience in programming, I've reduced the number of bugs I write per LOC. Experience is what makes the programmer.
    – phyrfox
    Commented Mar 10, 2016 at 21:20
  • @T.E.D. conditionally-compiled codes are one of the most powerful things in C. Make a subroutines/defines for a platform-specific actions, separate them by placing in properly named subfolders+files, make sure that they will "fire up" only when needed, reflect it in your Makefile. It is that simple. Badly organized codes are pain in an ass, I strongly agree, but this is not a language-specific problem : it's a bug in "developer's" mind, not in a language allowing a proper instrument. Commented Mar 10, 2016 at 21:41
  • 1
    @AlexeyVesnin Re "high-level trash": I don't understand this part at all. Are you answering through the viewpoint of hobby development as opposed to professional development? Commented Mar 10, 2016 at 22:16
  • 1
    @AlexeyVesnin I'm just wondering where you're coming from concerning this particular problem, because no professional programmer should intentionally accept the "challenge" of throwing away "high-level trash," as it would be a disservice to their company. Commented Mar 11, 2016 at 14:45

Not the answer you're looking for? Browse other questions tagged .