Kernel C

You know C. The kernel's C has different rules, a different standard library, a different error convention, and a type system extended by annotations a separate tool checks. None of it is arbitrary; every rule below exists because the alternative broke something.

This chapter covers four concepts in the six-part treatment — the freestanding environment, the stack, errors without exceptions, and types and annotations — then a closing section on the idioms you must be able to read without stopping.


Concept 1: The Freestanding Environment

1. What problem it solves

There is no C library beneath the kernel. There cannot be: libc is a user-space program's interface to the kernel, and the kernel has nothing under it but the hardware. So the kernel provides its own — a smaller, stricter, differently-named standard library, plus a set of prohibitions on language features that assume runtime support the kernel does not have.

2. Where it exists in the kernel

lib/, include/linux/, and per-architecture optimized versions in arch/<arch>/lib/.

ls lib/*.c | head -20
rg -n "^ssize_t strscpy|^int kstrtoint|^int scnprintf" lib/string.c lib/kstrtox.c lib/vsprintf.c
rg -n "kernel_fpu_begin" arch/x86/include/asm/fpu/api.h

The build enforces the freestanding-ness: -ffreestanding, -nostdinc, and a curated include path. If you #include <stdio.h> in a kernel file it will not be found, and that is deliberate.

rg -n "nostdinc|ffreestanding" Makefile scripts/Makefile.lib | head

3. Who owns or interacts with it

ActorInteraction
lib/ maintainersOwn the string, sorting, and conversion helpers
The compilerEnforces -ffreestanding; some builtins are still emitted (memcpy, memset) and the kernel provides them
checkpatch.plFlags deprecated functions by name
Coccinelle (make coccicheck)Automatically finds and can fix whole classes of misuse

4. The replacements you actually need

You wantDo not useUse
Allocatemalloc, calloc, realloc, freekmalloc, kzalloc, kcalloc, kvmalloc, krealloc, kfree, kvfree
Printprintf, fprintfpr_info/pr_err/pr_debug; in a driver, dev_info/dev_err/dev_dbg
Format into a buffersprintf (unbounded), snprintf (returns what it would have written)scnprintf (returns what it did write); in a sysfs show(), sysfs_emit()
Copy a stringstrcpy, strncpy, strlcpystrscpy — always NUL-terminates, returns -E2BIG on truncation
Parse a numberatoi, strtol, simple_strtoulkstrtoint, kstrtoul, kstrtou32, … — return 0 or -EINVAL/-ERANGE
Duplicate a stringstrdupkstrdup, kstrndup, kmemdup; from user space, strndup_user
Compare/search memorymemcmp, memchrSame names, kernel implementations. memcmp is not constant-time; for secrets use crypto_memneq.
Sortqsortsort() from <linux/sort.h>; list_sort() for lists
AssertassertWARN_ON/WARN_ON_ONCE (recoverable), BUG_ON (panics — almost never justified)

Verify the current state of any of those rather than trusting the table:

rg -n "strlcpy|strscpy" include/linux/string.h
rg -n "deprecated" scripts/checkpatch.pl | head -20
$EDITOR Documentation/process/deprecated.rst      # the canonical list, maintained

Two prohibitions with no replacement:

No floating point. The FPU/SIMD register state is not saved on kernel entry, so touching it corrupts the interrupted user program's registers. Code that genuinely needs SIMD (crypto, RAID parity) brackets it with kernel_fpu_begin()/kernel_fpu_end(), which is expensive and disables preemption. Ordinary kernel code uses fixed-point arithmetic.

No 64-bit division on 32-bit architectures. The compiler emits a call to a libgcc helper (__udivdi3, __aeabi_uldivmod) that the kernel does not link. It builds fine on x86-64 and fails to link on 32-bit ARM — which is exactly the kind of breakage the 0-day bot will mail you about.

u64 total = ...;
u32 count = ...;

/* WRONG on 32-bit: undefined reference to __udivdi3 at link time. */
u64 avg = total / count;

/* Right: */
u64 avg = div_u64(total, count);
/* or, when you also want the remainder — note do_div MODIFIES its first argument: */
u64 n = total;
u32 rem = do_div(n, count);     /* n becomes the quotient */

5. Experiment

CLAIM. snprintf and scnprintf differ in a way that turns a truncation into an out-of-bounds write in the classic accumulate-into-a-buffer loop.

METHOD. Reason it out on paper first, then confirm in user space where it is safe:

cat > /tmp/sn.c <<'EOF'
#include <stdio.h>
int main(void) {
    char buf[16];
    /* snprintf returns the length it WOULD have written. */
    int n = snprintf(buf, sizeof(buf), "%s", "a-string-far-longer-than-sixteen");
    printf("buf=%zu  snprintf returned %d\n", sizeof(buf), n);
    printf("if you now do buf+n, you are %d bytes PAST the end\n", n - (int)sizeof(buf) + 1);
    return 0;
}
EOF
gcc -o /tmp/sn /tmp/sn.c && /tmp/sn

PREDICT FIRST: in the loop below, how far past the end of buf does the second call write?

/* The bug, in the shape it actually appears in drivers: */
int len = 0;
for (i = 0; i < n; i++)
        len += snprintf(buf + len, size - len, "%s ", item[i]);
        /*             ^^^^^^^^^ len can exceed size    ^^^^^^^^^^
         *             → pointer past the end            → size - len underflows
         *                                                  to a huge size_t     */

/* The fix is one character: */
        len += scnprintf(buf + len, size - len, "%s ", item[i]);

scnprintf returns what it actually wrote, so len can never exceed size. This is why checkpatch and reviewers care, and it is a real CVE pattern.

6. Failure mode

MistakeSymptom
#include <stdio.h>"No such file or directory" — and the right reaction is not to find a path for it
Floating point in kernel codeSilent corruption of a user program's FPU registers; on some architectures, an oops
64-bit division on 32-bitundefined reference to __udivdi3 at link time — on an architecture you do not build
snprintf accumulation loopOut-of-bounds write; size - len underflows to a huge size_t
strcpy/strlcpycheckpatch rejection at minimum; a truncation bug at worst
sprintf into a sysfs bufferOverflow of the PAGE_SIZE buffer. Use sysfs_emit.
BUG_ON for a recoverable conditionYou crashed a machine to report something a WARN_ON and an error return would have handled

Warning: BUG_ON() kills the machine. Documentation/process/coding-style.rst and years of review agree: do not add new BUG_ONs. If the condition is recoverable, return an error. If you want to be loud, WARN_ON_ONCE() produces a stack trace and continues, and syzbot and the CI bots treat a WARN as a failure — so it will be noticed.


Concept 2: The Kernel Stack

1. What problem it solves

Every task needs somewhere to put its kernel-mode locals and call frames. Giving each of the thousands of tasks on a machine a growable multi-megabyte stack is not affordable, so the kernel gives each one a small, fixed allocation — and then imposes the discipline that makes that affordable.

2. Where it exists in the kernel

Per-architecture, allocated at task creation.

rg -n "define THREAD_SIZE" arch/x86/include/asm/page_64_types.h arch/arm64/include/asm/memory.h
grep -E 'CONFIG_(VMAP_STACK|FRAME_WARN|THREAD_SHIFT)' ~/kernel/build/.config
rg -n "vla|Wvla" Makefile scripts/Makefile.extrawarn | head

Typically 16 KB on 64-bit architectures (it was 8 KB historically and was raised because deep call chains were overflowing). It does not grow. There is no guard mechanism at all unless CONFIG_VMAP_STACK is on, in which case there is a guard page and an overflow faults instead of quietly scribbling on the adjacent allocation.

3. Who owns or interacts with it

ActorInteraction
The schedulerSaves and restores the stack pointer on every context switch
InterruptsLand on the current task's stack (or a dedicated IRQ stack, per architecture)
The compilerEmits -Wframe-larger-than=N when one function's frame exceeds CONFIG_FRAME_WARN
objtoolValidates stack frame bookkeeping so the unwinder can produce correct traces

4. The rules that follow

RuleReason
No large locals. A 4 KB buffer is a quarter of the budget.16 KB total, shared with every frame beneath you
No recursion. Ever.Nothing bounds the depth
No variable-length arrays. The kernel removed all of them and -Wvla is on.An attacker-influenced length becomes an attacker-chosen stack offset
No alloca().Same
Do not DMA to or from the stack.The DMA API requires cache-line-aligned memory that is not on a stack; see Memory
Watch what the compiler inlines.Three inlined functions with 1 KB frames each is a 3 KB frame in one function

The idiom when you need a real buffer:

/* NOT this: */
char buf[PAGE_SIZE];              /* 4 KB of a 16 KB budget, in one frame */

/* This: */
char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
        return -ENOMEM;
...
kfree(buf);

/* Or, for a buffer whose size is only known at runtime and may be large: */
void *buf = kvmalloc(len, GFP_KERNEL);   /* kmalloc, falling back to vmalloc */
...
kvfree(buf);

5. Experiment

CLAIM. The build system enforces the stack budget statically, and CONFIG_VMAP_STACK turns a runtime overflow from silent corruption into a diagnosable fault.

METHOD. In a scratch module, add a function with an oversized frame and build it:

static noinline int hog(void)
{
        char big[8192];                 /* half the stack, in one frame */
        memset(big, 0xAA, sizeof(big));
        return big[get_random_u32() % sizeof(big)];
}

PREDICT FIRST: does this (a) fail to compile, (b) compile with a warning, (c) compile silently and crash at runtime, or (d) compile and work? Write your answer, then:

make -C ~/kernel/build M=$PWD modules 2>&1 | grep -i "frame size"
grep CONFIG_FRAME_WARN ~/kernel/build/.config

Then check what real functions in the tree cost:

cd ~/kernel/build
# The functions with the largest stack frames in your build:
objdump -d vmlinux 2>/dev/null | \
  awk '/^[0-9a-f]+ </{f=$2} /sub .*0x[0-9a-f]+,%rsp/{print $NF, f}' | \
  sort -rn | head -10

6. Failure mode

MistakeSymptom
A large stack localBuild warning at best; with VMAP_STACK, a "kernel stack overflow" oops; without it, corruption of an unrelated allocation
Recursion in kernel codeOverflow whose depth depends on input — i.e. attacker-controllable
A VLA-Wvla error; historically, a stack-clash vulnerability class
Deep call chains through function pointersOverflow that only happens with one particular driver stack loaded
DMA to a stack bufferSilent data corruption, or an IOMMU fault, depending on the platform

Note: CONFIG_VMAP_STACK does not prevent overflow — it makes it detectable. The difference between "kernel stack overflow at foo+0x2c" and "some unrelated structure was corrupted, and three seconds later something else crashed" is the difference between a ten-minute fix and a week. Leave it on.


Concept 3: Errors Without Exceptions

1. What problem it solves

There is no unwinding mechanism in the kernel: no exceptions, no destructors, no RAII, no defer. Every function that can fail must communicate that in its return value, and every caller must handle it — including releasing everything acquired so far, in the right order.

The kernel's answer is a strict convention plus one structural idiom (goto unwinding) that makes the correct thing also the readable thing.

2. Where it exists in the kernel

$EDITOR include/uapi/asm-generic/errno-base.h    # 1-34: the classic errnos
$EDITOR include/uapi/asm-generic/errno.h         # 35+
$EDITOR include/linux/errno.h                    # 512+: KERNEL-INTERNAL ONLY
rg -n "define MAX_ERRNO|define IS_ERR\b|define ERR_PTR\b" include/linux/err.h

3. Who owns or interacts with it

ActorInteraction
Every kernel functionReturns int (0/negative) or a pointer wrapped with ERR_PTR
The syscall exit pathPasses the negative value out; libc negates it into errno
The driver coreInterprets -EPROBE_DEFER specially — retry later
checkpatch/smatch/coccinelleFind unchecked returns and leaked-on-error resources

4. The conventions

Integer returns. 0 or a positive value on success; a negative errno on failure. Pick the errno that already means what you mean:

ErrnoMeans
-EINVALThe caller passed something that cannot be right
-ENOMEMAllocation failed
-EFAULTA user pointer was bad
-EBUSYRight request, wrong time
-EAGAINTry again; on a non-blocking fd, "nothing right now"
-ENODEV / -ENXIONo such device
-EOPNOTSUPP / -ENOTSUPPNot implemented for this case (-EOPNOTSUPP is the one user space should see)
-EPERM / -EACCESNot permitted (capability) / not allowed (permission bits)
-EPROBE_DEFERKernel-internal. A dependency is not ready; the driver core will retry.
-ERESTARTSYSKernel-internal. A signal interrupted a sleep; restart the syscall.

Warning: The values at 512 and above in include/linux/errno.h — ERESTARTSYS, ENOIOCTLCMD, EPROBE_DEFER — must never reach user space. Returning -EPROBE_DEFER out of an ioctl gives the caller a nonsense errno of 517. Some of these are caught by the syscall exit path; not all are. This is a real and recurring review comment.

Pointer returns. A function returning a pointer encodes an error in the pointer itself, using the fact that the top 4096 addresses are never valid kernel pointers:

struct thing *thing_create(int n)
{
        struct thing *t;

        if (n < 0)
                return ERR_PTR(-EINVAL);
        t = kzalloc(sizeof(*t), GFP_KERNEL);
        if (!t)
                return ERR_PTR(-ENOMEM);
        return t;
}

/* At the call site — pick the right test: */
t = thing_create(n);
if (IS_ERR(t))
        return PTR_ERR(t);              /* an error was encoded             */

/* If the function can return NULL *or* an error pointer (many lookups do): */
if (IS_ERR_OR_NULL(t))
        return t ? PTR_ERR(t) : -ENOENT;

/* If you only care whether it failed: */
ret = PTR_ERR_OR_ZERO(t);

/* If you must return an error pointer of a DIFFERENT pointer type: */
return ERR_CAST(t);                     /* keeps sparse quiet, no cast      */

The goto unwind. This is the single most important idiom in the chapter, and it is the one place the kernel uses goto freely and on purpose.

static int mylab_probe(struct platform_device *pdev)
{
        struct mylab *ml;
        int ret;

        ml = kzalloc(sizeof(*ml), GFP_KERNEL);
        if (!ml)
                return -ENOMEM;                 /* nothing acquired yet     */

        ml->buf = kmalloc(BUF_SZ, GFP_KERNEL);
        if (!ml->buf) {
                ret = -ENOMEM;
                goto err_free_ml;
        }

        ml->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(ml->clk)) {
                ret = PTR_ERR(ml->clk);
                goto err_free_buf;
        }

        ret = clk_prepare_enable(ml->clk);
        if (ret)
                goto err_put_clk;

        ret = request_irq(ml->irq, mylab_isr, 0, "mylab", ml);
        if (ret)
                goto err_disable_clk;

        platform_set_drvdata(pdev, ml);
        return 0;

        /* Labels are named for WHAT THEY UNDO, and they fall through in
         * exact reverse order of acquisition. Each error path jumps to the
         * label that undoes the step BEFORE the one that failed.           */
err_disable_clk:
        clk_disable_unprepare(ml->clk);
err_put_clk:
        clk_put(ml->clk);
err_free_buf:
        kfree(ml->buf);
err_free_ml:
        kfree(ml);
        return ret;
}

Three rules that make this reliable:

  1. Name labels after the action they perform, not err1/err2/out. When you insert a step in the middle, numbered labels all become wrong and nobody notices.
  2. Fall through, in reverse order. Each label undoes one thing and falls into the next.
  3. Jump to the label for the step before the one that failed. The failed step acquired nothing.

The devres API in the device model chapter removes most of this from driver probe paths — but you must be able to write it by hand first, because the moment your teardown order is not simply "reverse of acquisition", devres cannot express it and you are back here.

5. Experiment

CLAIM. The error paths in your code are never executed by normal testing, and the kernel gives you a way to execute them all.

METHOD. Fault injection makes allocations fail on demand.

# In the guest, with CONFIG_FAULT_INJECTION and CONFIG_FAILSLAB (lab-paranoid):
F=/sys/kernel/debug/failslab
echo 10   > $F/probability      # 10% of allocations fail
echo 1    > $F/times            # ...this many times (-1 for unlimited)
echo 0    > $F/verbose
echo Y    > /sys/kernel/debug/failslab/ignore-gfp-wait

insmod /mnt/host/modules/02-chardev/chardev.ko    # over and over

PREDICT FIRST: with a 10% allocation failure rate and four allocations in your probe path, what fraction of load attempts should fail cleanly? And of those, how many will leak — i.e. how confident are you in your unwind?

Then check with kmemleak:

echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak

RESULT. Record how many leaks you found. Most people find at least one on their first module, and that is the point of the experiment.

6. Failure mode

MistakeSymptom
Ignoring a return valueThe thing you needed did not happen; the failure surfaces somewhere unrelated
if (ret) where ret is a byte countSee the boundary chapter
Early return after acquiring somethingA leak, on a path nothing tests
Numbered goto labelsSomeone inserts a step and the unwinding is now off by one
Unwinding in the wrong orderFreeing a structure while an IRQ handler that uses it is still registered → use-after-free
Leaking an error pointer to user spaceerrno 517 in a program, and a confused bug report
IS_ERR on a pointer that returns NULL for failureThe NULL sails through IS_ERR and is dereferenced
Testing only the happy pathEvery one of the above ships

Concept 4: Types, Annotations, and Sparse

1. What problem it solves

C's type system cannot express "this pointer is in another address space", "this integer is little-endian regardless of the CPU", or "this pointer may only be read inside an RCU read-side critical section". Those are exactly the distinctions whose violation causes the kernel's worst bugs.

The kernel extends the type system with annotations that the compiler ignores and a separate checker, sparse, enforces.

2. Where it exists in the kernel

rg -n "__user|__iomem|__rcu|__percpu|__kernel" include/linux/compiler_types.h | head
$EDITOR Documentation/dev-tools/sparse.rst
make C=1 M=$PWD          # check the files being (re)compiled
make C=2 M=$PWD          # check everything, even unchanged files

3. Who owns or interacts with it

ActorInteraction
sparseEnforces address-space and endianness annotations
smatchA deeper flow-sensitive checker built on sparse's parser (make CHECK=smatch C=1)
The compilerSees the annotations expand to nothing (or to a GCC attribute)
The 0-day botRuns sparse on your patch and mails you the new warnings

4. The vocabulary

Fixed-width integers. Two spellings, and using the wrong one in the wrong place is a review comment:

ContextSpelling
Kernel-internal codeu8 u16 u32 u64, s8 s16 s32 s64
include/uapi/ headers__u8 __u16 __u32 __u64, __s8 … — user space has no u32

Semantic types, which exist so a mismatch is visible: size_t, ssize_t, loff_t (file offsets), pid_t, gfp_t (allocation flags), dma_addr_t (a device's view of memory — not a physical address), phys_addr_t, resource_size_t, cycles_t, atomic_t, refcount_t.

Address-space annotations, checked by sparse:

AnnotationMeansAccess it with
__userA user-space pointercopy_*_user, get_user, put_user
__iomemA pointer to device MMIOreadb/w/l/q, writeb/w/l/q, ioread32, memcpy_fromio
__rcuOnly valid inside an RCU read-side critical sectionrcu_dereference, rcu_assign_pointer
__percpuA per-CPU pointer, not a real addressthis_cpu_ptr, per_cpu_ptr

Endianness types: __le16/32/64, __be16/32/64. Sparse tracks these and flags implicit conversions.

struct on_disk_header {
        __le32 magic;        /* the FORMAT is little-endian, whatever the CPU is */
        __le64 size;
        __be16 port;         /* network byte order */
};

/* Every access converts explicitly. sparse rejects `if (h->magic == MAGIC)`. */
if (le32_to_cpu(h->magic) != MYFS_MAGIC)
        return -EINVAL;
h->size = cpu_to_le64(new_size);
port    = be16_to_cpu(h->port);

/* And for a possibly-unaligned buffer (a packet, a firmware blob): */
u32 v = get_unaligned_le32(ptr);

Function and variable attributes you will see constantly:

AttributeEffect
__init / __exitPlaced in a section freed after boot / after module unload
__initdata / __initconstSame, for data
__ro_after_initWritable during init, read-only forever after — a hardening measure
__must_checkThe caller is warned if it discards the return value
__packedNo padding. Slow on some architectures; use only for on-wire/on-disk layouts.
__aligned(n)Force alignment (e.g. cache line, DMA requirement)
__printf(a, b)Format-string checking for a printk-like function
__maybe_unusedSuppress an unused warning in a #ifdef-heavy file
noinline / __always_inlineControl inlining, usually for stack or tracing reasons

printk format specifiers — the kernel's are not libc's, and the differences matter:

SpecifierPrints
%pA hashed pointer. Since the pointer-leak hardening work, raw addresses are not printed by default.
%pxThe raw address. A deliberate security decision — justify it in review.
%pKHashed or raw depending on kptr_restrict and the reader's privilege
%pS / %psA symbol name for a code address: vfs_read+0x2c/0x180
%peAn error pointer, by name: -ENOMEM instead of fffffffffffffff4
%pI4 / %pI6 / %pMAn IPv4 / IPv6 address / a MAC address
%pOF / %pfwA device-tree node / a fwnode path
$EDITOR Documentation/core-api/printk-formats.rst      # the full list, in your tree

5. Experiment

CLAIM. sparse finds a whole class of bug that the compiler compiles without complaint.

METHOD. In a scratch module, write three deliberate annotation errors:

static int deliberate(void __user *up, void __iomem *io, __le32 *disk)
{
        char c    = *(char *)up;        /* 1. user deref, cast away __user  */
        u32  reg  = *(u32 *)io;         /* 2. MMIO deref instead of readl() */
        if (*disk == 0x1234)            /* 3. __le32 compared as native     */
                return 1;
        return c + reg;
}

PREDICT FIRST: for each of the three, does gcc warn? Does gcc -Wall -Wextra warn? Does sparse warn? Does it crash at runtime, and on which architectures?

make        M=$PWD modules            # gcc alone
make W=1    M=$PWD modules            # extra warnings
make C=2    M=$PWD modules            # sparse

RESULT. Note which tool caught which. Error 3 in particular is invisible on a little-endian machine and produces a filesystem that cannot be read on a big-endian one — the exact bug class sparse was written for.

6. Failure mode

MistakeSymptom
Casting away __userOops on SMAP/PAN hardware; a security hole on older hardware
Dereferencing __iomemWorks on x86 by accident (MMIO is memory-mapped); fails or reorders badly on other architectures
Reading __rcu without rcu_dereferenceA use-after-free that appears only under load
Ignoring endianness typesWorks on your machine; corrupts data on the other half of the world's machines
u32 in a uapi headerThe header does not compile in user space
%p where you needed a real addressYou debug for an hour with a hashed value that changes every boot
%px in code you shipAn address leak that defeats KASLR
__packed on a hot structUnaligned accesses; a large, silent performance loss on some architectures
Calling an __init function after bootmodpost section-mismatch warning; a jump into freed memory if you ignore it

The Idioms You Must Read Fluently

Not concepts — vocabulary. You will hit every one of these in the first file you open.

container_of: the kernel's inheritance

There are no base classes. Instead, a generic structure is embedded in yours, and container_of() recovers the outer structure from a pointer to the inner one.

struct my_device {
        int                  my_field;
        struct device        dev;      /* embedded, not a pointer */
        struct list_head     node;     /* embedded, not a pointer */
};

/* Given a `struct device *d` that the driver core handed you: */
struct my_device *md = container_of(d, struct my_device, dev);
/*                                  ^   ^                 ^
 *                    the inner ptr─┘   │                 └─ the member name
 *                                      └─ the outer type
 * It is pointer arithmetic: d minus offsetof(struct my_device, dev).      */
rg -n -A 12 "define container_of" include/linux/container_of.h

struct list_head: intrusive lists

The list node lives inside your object, so a list operation never allocates and cannot fail.

struct my_device *md;
LIST_HEAD(devices);                        /* a head, on the stack or static */

list_add_tail(&md->node, &devices);
list_for_each_entry(md, &devices, node)    /* md is set to each container    */
        pr_info("%d\n", md->my_field);
list_for_each_entry_safe(md, tmp, &devices, node)  /* safe against deletion  */
        list_del(&md->node);
rg -n "list_for_each_entry\b" include/linux/list.h
rg -n "hlist_for_each_entry\b" include/linux/list.h    # hash tables: 1-word heads

The helper macros

MacroDoes
ARRAY_SIZE(a)Element count, with a build-time check that a is really an array
min_t(type, a, b) / max_t / clampCompare after an explicit cast — avoids signed/unsigned surprises
DIV_ROUND_UP(n, d), ALIGN(x, a), PAGE_ALIGN(x)Arithmetic without off-by-ones
BIT(n), GENMASK(hi, lo)Bit and bitfield masks, readable
FIELD_GET(mask, reg), FIELD_PREP(mask, val)Extract/insert a register bitfield without shifts. From <linux/bitfield.h>. Use these.
BUILD_BUG_ON(cond), static_assert(cond)Fail the build on a bad assumption (struct size, ABI layout)
likely(x) / unlikely(x)Branch hints. Only in genuinely hot paths; measure.
READ_ONCE(x) / WRITE_ONCE(x)Stop the compiler tearing, reordering, or re-reading. Required for lockless access.
struct_size(p, member, n)Overflow-safe size of a struct with a trailing flexible array
swap(a, b)What it says
/* Register bitfields, before and after: */
#define CTRL_MODE   GENMASK(5, 3)

val = (reg & CTRL_MODE) >> 3;              /* shift constants: bug bait   */
val = FIELD_GET(CTRL_MODE, reg);           /* same thing, checked         */

reg = (reg & ~CTRL_MODE) | (val << 3);
reg = (reg & ~CTRL_MODE) | FIELD_PREP(CTRL_MODE, val);

Coding style, in one table

Documentation/process/coding-style.rst is short, opinionated, and worth reading in full once. checkpatch.pl enforces most of it. The parts people get wrong:

RuleNote
Tabs, 8 columns wideNot spaces. If your code needs more than three levels of indentation, that is the actual message.
Line length80 columns is the guideline; up to 100 is tolerated when it genuinely helps readability. Do not split a printk string literal to fit — grep-ability wins.
BracesOmitted for a single statement — but if any branch of an if/else needs them, all do
One declaration per line, no assignments in declarations of anything non-trivial—
No typedef for structsWith narrow exceptions listed in the style doc
Function nameslower_snake_case, prefixed with the subsystem or driver name
Comments/* … */. net/ and drivers/net/ use a different block-comment opening style from the rest of the tree — match the file you are in.
net/ also uses "reverse Christmas tree"Local declarations sorted longest line first. Only in networking.
SPDX identifier on line 1// SPDX-License-Identifier: GPL-2.0 in .c, /* … */ in headers

Tip: Do not fight style. Run ./scripts/checkpatch.pl --strict -f yourfile.c and clang-format with the tree's .clang-format on your own new code only — never reformat surrounding code in a patch that does something else. A style change mixed into a functional change is one of the fastest ways to have a patch bounced.


Validation / Self-check

  1. Name the kernel replacement for each of: malloc, printf, snprintf, strlcpy, atoi, assert.
  2. Why is scnprintf safer than snprintf? Write the buggy accumulation loop from memory and fix it.
  3. Why can kernel code not use floating point, and what does kernel_fpu_begin() cost?
  4. Your code builds on x86-64 and fails to link on 32-bit ARM with undefined reference to __udivdi3. What did you write, and what should you have written?
  5. How large is the kernel stack, and name four language features it forbids as a consequence.
  6. What does CONFIG_VMAP_STACK actually change? (It does not prevent overflow.)
  7. Explain ERR_PTR/IS_ERR/PTR_ERR in terms of the address space. When do you need IS_ERR_OR_NULL instead?
  8. Give the three rules of goto-based unwinding, and say what goes wrong when labels are numbered.
  9. Which errno values must never reach user space, and where are they defined?
  10. What do __user, __iomem, __rcu, and __percpu mean, and which tool enforces them?
  11. Why is %p hashed, when would you use %px, and what does %pe save you?
  12. What does container_of compute? Write it out as arithmetic.
  13. Why do the kernel's lists put the node inside the object rather than allocating a node?
  14. What does FIELD_GET buy over a shift and a mask?
  15. Name three things sparse catches that gcc -Wall -Wextra does not.

Next: Context and Atomicity — the chapter to read twice, and the one whose bugs never appear at the line that caused them.