How to secure C
The C language is very powerful and where it is used a lot - especially in the Linux kernel - but it is very dangerous. One of the developers of the Linux kernel told how to deal with security vulnerabilities of C.
You can do almost any thing on C, but that does not mean that you need to do it. Code C is very fast, but is carried without seat belts. Even if you are an expert, like most of the Linux kernel developers , deadly errors are still possible.
In addition to the pitfalls of the type of pseudonyms of pointers , the C language has fundamental uncorrected errors that are waiting for their victims. It was these vulnerabilities that Case Cook , Google Linux kernel security engineer, discussed at the Linux security conference in Vancouver.
“C is a kind of assembler. It’s almost machine code, ”said Cook, addressing an audience of several hundred colleagues who understand and appreciate the speed of C applications. But the bad news is that“ C comes with some dangerous baggage, undefined behavior and other weaknesses that lead to security holes and vulnerable infrastructure. "
If you use C in your projects, you should pay attention to security issues.
Linux kernel security
Over time, Cook and his colleagues discovered numerous problems of the native C. To eliminate them, the Kernel Self Protection Project, the Kernel Self -Defense Project, was launched . He is slowly and steadily working to protect the Linux kernel from attacks, removing the problematic code from there.
This is difficult, says Cook, because "the kernel needs to do very specific architecture-specific things for memory management, interrupt handling, scheduling, and so on." A large amount of code refers to specific tasks that need to be carefully checked. For example, “C has no API for setting page tables or switching to 64-bit mode,” he said.
With such a load, and with weak standard libraries in C, there are too many undefined behaviors. Cook quoted - and agreed - with Raf Levien ’s blog post “Everything is possible with undefined behavior” .
Cook gave specific examples: “What is the content of“ uninitialized ”variables? This is all that was in memory before! In void pointers, there is no type, but can we call typed functions through them? Of course! Assembly anyway: you can go to any address! Why do u
memcpy()have no argument 'max destination length'? It doesn't matter, just do as said; all memory areas are the same! ”
Ignoring warnings ... but not always
Some of these features are relatively easy to handle. Cook commented: “Linus [Torvalds] likes the idea of always initializing local variables. So just do it. ”
But with a reservation. If you initialize a local variable in switch, you will receive a warning: “The statement will never be executed
[-Wswitch-unreachable]” because of how the compiler processes the code. This warning can be ignored.
But not all warnings can be ignored. “Variable length arrays are always bad,” said Cook. Other problems include stack exhaustion, linear overflow, and paging violation. In addition, Cook drew attention to the slowness of the VLA. Removing all VLAs from the core improved performance by 13%. Improving both speed and security is a double benefit.
Although the VLAs were almost removed from the kernel, they still remained in some code. Fortunately, VLA is easy to find using the compiler flag
Another problem is hidden in the semantics of C. If break is skipped in a switch, what did the programmer mean? Skipping a break may lead to the execution of a code of several conditions; This is a well-known problem.
If you are looking for break / switch statements in existing code, you can use it
-Wimplicit-fallthroughto add a new switch statement. In fact, this is a comment, but modern compilers disassemble it. You can also explicitly mark the absence of a break with the “fallthrough” comment .
Cook also found a decrease in performance when checking boundaries for allocating slab memory . For example, validation
strcpy()-familyreduces performance by 2%. The alternatives seem to have
strncpy()their own problems. It turns out that Strncpy does not always end with a null character. Cook sadly addressed the audience: “Where can I get the best APIs?”
During a question and answer session, one Linux developer asked: “Can I get rid of old, bad APIs?” Cook replied that for some time Linux had supported the concept of outdated APIs. However, Torvalds abandoned this idea, arguing that if an API is outdated, it should be completely discarded. However, throwing the API “politically dangerous” forever, Cook added. So while we are stuck with them.
Long-term solution to the problem? More developers who understand security issues
Cook foresees a long and difficult journey. Once upon a time, the idea of creating a Linux C dialect seemed attractive, but it would not. The real problem with dangerous code is that “people do not want to do the work of clearing the code — not only bad code, but C itself,” he said. As with all open source projects, “we need more dedicated developers, reviewers, testers and backport specialists.”
Dangerous C: lessons
- C is a mature and powerful language, but it creates technical difficulties and security problems.
- Linux developers pay special attention to securing C (without losing its power), because most of the operating system is written on it.
- Google Linux kernel security engineer identified specific language vulnerabilities and explained how to avoid them.