Flaws, vulnerabilities, and language safety
The four terms the unit separates (flaw, vulnerability, exploit, bug), the factors that decide how safe software is, and what memory safety and type safety mean for C and its alternatives.
- Distinguish a flaw, a vulnerability, an exploit and a bug, and say which design or implementation level each sits at.
- List the four memory access errors that a memory-safe program never has.
- Explain why C and C++ are classed as memory unsafe and why they are still widely used.
- Compare static (compile-time) and dynamic (runtime) type safety.
15 min readJump to the quiz ↓
Design flaws and implementation vulnerabilities require different fixes
Security goals, cryptography, authentication, access control, protocols and firewalls depend on their implementation. A sound design can still fail because of a coding mistake. The lecture distinguishes a design flaw from an implementation vulnerability, an exploit and a bug.
The lecture defines four terms.
- Flaw. A system property at the design level that may lead to a breach of security policy. The notes add that a flaw exists in all implementations.
- Vulnerability. A system property at the implementation level that may lead to a breach of security policy.
- Exploit. A method (for example in software) to breach a security policy because of a flaw or a vulnerability.
- Bug. A property in a program’s code that makes it produce incorrect or unexpected results.
The notes give one example of each. The flaw is WEP, the WiFi security scheme: its design used weak RC4 encryption, static keys and a 24-bit initialisation vector that was too small, so key reuse became predictable. No implementation could fix that. The vulnerability is EternalBlue (2017), a buffer overflow in how Windows implemented SMBv1, not a problem with the SMB protocol’s design. The exploit is WannaCry, which used EternalBlue by sending a crafted SMB request to unpatched machines and overflowing a buffer to inject its own code.
Compare
Flaw
Design level. The whole scheme is weak, so every correct implementation inherits it. Example: WEP.
Vulnerability
Implementation level. The design may be fine, but this particular code is wrong. Example: the EternalBlue buffer overflow.
Language safety catches errors earlier
Software security depends on several factors, listed on the slide: language safety (memory safety and type safety), API safety, dependencies, and the tools available to find bugs, in particular vulnerabilities. The first of these gets most of the lecture.
The lecture defines language safety as the protection from undefined or undesired behaviour that a language and its ecosystem of development tools and compilers give you. Its rule of thumb is that the earlier an error is raised, the safer the software will be. There are three kinds, and the slide ranks languages for each.
| Feature | Unsafe languages | Safe languages |
|---|---|---|
| Memory safety | C, C++ | Rust, then Java, Python, Haskell |
| Type safety | C, C++ | Java |
| Thread safety | C, C++ | Rust, Java |
The notes use out-of-bounds array access to show the gradient. In C and C++ the error is never caught, and the program may crash. In Java and Python it is caught at runtime. In Haskell and LISP the usual data structures are built so it cannot happen at all.
Memory safety excludes four kinds of access error
Memory safety. A program is memory safe if memory access errors never occur. The lecture names four.
- Buffer overflow. Writing data beyond the allocated memory boundary and corrupting the memory next to it.
- Null pointer dereference. Accessing memory through a pointer that points to nothing (
NULLor0). - Dangling pointer / use after free. Accessing memory through a pointer after that memory has been deallocated.
- Double free. Calling
freeordeletetwice on the same address, which corrupts the memory allocator.
A language is memory safe if every program written in it is free of these errors.
What makes C unsafe. C and C++ support arbitrary pointer operations and have no bounds checking on arrays or on memory allocation. A program can write past the end of a buffer and no error is raised. The notes explain the design reason: if the compiler took over the checking, every variable access would need extra work, programs would run slower and the programmer would lose control. C leaves integrity of data to the programmer.
The notes separate referencing a pointer (using the address it holds) from dereferencing it (reading or writing the data at that address). A null pointer dereference is the second one, applied to an address that is not valid.
C persists because speed, control and resources matter
Why not use a safe language everywhere? The slide gives four reasons.
- Legacy code that still has to be maintained.
- Speed. Compiled C and C++ is highly efficient on the given machine.
- Control over the number of CPU cycles used, which matters for side-channel attacks.
- Resources. Higher-level languages need a costly execution environment.
C and C++ are also widely used in critical applications such as embedded systems and IoT devices, where a memory error can mean full system compromise, and older or weaker systems may lack the defences covered later in this module. Even if you never write C, the lecture wants you to understand the trade between the control a language gives you and the protection it gives you.
Type safety checks compatible operations at compile time or runtime
Type safety. Type safety means a program only performs operations on compatible types. It comes in two forms.
- Static (compile-time). The compiler catches the error before execution. Examples on the slide: Java, Haskell, C#. Many type errors are caught early and are easier to fix.
- Dynamic (runtime). The check happens while the program runs. Examples: Python, JavaScript, Ruby. Errors may appear only under certain runtime conditions, so they are harder to detect and potentially exploitable.
Compare
Static type safety
Checked by the compiler before the program runs. Java, Haskell, C#. Errors are caught early.
Dynamic type safety
Checked as the program executes. Python, JavaScript, Ruby. An error may surface only on one rare path.
Pitfall
Do not answer “a bug” when the question asks what a vulnerability is, or the reverse. The marked distinction is the level: flaw at design, vulnerability at implementation. And keep the direction of the relationship straight, because the lecture states it one way only. Every vulnerability is a bug, but not every bug is a vulnerability.
Exam detail
Be ready to give the four definitions in the lecture’s own wording, to name the four memory access errors, and to say what makes a language memory unsafe (arbitrary pointer operations, no bounds checking). The slide’s safe/unsafe table is a likely recall item: C and C++ are the unsafe column for all three kinds of safety. Expect “why do C and C++ still matter” to be answered with the four reasons above.
Optional aside
The notes say alternative definitions of flaw and vulnerability exist, for example in RFC 2828, the Internet Security Glossary. The exam marks the lecture’s wording.
The notes also say C and C++ catch type errors at compile time, while the slide table lists C and C++ as the type-unsafe languages. The notes add that casting defeats compile-time detection in C, and in Java too. The lecture does not reconcile the two statements. If a question asks which column C sits in, follow the slide: unsafe.
Recall
How do the four software-security terms, memory safety and the two forms of type safety differ?
- Flaw is design level, vulnerability is implementation level, an exploit is the method that uses either, and a bug is any incorrect behaviour.
- Memory safety means none of: buffer overflow, null pointer dereference, use after free, double free.
- C and C++ are unsafe for memory, type and thread safety. They persist because of legacy code, speed, cycle control and low resource cost.
- Static type safety is checked by the compiler. Dynamic is checked at runtime and can hide errors on rarely run paths.
Check yourself
Source
Week 10 slides PDF