Enabling a hardening option does not mean all related attacks are blocked. First determine whether the mechanism targets the attack surface, error detection, exploitation conditions, or information leaks, and then verify the actual enabled state.
Target to Check
Where to Read
Address, permission, and boundary checks
Sections 1–3
Detection coverage and lockdown
Sections 4–5
seccomp, allocation protection, and observation
Sections 6–8
1. KASLR and Strict Memory Permissions
KASLR randomizes part of the kernel's address layout during boot. The scope and granularity of randomization vary by architecture, configuration, and implementation; it is incorrect to assume that code, data, and modules are all controlled by a single random base address. On x86, early decompression/relocation code participates in selecting the location and should not be described as being solely completed by the bootloader.
It increases the difficulty for attackers to predict the location of code or data; address leaks and limited entropy can weaken this effect, so it must be combined with address information restrictions. kptr_restrict=2 hides addresses in outputs controlled by %pK rules; it does not mean "only non-root users cannot see them," nor does it guarantee that all kernel address outputs are controlled by this setting.
Mechanisms such as CONFIG_STRICT_KERNEL_RWX and module-related strict permissions constrain writable and executable regions. Read-only data and non-executable data pages limit code modification and direct execution of injected data, but do not automatically prevent all code reuse and data tampering. See Kernel Self-Protection and kptr_restrict documentation.
The user-space /proc/sys/kernel/randomize_va_space controls user process ASLR and cannot be used as evidence that kernel KASLR or per-syscall kernel stack offset randomization is enabled.
2. Stack Protector and Object Boundary Checks
The compiler inserts canaries only into functions that match the option rules; not every function necessarily has a canary. Checks typically occur before the protected function exits; upon detecting canary corruption, it enters a failure handling path. It cannot cover out-of-bounds writes at arbitrary locations, nor does it guarantee immediate detection before data is misused.
Kernel CONFIG_FORTIFY_SOURCE strengthens certain memory/string operations based on object sizes derivable by the compiler. Known errors can be diagnosed at compile time, and some dynamic out-of-bounds accesses can trigger runtime reports and termination paths. Unknown object boundaries and uncovered operations remain gaps; one cannot deduce that "all memcpy calls are safe." It is also not the glibc _FORTIFY_SOURCE=3 compile macro.
CONFIG_HARDENED_USERCOPY checks object and region constraints during kernel-to-user and user-to-kernel copies, addressing certain illegal copies. It does not prove that copied content conforms to business protocols, nor does it make passed lengths automatically trustworthy.
3. KPTI: Limiting Kernel Mappings in User-Space Page Tables
Meltdown-class issues involve transient execution leaks on specific processors. KPTI ensures that page tables used by user space retain only user mappings and a small number of mappings required for entering the kernel, switching to the appropriate kernel page table upon entry.
This reduces the conditions for user-space transient access to a large range of kernel mappings, but it is not a unified solution for all Spectre/Meltdown-class vulnerabilities. Page table switching and TLB management incur overhead; capabilities like PCID can mitigate this; actual impact should be measured against workloads and cannot be fixed to a specific percentage.
Enablement depends on processor vulnerability status, kernel, and boot policies; it cannot be generalized as "automatically disabled for all Intel/AMD generations after a certain point." Check the actual reports in /sys/devices/system/cpu/vulnerabilities/ and distinguish between Not affected, mitigated, and existing risks. See x86 PTI.
4. Detection Tools Have Different Coverage
Tool
Main Issues Found
Important Boundaries
KASAN
Memory errors like out-of-bounds and use-after-free
Capabilities and costs differ between generic, software tag, and hardware tag modes
KMSAN
Use and propagation of uninitialized values
Requires corresponding compiler, architecture, and instrumentation support
UBSAN
Selected undefined behavior checks
Which sub-checks are enabled determines coverage; it cannot be broadly stated as checking all integer overflows
KFENCE
Sampled detection of heap memory errors
Low overhead relies on sampling; not reporting does not mean all accesses are error-free
Unsigned wraparound is not itself undefined behavior in C; kernel compile options and sub-checks also affect integer behavior. A test run that reports no issues only indicates that no problems were caught within that execution and coverage scope.
These tools are not exclusively for kernel development: for example, hardware-tag KASAN or KFENCE can be applicable to different production targets, but actual overhead and support conditions should be evaluated; a uniform conclusion of "twice as slow" cannot be given. Refer to KASAN, KMSAN, UBSAN, KFENCE.
5. Lockdown: Limiting High-Privilege Users from Changing Kernel Trust Boundaries
Lockdown can restrict interfaces that read/write kernel memory, replace unverified code, or expose secrets. The integrity and confidentiality modes impose different constraints; the latter further restricts certain read paths. It is inaccurate to describe all BPF and debugging functionalities with a blanket "all disabled."
The status interface is typically /sys/kernel/security/lockdown in securityfs, not /proc/sys/kernel/lockdown. Secure Boot may trigger lockdown depending on the distribution, configuration, and patches, but the two cannot be unconditionally equated; signature verification, module loading policies, and lockdown must be checked separately. See kernel_lockdown(7).
6. Seccomp: Narrowing Interfaces by System Call ABI
The seccomp filter reads the system call number, architecture identifier, instruction address, and scalar parameters, returning actions such as ALLOW, ERRNO, TRAP, KILL_PROCESS, or USER_NOTIF via a classic BPF filter program. There is no actual generic return action named SECCOMP_RET_DENY.
Installation typically requires no_new_privs or satisfying permission conditions within the corresponding namespace. Filters are attached to threads and can synchronize thread groups using TSYNC; allowed fork/exec calls inherit filter constraints. Overlapping filters cannot relax previously stricter results with later ALLOW actions.
What the Filter Sees
What It Cannot Directly Know
System call number
Without combining with ABI, the call's meaning cannot be correctly determined
Scalar parameters and pointer values
Cannot be dereferenced into stable path strings
Architecture identifier
Must be included in rule evaluation; cannot be ignored
Therefore, allowing openat and comparing pointer values does not equate to allowing only the opening of specified paths.
Target Memory May Change After Supervisor Checks
USER_NOTIF allows the supervisor to receive notifications and handle or respond, but since it reads the target process's mutable memory before allowing the original syscall to continue, it faces TOCTOU (Time-of-Check to Time-of-Use) issues. The notification mechanism is not an automatically secure file access controller. It should be used in conjunction with LSM, file descriptor permissions, and other isolation mechanisms; see seccomp filter documentation.
After checking, modify the path buffer, then choose either "continue using the current pointer" or "operate on the already held object." By comparing the actually used targets, one can see why the facts established at check time become stale.
Preparing the visual
Is the object checked the same as the object subsequently used?
A snapshot of a target process's memory at check time does not freeze its subsequent parameters. Secure operations must be bound to the actually verified object and permissions; CONTINUE cannot be treated as such a binding.
{"id":"seccomp-check-use-race","title":"Is the object checked the same as the object subsequently used?","summary":"A snapshot of a target process's memory at check time does not freeze its subsequent parameters. Secure operations must be bound to the actually verified object and permissions; CONTINUE cannot be treated as such a binding.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"target\"><button data-mode=\"A\" data-i18n=\"a\"></button><button data-mode=\"B\" data-i18n=\"b\"></button></div><div class=\"actions\"><button id=\"check\" data-i18n=\"check\"></button></div><label for=\"mode\" data-i18n=\"mode\"></label><select id=\"mode\"><option value=\"continue\" data-i18n=\"continueCall\"></option><option value=\"held\" data-i18n=\"held\"></option></select><div class=\"track-row\"><span data-i18n=\"buffer\"></span><div id=\"buffer\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"checked\"></span><div id=\"checked\" class=\"track\"></div></div><div class=\"actions\"><button id=\"perform\" data-i18n=\"perform\"></button></div><div class=\"card\"><header data-i18n=\"result\"></header><div id=\"result\" class=\"readout\"></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.readout{min-height:4.8em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let target='A',held=null,approved=false,result=null;function draw(){cells('buffer',[{text:'path → '+target,cls:'on'}]);cells('checked',[{text:held===null?t('unseen'):'ref → '+held+' · '+t(approved?'approved':'rejected'),cls:approved?'on':'gold'}]);set('result',result===null?t('pending'):result.via+' → '+result.object);$('#perform').disabled=!approved;pressed(target);set('explanation',t('note'));}document.querySelectorAll('[data-mode]').forEach(e=>e.onclick=()=>{target=e.dataset.mode;draw();});$('#check').onclick=()=>{held=target;approved=held==='A';draw();};$('#perform').onclick=()=>{if(!approved)return;const stable=$('#mode').value==='held';result={object:stable?held:target,via:stable?'ref':'path'};draw();};$('#reset').onclick=()=>{target='A';held=null;approved=false;result=null;draw();};draw();","audio":false,"strings":{"a":"Points to A","approved":"Verified and allowed","b":"Points to B","buffer":"Mutable path buffer","check":"Obtain reference and verify","checked":"Object held at check time","continueCall":"Original call CONTINUE","heading":"Is the object checked the same as the object subsequently used?","held":"Supervisor uses verified reference","intro":"First obtain and verify object A, then modify the target process's path buffer. Choose to either let the original call continue or have the supervisor use a verified stable object reference.","mode":"How to perform operation","note":"After A is verified, switch to B, then execute. The original call re-reading mutable parameters will get B; the operation holding the reference still uses the already verified A.","pending":"Not yet accessed","perform":"Perform selected operation","rejected":"Verified and rejected","reset":"Reset","result":"Most recent actual access","scope":"A/B are toy objects without real file access; the policy only allows A. The check action first obtains a stable reference and then verifies it; the controlled solution has the supervisor perform operations via that reference, rather than reopening the original string later. Notification ID, ADDFD, path resolution constraints, and concurrency protocols are omitted; this is not a complete seccomp sandbox implementation.","scopeLabel":"Model Scope and Conventions","target":"Target process current parameters","unseen":"Not yet checked"}}
7. Zeroing and Freelist Protection
init_on_alloc / init_on_free can zero objects and pages in the respective allocation or free paths, reducing risks such as old data exposure, but it does not restore dangling pointers to validity, nor does it mean all allocators and special paths use the same handling.
CONFIG_SLAB_FREELIST_RANDOM changes the arrangement of free objects, while CONFIG_SLAB_FREELIST_HARDENED strengthens freelist metadata; random order and pointer protection are different mechanisms. They increase the difficulty of certain heap exploits but cannot replace fixing UAF (Use-After-Free).
Check the target kernel configuration and /proc/cmdline simultaneously, and then judge based on the runtime state. Having the capability in the configuration does not mean it is adopted at boot; a state file being invisible may simply be due to permission or namespace observation limitations.