[{"content":"One of the best ways to learn systems programming is by reading real-world code. Recently, while reading the Linux kernel source, I noticed something interesting inside drivers/pinctrl/core.h:\n#include \u0026lt;linux/kref.h\u0026gt; #include \u0026lt;linux/list.h\u0026gt; #include \u0026lt;linux/mutex.h\u0026gt; #include \u0026lt;linux/radix-tree.h\u0026gt; #include \u0026lt;linux/types.h\u0026gt; #include \u0026lt;linux/pinctrl/machine.h\u0026gt; struct dentry; struct device; struct device_node; struct module; At first glance, these lines seem incomplete. We see declarations like struct device; but no actual definition. Where is the complete structure with all its members? Why didn\u0026rsquo;t file defining this structure isn\u0026rsquo;t included?\nThis technique is called a forward declaration, and understanding it is key to writing efficient C code, especially in large projects.\nWhat Are Forward Declarations? A forward declaration tells the compiler that a type exists, but does not provide its full definition yet. Here is a simple example:\nstruct device; // Forward declaration This is different from including a full definition:\n#include \u0026lt;device.h\u0026gt; // Full definition with all members When you write a forward declaration, the compiler learns:\nstruct device is a valid type name you can create pointers to it you can pass references to it However, the compiler still does not know:\nthe size of the structure what fields it contains how it is laid out in memory Because of this limited knowledge, you can only perform certain operations with forward declared types.\nBenefits of Forward Declarations Dramatically Reduce Compilation Time This is perhaps the most important benefit. When you include a header file, you do not just get one definition. You trigger an entire cascade of dependencies.\nConsider what happens when you include a device.h file:\ncore.h includes device.h includes kobject.h includes list.h includes spinlock.h includes irqflags.h (and more files) includes atomic.h includes compiler.h Each file gets parsed, tokenized, and processed. For large projects like the Linux kernel:\nA single include can expand to thousands of lines of code In projects with thousands of header files, this expansion can make compilation take minutes or even hours Using forward declarations breaks these chains and reduces recompilation time significantly In large C projects, forward declarations can reduce compile times by 20 to 40 percent in some cases.\nBreak Circular Dependencies Forward declarations are the only way to handle circular dependencies elegantly in C:\nWithout forward declarations, this creates a problem:\n// header1.h #include \u0026#34;header2.h\u0026#34; struct Type1 { struct Type2 *ptr; }; // header2.h #include \u0026#34;header1.h\u0026#34; struct Type2 { struct Type1 *ptr; }; Each file tries to include the other. This causes infinite recursion and compilation fails.\nWith forward declarations, the problem disappears:\n// header1.h struct Type2; // Forward declaration struct Type1 { struct Type2 *ptr; // Works fine }; // header2.h struct Type1; // Forward declaration struct Type2 { struct Type1 *ptr; // Works fine }; Now both files can refer to each other without problems.\nImprove Code Design Forward declarations encourage better API design. When a header only uses forward declarations, it signals that these types are used as opaque pointers. This means the internal structure is hidden from users.\nThis leads to better encapsulation:\n// Bad practice: requires full definition struct device dev; dev.parent = \u0026amp;dev_p; // Direct member access // Good practice: with forward declaration struct device *dev = device_create(); device_set_active(dev); // Function based access When Should You Use Forward Declarations? Use forward declarations in these situations:\nYou only need pointers or references struct device; void func(struct device *dev); // Only passing pointer void setup(struct device **dev_ptr); // Pointer to pointer Breaking compilation dependencies is important In large projects or libraries where compilation speed matters, forward declarations become part of the architecture design.\nHandling circular dependencies between files struct A { struct B *b; }; struct B { struct A *a; }; Hiding implementation details with opaque types Users see only the pointer, not what is inside:\n// Public header struct FileHandle; // Users do not need to know what is inside struct FileHandle *file_open(const char *name); int file_read(struct FileHandle *handle, char *buf); Creating stable public APIs When internal struct members change, you do not need to recompile all user code.\nWhen Should You NOT Use Forward Declarations? Do not use forward declarations when you need to:\nAccess struct members struct device; void setup(struct device *dev) { dev-\u0026gt;parent = NULL; // ERROR: need full definition } Create instances of the struct struct device; void func() { struct device dev; // ERROR: need full definition } Use the sizeof operation struct device; size_t size = sizeof(struct device); // ERROR: compiler does not know size In these cases, you must include the full definition.\nA Simple Rule to Follow Here is a practical rule that works well:\nIn header files:\nprefer forward declarations when possible\nIn source files:\ninclude the actual headers when you need full definitions\nThis keeps your interfaces small and clean while allowing implementation code full access to what it needs.\nReal World Example: The Linux Kernel The Linux kernel uses this approach consistently. Let\u0026rsquo;s look at two files side by side to understand the strategy.\nIn the header file drivers/pinctrl/core.h, you see:\n// Include what is actually embedded in our structures #include \u0026lt;linux/kref.h\u0026gt; // Reference counting #include \u0026lt;linux/list.h\u0026gt; // Linked lists #include \u0026lt;linux/mutex.h\u0026gt; // Synchronization #include \u0026lt;linux/radix-tree.h\u0026gt; // Data structure #include \u0026lt;linux/types.h\u0026gt; // Basic types // Only include our own definitions #include \u0026lt;linux/pinctrl/machine.h\u0026gt; // Forward declarations for opaque pointers struct dentry; // For debugfs operations struct device; // For driver model struct device_node; // For device tree struct module; // For module information But in the actual implementation file drivers/pinctrl/core.c, all the full definitions are included:\n#include \u0026lt;linux/debugfs.h\u0026gt; #include \u0026lt;linux/device.h\u0026gt; #include \u0026lt;linux/err.h\u0026gt; #include \u0026lt;linux/export.h\u0026gt; #include \u0026lt;linux/init.h\u0026gt; #include \u0026lt;linux/kernel.h\u0026gt; #include \u0026lt;linux/kref.h\u0026gt; #include \u0026lt;linux/list.h\u0026gt; #include \u0026lt;linux/seq_file.h\u0026gt; #include \u0026lt;linux/slab.h\u0026gt; [...] This shows the pattern clearly:\nIn the header file: use forward declarations for types you only pass as pointers In the source file: include everything you actually need to work with those types This design:\nKeeps the header file minimal and stable Allows the implementation full access to all definitions it needs Reduces recompilation when other files change Makes the public interface cleaner and more maintainable Lets developers using the library avoid unnecessary includes Final Thoughts Forward declarations are a small C feature that becomes increasingly important as projects grow larger.\nIn tiny projects, they may feel unnecessary. You can include everything and not worry about compilation time.\nIn large systems like the Linux kernel, they become part of architecture design itself. Understanding when and how to use them becomes essential.\nThe goal is not simply to use fewer includes.\nThe real goal is to control dependencies intentionally.\nThat is the actual value of forward declarations. By choosing what information you expose and what you hide, you create better designed code that compiles faster and changes more safely.\n","permalink":"https://www.ankitkdev.com/blog/forward-declaration-in-c/","summary":"\u003cp\u003eOne of the best ways to learn systems programming is by reading real-world code. Recently, while reading the Linux kernel source, I noticed something interesting inside \u003ccode\u003edrivers/pinctrl/core.h\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/kref.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/list.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/mutex.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/radix-tree.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/types.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;linux/pinctrl/machine.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e dentry;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e device;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e device_node;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e module;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt first glance, these lines seem incomplete. We see declarations like \u003ccode\u003estruct device;\u003c/code\u003e but no actual definition. Where is the complete structure with all its members? Why didn\u0026rsquo;t file defining this structure isn\u0026rsquo;t included?\u003c/p\u003e","title":"Understanding Forward Declarations in C"},{"content":"Disclaimer This website is provided for educational and informational purposes only. While efforts are made to keep content accurate and up to date, no guarantees are made regarding completeness, accuracy, or reliability.\nTechnical Content Articles related to Linux, kernel development, embedded systems, C programming, and debugging reflect personal experience and research. Readers should independently verify technical instructions before applying them in production or critical environments.\nExternal Links This website may contain links to third-party websites. I am not responsible for the content, privacy practices, or availability of those external sites.\nIntellectual Property Unless otherwise stated, original text, code snippets, and media on this website are the property of the author. Reasonable quotation with attribution and a link to the original source is permitted.\nLimitation of Liability Use of this website and its content is at your own risk. The author is not liable for any direct or indirect damages arising from use of this website or reliance on its content.\nJurisdiction This website is governed by the laws of India.\nContact For questions or concerns, contact: ankitkhushwaha.dev@gmail.com\nLast Updated: May 14, 2026\n","permalink":"https://www.ankitkdev.com/legal-notice/","summary":"\u003ch3 id=\"disclaimer\"\u003eDisclaimer\u003c/h3\u003e\n\u003cp\u003eThis website is provided for educational and informational purposes only. While efforts are made to keep content accurate and up to date, no guarantees are made regarding completeness, accuracy, or reliability.\u003c/p\u003e\n\u003ch3 id=\"technical-content\"\u003eTechnical Content\u003c/h3\u003e\n\u003cp\u003eArticles related to Linux, kernel development, embedded systems, C programming, and debugging reflect personal experience and research. Readers should independently verify technical instructions before applying them in production or critical environments.\u003c/p\u003e\n\u003ch3 id=\"external-links\"\u003eExternal Links\u003c/h3\u003e\n\u003cp\u003eThis website may contain links to third-party websites. I am not responsible for the content, privacy practices, or availability of those external sites.\u003c/p\u003e","title":"Legal Notice"},{"content":"This website is a personal blog and portfolio.\nInformation Collection When you visit this website, basic technical information such as IP address, browser type, device information, and pages visited may be logged automatically by hosting providers or analytics services.\nAnalytics This website may use analytics tools to understand traffic and improve content. These tools may collect anonymized usage information.\nCookies This website itself does not intentionally use cookies for tracking, though third-party embedded services or external links may use cookies according to their own policies.\nExternal Links This website may contain links to third-party websites. I am not responsible for their privacy practices or content.\nData Sharing Personal information is not sold or shared with third parties except where required for website functionality or legal compliance.\nContact If you have privacy-related questions, contact: ankitkhushwaha.dev@gmail.com\nChanges This privacy policy may be updated periodically. Updates will be reflected on this page.\nLast Updated: May 14, 2026\n","permalink":"https://www.ankitkdev.com/privacy-policy/","summary":"\u003cp\u003eThis website is a personal blog and portfolio.\u003c/p\u003e\n\u003ch3 id=\"information-collection\"\u003eInformation Collection\u003c/h3\u003e\n\u003cp\u003eWhen you visit this website, basic technical information such as IP address, browser type, device information, and pages visited may be logged automatically by hosting providers or analytics services.\u003c/p\u003e\n\u003ch3 id=\"analytics\"\u003eAnalytics\u003c/h3\u003e\n\u003cp\u003eThis website may use analytics tools to understand traffic and improve content. These tools may collect anonymized usage information.\u003c/p\u003e\n\u003ch3 id=\"cookies\"\u003eCookies\u003c/h3\u003e\n\u003cp\u003eThis website itself does not intentionally use cookies for tracking, though third-party embedded services or external links may use cookies according to their own policies.\u003c/p\u003e","title":"Privacy Policy"},{"content":"Overview C memory layout looks simple in isolation: structs store fields sequentially, unions overlap memory, and flexible array members allow variable-sized trailing storage. But combining these features can create subtle layout issues.\nWhile working on a Linux kernel selftest, I encountered a case where a union contained multiple structures, each ending with a flexible array member at different offsets. Although the code was functionally correct, this confused the compiler into treating the union as effectively variable-sized, triggering a layout warning.\nThis article tackles an unusual case: what happens when you place variable-sized structs (structs with FAMs) inside a union? The answer reveals elegant C patterns for memory aliasing and why offsetof is a zero-cost tool for precision.\nWe will start by understanding how structs, unions, and flexible array members are laid out in memory, then use a real Linux kernel patch to see how these rules interact in practice.\n1. Struct Memory Layout In C, a struct lays out its fields sequentially in memory, from top to bottom. The compiler may insert invisible padding bytes between fields to satisfy alignment requirements of the hardware.\nstruct example { char a; // 1 byte // 3 bytes padding (to align \u0026#39;b\u0026#39; to a 4-byte boundary) int b; // 4 bytes char c; // 1 byte // 3 bytes padding (to make total size a multiple of 4) }; // sizeof(struct example) == 12, NOT 6 Memory picture:\nOffset 0: [ a ][ pad ][ pad ][ pad ] Offset 4: [ b ][ b ][ b ][ b ] Offset 8: [ c ][ pad ][ pad ][ pad ] You can always ask the compiler where a field lives with offsetof:\noffsetof(struct example, b) // -\u0026gt; 4 offsetof(struct example, c) // -\u0026gt; 8 2. Union Memory Layout A union is different. All members share the same starting address (offset 0). The union is sized to fit the largest member.\nunion variant { int i; // 4 bytes short s; // 2 bytes char c; // 1 byte }; // sizeof(union variant) == 4 Memory picture - all three members overlap:\nOffset 0: [ i/s/c ][ i ][ i ][ i ] ^ same address, different interpretation Unions are commonly used when you want one memory region to be interpreted as different types depending on context: a tagged union, a type-punning trick, or a protocol message that can carry different payloads.\nunion data { int x; char bytes[4]; }; union data d = { .x = 65 }; printf(\u0026#34;%d\\n\u0026#34;, d.x); // interpret as int printf(\u0026#34;%d\\n\u0026#34;, d.bytes[0]); // interpret as first byte All union members begin at offset 0, so the same memory can be interpreted in different ways.\n3. Flexible Array Members (FAMs) A flexible array member is a zero-length array declared as the last field of a struct. It acts as a variable-length tail allowing you to allocate extra memory beyond the struct\u0026rsquo;s fixed size and access it through the FAM.\nNormally every field in a struct has a fixed, known size. But sometimes you don\u0026rsquo;t know at compile time how much data a struct needs to carry. Think of a network packet, a kernel message, or a string with a length header.\nThe naive solution is to pre-allocate a large fixed buffer:\nstruct message { int length; char data[1024]; // wastes memory for small messages }; This wastes memory for small messages and breaks for large ones. FAMs solve this cleanly.\nThe Syntax struct message { int length; char data[]; // \u0026lt;- flexible array member, no size given }; data[] has no fixed size. It is a placeholder that says: \u0026ldquo;there will be bytes here, how many depends on how much memory you allocate.\u0026rdquo;\nHow You Use It int n = 5; struct message *msg = malloc(sizeof(struct message) + n); msg-\u0026gt;length = n; msg-\u0026gt;data[0] = \u0026#39;H\u0026#39;; msg-\u0026gt;data[1] = \u0026#39;e\u0026#39;; msg-\u0026gt;data[2] = \u0026#39;l\u0026#39;; msg-\u0026gt;data[3] = \u0026#39;l\u0026#39;; msg-\u0026gt;data[4] = \u0026#39;o\u0026#39;; Memory picture:\nsizeof(struct message) \u0026lt;--------------------\u0026gt; [ length (4 bytes) ][ H ][ e ][ l ][ l ][ o ] ^ data[] starts here (extra bytes you malloc\u0026#39;d) The struct\u0026rsquo;s fixed part (length) is followed immediately by the extra bytes you allocated. data[] just gives you a typed pointer into that region. No copying, no indirection.\nThe Hard Rule The C standard requires FAMs to be the last field in a struct. This is not a style guideline; it is enforced by the compiler.\nstruct broken { char data[]; // \u0026lt;- FAM not at end int length; // \u0026lt;- compiler error }; This rule is the root of the problem we explore next.\n4. The Problem: A Union of Structs, Each With a FAM The Linux kernel\u0026rsquo;s IPsec selftest (tools/testing/selftests/net/ipsec.c) needs to pack algorithm data into a netlink message. Three different algorithm families exist, each with a FAM at a different offset:\nstruct xfrm_algo { char alg_name[64]; unsigned int alg_key_len; /* in bits */ char alg_key[]; // FAM at offset 68 }; struct xfrm_algo_auth { char alg_name[64]; unsigned int alg_key_len; /* in bits */ unsigned int alg_trunc_len; /* in bits */ char alg_key[]; // FAM at offset 72 }; struct xfrm_algo_aead { char alg_name[64]; unsigned int alg_key_len; /* in bits */ unsigned int alg_icv_len; /* in bits */ char alg_key[]; // FAM at offset 72 }; Each struct ends with alg_key[], but the FAM starts at a different offset because each fixed header is a different size.\nThe original code tried to combine them in a struct like this:\nstatic int xfrm_state_pack_algo(...) { struct { union { struct xfrm_algo alg; struct xfrm_algo_aead aead; struct xfrm_algo_auth auth; } u; char buf[XFRM_ALGO_KEY_BUF_SIZE]; // \u0026lt;- intended key backing buffer } alg = {}; ... } Memory picture of what was intended:\nOffset 0 68 580 +---------------------------------------------------+------+----------+ | u.alg [ alg_name(64) | key_len | pad ] alg_key[] | +---------------------------------------------------+------+----------+ | u.aead [ alg_name(64) | key_len | icv ] alg_key[] | buf[512] +---------------------------------------------------+------+----------+ | u.auth [ alg_name(64) | key_len | trunc] alg_key[] | +---------------------------------------------------+------+----------+ ↑ Different offsets! Can one buf overlap all three? The problem is that the FAMs start at different offsets, so buf cannot correctly overlap all of them simultaneously. More importantly, the compiler sees the variable-sized union in a non-terminal position and raises:\nipsec.c:835:5: warning: field \u0026#39;u\u0026#39; with variable sized type \u0026#39;union (unnamed union at ipsec.c:831:3)\u0026#39; not at the end of a struct or class is a GNU extension [-Wgnu-variable-sized-type-not-at-end] Why the Compiler Sees This as a Problem Each struct with a FAM is variable-sized from the compiler\u0026rsquo;s perspective. The fixed header has a known size, but the FAM can hold 0 bytes or 10000 bytes depending on runtime allocation. So sizeof(struct xfrm_algo) only counts the fixed part.\nA union containing variable-sized members is itself variable-sized. When a variable-sized field is not the last field in an outer struct, the compiler cannot compute where subsequent fields live. It needs to know the offset of buf, which is sizeof(u), but sizeof(u) is unknown at compile time.\nIn short: when you place variable-sized structs in a union, the union becomes variable-sized, and the compiler forbids placing variable-sized fields in non-terminal positions. Nothing can safely follow something whose size is unknown.\nSolution 1: Move the Union to the End One approach is to place union u at the end of struct alg. But as Simon explains here, the intention of char buf is to provide buffer space for the variable-length trailing field of the preceding structure. Moving u to the end breaks that design.\nSolution 2: TRAILING_OVERLAP Another solution is TRAILING_OVERLAP:\n/** * TRAILING_OVERLAP() - Overlap a flexible-array member with trailing members. * * @TYPE: Flexible structure type name, including \u0026#34;struct\u0026#34; keyword. * @NAME: Name for a variable to define. * @FAM: The flexible-array member within @TYPE * @MEMBERS: Trailing overlapping members. */ #define TRAILING_OVERLAP(TYPE, NAME, FAM, MEMBERS) \\ union { \\ TYPE NAME; \\ struct { \\ unsigned char __offset_to_FAM[offsetof(TYPE, FAM)];\\ MEMBERS \\ }; \\ } Consider struct asymmetric_key_id with unsigned char data[] as FAM:\nstruct asymmetric_key_id { unsigned short len; unsigned char data[] __counted_by(len); }; Naively embedding it triggers the same warning:\nstatic struct { struct asymmetric_key_id id; // \u0026lt;- variable-sized, not at end unsigned char data[10]; } cakey; The fix using TRAILING_OVERLAP:\nstatic struct { TRAILING_OVERLAP(struct asymmetric_key_id, id, data, unsigned char data[10]; ); } cakey; Which expands to:\nstatic struct { union { struct asymmetric_key_id id; struct { unsigned char __offset_to_FAM[offsetof(struct asymmetric_key_id, data)]; unsigned char data[10]; }; }; } cakey; Memory picture (offsetof(struct asymmetric_key_id, data) = 2):\nOffset 0 2 11 |-------- 2 bytes ---------|----------| id: [ len | len ][ data[] ] anon: [__offset_to_FAM][ data[10] ] Both id and the anonymous struct overlap. The padding array pushes data[10] to exactly the byte where id.data[] begins.\nSolution 3: Custom Fix for Multiple FAMs at Different Offsets TRAILING_OVERLAP works for a single FAM at a known offset. But our union has three FAMs at three different offsets. TRAILING_OVERLAP cannot handle that.\nHere is the patch merged into the Linux kernel:\nstatic int xfrm_state_pack_algo(...) { union { // \u0026lt;- outer union union { struct xfrm_algo alg; struct xfrm_algo_aead aead; struct xfrm_algo_auth auth; } u; struct { // \u0026lt;- anonymous overlay struct unsigned char __offset_to_FAM[offsetof(struct xfrm_algo_auth, alg_key)]; char buf[XFRM_ALGO_KEY_BUF_SIZE]; }; } alg = {}; ... } Step 1: Outer union instead of outer struct The key change is wrapping everything in an outer union instead of a struct. Both members of the outer union start at offset 0 and overlap completely.\nStep 2: Anonymous Overlay Struct The second member of the outer union is an anonymous struct:\nstruct { unsigned char __offset_to_FAM[offsetof(struct xfrm_algo_auth, alg_key)]; char buf[XFRM_ALGO_KEY_BUF_SIZE]; }; offsetof(struct xfrm_algo_auth, alg_key) computes the byte distance from the start of xfrm_algo_auth to its alg_key FAM. Since auth has the largest fixed header, this is the biggest offset among all three structs.\nThe padding array __offset_to_FAM pushes buf forward by exactly that many bytes from the start of the outer union.\nStep 3: buf Now Overlaps Correctly Here\u0026rsquo;s why this works. The three FAM offsets are:\noffsetof(struct xfrm_algo, alg_key) = 68 offsetof(struct xfrm_algo_aead, alg_key) = 72 ← auth has same header offsetof(struct xfrm_algo_auth, alg_key) = 72 ← largest, used for padding Memory diagram of the full layout:\nOffset 0 72 584 +---------------------------------------------------+---+----------+ | u.alg [ alg_name(64) | key_len ][ pad ] | alg_key[] | +---------------------------------------------------+---+----------+ | u.aead [ alg_name(64) | key_len | icv ] | alg_key[] | buf[512] +---------------------------------------------------+---+----------+ | u.auth [ alg_name(64) | key_len | trunc] | alg_key[] | +---------------------------------------------------+---+----------+ | anon [ __offset_to_FAM[72] ][ buf[512] | +---------------------------------------------------+---+----------+ ↑ All alg_key[] and buf meet here at offset 72 Since auth has the largest fixed header, buf correctly covers the key region for auth. For alg and aead, whose FAMs start earlier, the key bytes still fall inside buf because the code already knows which union member is active and reads from the correct offset within the buffer. The calling code handles partial overlaps correctly.\nStep 4: Adding offsetof When Missing The patch also adds a guard:\n#ifndef offsetof #define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) #endif __builtin_offsetof is a GCC/Clang built-in that computes the offset at compile time with zero runtime cost. This guard ensures portability across compilers that may not provide the standard offsetof macro.\n5. Key Takeaways Structs lay fields out sequentially with padding for alignment.\nUnions overlay all members at offset 0, sized to the largest member.\nFlexible array members must be the last field in a struct. This is a hard language rule enforced by the compiler.\nWhen you have a union of structs each carrying a FAM, the union is itself variable-sized. Placing that union in a non-terminal position inside an outer struct violates the standard.\noffsetof is a compile-time constant. You can use it as an array size to create precisely-sized padding. A portable, zero-cost way to express \u0026ldquo;start this field at exactly byte N.\u0026rdquo;\nThe anonymous struct inside an outer union is a classic low-level C pattern for creating an aliased view of memory at a specific offset without casts or undefined behaviour. When TRAILING_OVERLAP cannot handle mismatched FAM offsets, this manual approach is the right tool.\nThe final code no longer triggers the compiler warning because the outer union makes the variable-sized issue disappear: both union members are variable-sized, and the union itself is the last field. All pieces align perfectly.\n6. The Patch diff --git a/tools/testing/selftests/net/ipsec.c b/tools/testing/selftests/net/ipsec.c index 0ccf484b1d9d..f4afef51b930 100644 --- a/tools/testing/selftests/net/ipsec.c +++ b/tools/testing/selftests/net/ipsec.c @@ -43,6 +43,10 @@ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#ifndef offsetof +#define offsetof(TYPE, MEMBER)\t__builtin_offsetof(TYPE, MEMBER) +#endif + #define IPV4_STR_SZ\t16 #define MAX_PAYLOAD\t2048 #define XFRM_ALGO_KEY_BUF_SIZE\t512 @@ -827,13 +831,16 @@ static int xfrm_fill_key(char *name, char *buf, static int xfrm_state_pack_algo(struct nlmsghdr *nh, size_t req_sz, struct xfrm_desc *desc) { -\tstruct { +\tunion { union { struct xfrm_algo\talg; struct xfrm_algo_aead\taead; struct xfrm_algo_auth\tauth; } u; -\tchar buf[XFRM_ALGO_KEY_BUF_SIZE]; +\tstruct { +\tunsigned char __offset_to_FAM[offsetof(struct xfrm_algo_auth, alg_key)]; +\tchar buf[XFRM_ALGO_KEY_BUF_SIZE]; +\t}; } alg = {}; Reviewed-by: Simon Horman horms@kernel.org\nSigned-off-by: Ankit Khushwaha ankitkhushwaha.linux@gmail.com\nFurther Reading GCC offsetof documentation Linux kernel TRAILING_OVERLAP macro Original kernel discussion C11 Standard - Flexible Array Members ","permalink":"https://www.ankitkdev.com/blog/c-memory-layout/","summary":"\u003ch1 id=\"overview\"\u003eOverview\u003c/h1\u003e\n\u003cp\u003eC memory layout looks simple in isolation: structs store fields sequentially, unions overlap memory, and flexible array members allow variable-sized trailing storage. But combining these features can create subtle layout issues.\u003c/p\u003e\n\u003cp\u003eWhile working on a Linux kernel selftest, I encountered a case where a union contained multiple structures, each ending with a flexible array member at different offsets. Although the code was functionally correct, this confused the compiler into treating the union as effectively variable-sized, triggering a layout warning.\u003c/p\u003e","title":"C Memory Layout: Structs, Unions, and a Linux Kernel Patch"},{"content":"Overview U-Boot (Universal Boot Loader) is the go-to bootloader for embedded Linux systems. If you\u0026rsquo;re working with the BeagleBone Black (BBB), building U-Boot from source gives you full control over the boot process, from initializing hardware to loading your kernel. This guide walks you through cross-compiling and installing U-Boot on the BBB from scratch.\nThis blog will explain how to compile and install U-Boot, and then either manually boot from the U-Boot prompt or use uEnv.txt to autoboot the kernel. You can also use extlinux/extlinux.conf to boot the kernel.\nPrerequisites Before starting, make sure you have the following ready:\nA Linux host machine (Ubuntu 20.04 / 22.04 recommended) A BeagleBone Black board A microSD card (4 GB or more) A USB-to-serial cable (for console output via UART0 / J1 header) Internet connection SD Card Partition Layout Before touching the SD card, it helps to understand the partition structure this guide uses:\nPartition Device (Host) Device (BBB) Type Size Contents 1 /dev/sda1 /dev/mmcblk0p1 FAT32 64 MB MLO, u-boot.img, uEnv.txt 2 /dev/sda2 /dev/mmcblk0p2 ext4 512 MB (optional swap/data) 3 /dev/sda3 /dev/mmcblk0p3 ext4 Remaining Root filesystem (/) Note: On your host machine, the SD card appears as /dev/sdaX. On the BBB itself, the same SD card appears as /dev/mmcblkX. U-Boot refers to the SD card as mmc 0 and the onboard eMMC as mmc 1.\n1. Set Up the Environment On your host machine, install the required build tools:\nsudo apt update sudo apt install -y build-essential git bison flex libssl-dev \\ device-tree-compiler swig python3 python3-dev python3-setuptools \\ bc libncurses-dev Export the cross-compilation environment variables:\nexport ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- 2. Get U-Boot Source git clone https://source.denx.de/u-boot/u-boot.git cd u-boot Note: There is also a TI fork of U-Boot. Mainline U-Boot has sufficient support for BBB and is recommended here , the TI fork can lag behind mainline and adds complexity you likely don\u0026rsquo;t need.\nCheckout a stable release tag (recommended for production):\ngit checkout v2026.04 3. Configure for BBB make am335x_evm_defconfig Note: The BBB uses the TI AM335x SoC, so am335x_evm_defconfig is the correct config target. All available configs are located in the configs/ directory.\n4. Update Configuration Before building, you can customize U-Boot behavior using the configuration menu:\nmake menuconfig Navigate to:\nBoot options → Autoboot options → delay in seconds before autoboot Set the value to 5. This controls how long U-Boot waits before executing the default boot command (BOOTDELAY).\nAlternatively, verify or edit the config file directly:\ngrep CONFIG_BOOTDELAY .config # CONFIG_BOOTDELAY=5 5. Build U-Boot make -j$(nproc) After a successful build, you will find these files in the source directory:\nFile Description MLO First-stage bootloader (SPL) u-boot.img Main U-Boot image u-boot-dtb.img U-Boot with device tree blob 6. Prepare the microSD Card Identify your SD card device lsblk Example output:\nsda 8:0 1 29.7G 0 disk ├─sda1 8:1 1 64M 0 part ← FAT32 boot partition ├─sda2 8:2 1 512M 0 part ← ext4 (optional swap/data) └─sda3 8:3 1 29.2G 0 part ← ext4 root filesystem Warning: Replace sda with your actual device name. Double-check before proceeding , writing to the wrong device will destroy data.\nPartition the SD card If your SD card is not yet partitioned correctly, use fdisk:\nsudo fdisk /dev/sda Inside fdisk:\nPress o , create a new empty DOS partition table Press n , new partition 1, primary, start at sector 2048, size +64M Press t , change type to 0c (FAT32 LBA) Press a , mark partition 1 as bootable Press n , new partition 2, primary, size +512M Press n , new partition 3, primary, use remaining space Press w , write and exit Format the partitions sudo mkfs.vfat -F 32 -n \u0026#34;BOOT\u0026#34; /dev/sda1 sudo mkfs.ext4 -L \u0026#34;rootfs\u0026#34; /dev/sda3 7. Install U-Boot to the SD Card sudo mount /dev/sda1 /mnt sudo cp MLO /mnt/ sync sudo cp u-boot.img /mnt/ sync sudo umount /mnt Order matters! MLO must be copied before u-boot.img. Both files must be present on the FAT32 partition. The sync commands flush writes and help prevent memory corruption.\n8. Booting from SD Card (Forcing Boot Source) Insert the microSD card into the BBB Connect your USB-to-serial cable to the BBB\u0026rsquo;s J1 header (UART0): Pin 1 → GND Pin 4 → RX Pin 5 → TX Open a serial terminal on your host: sudo minicom -D /dev/ttyUSB0 -b 115200 To ensure the board boots from the SD card instead of the onboard eMMC, you need to override the default boot order.\nPress and hold the BOOT button (S2, located near the SD card slot), then apply power. Keep it pressed for a couple of seconds until the board starts booting, then release.\nYou should see U-Boot output on the serial console ending with:\n=\u0026gt; This =\u0026gt; prompt means U-Boot is running successfully.\n9. Manually Booting from the U-Boot Prompt First, interrupt autoboot when prompted:\nHit any key to stop autoboot Set load addresses Set these first, before any load commands:\nsetenv loadaddr 0x82000000 setenv fdtaddr 0x88000000 Important: The kernel and DTB addresses must not overlap in RAM. The values above are safe defaults for BBB.\nSelect the SD card mmc dev 0 This tells U-Boot to use the SD card (mmc 0). The onboard eMMC is mmc 1 on the BBB.\nLoad the kernel image The kernel lives in /boot on partition 3 (ext4 rootfs). Use ext4load for ext4 partitions:\n# ext4load mmc \u0026lt;dev\u0026gt;:\u0026lt;partition\u0026gt; \u0026lt;ram_addr\u0026gt; \u0026lt;file_path\u0026gt; # 0:3 → SD card (mmc 0), third partition (ext4 rootfs) ext4load mmc 0:3 ${loadaddr} /boot/vmlinuz-6.6.20 Note: Replace vmlinuz-6.6.20 with the actual kernel version present in /boot on your rootfs.\nLoad the device tree ext4load mmc 0:3 ${fdtaddr} /boot/dtbs/6.6.20/am335x-boneblack.dtb Note: Replace the DTB filename with the one matching your kernel version and board variant.\nSet boot arguments setenv bootargs console=ttyS0,115200n8 root=/dev/mmcblk0p3 rw rootfstype=ext4 rootwait mmcblk0p3 → SD card, 3rd partition (root filesystem) Adjust this if your rootfs is on a different partition Boot the kernel bootz ${loadaddr} - ${fdtaddr} Save variables for next boot (optional) saveenv Common Mistakes Mistake Result Using load instead of ext4load for ext4 partitions File not found / load failure Wrong partition number (0:1 vs 0:3) Boot files or rootfs not found Incorrect DTB path or filename Early boot failure or hang Wrong root device in bootargs (mmcblk0p2 vs mmcblk0p3) Kernel panic , cannot mount root Overlapping loadaddr and fdtaddr in RAM Kernel or DTB corruption 10. Autoboot Using uEnv.txt Instead of typing commands manually every time, configure U-Boot to autoboot using a uEnv.txt file.\nCreate uEnv.txt Place this file in the root of the FAT32 boot partition (/dev/sda1), not inside any subdirectory:\nsudo mount /dev/sda1 /mnt sudo nano /mnt/uEnv.txt Paste the following content:\nloadaddr=0x82000000 fdtaddr=0x88000000 bootpart=0:3 bootdir=/boot bootfile=vmlinuz-6.6.20 dtbdir=/boot/dtbs/6.6.20 dtbfile=am335x-boneblack.dtb mmcroot=/dev/mmcblk0p3 rw mmcrootfstype=ext4 rootwait console=ttyS0,115200n8 optargs=earlyprintk mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype} loadfdt=ext4load mmc ${bootpart} ${fdtaddr} ${dtbdir}/${dtbfile} loadimage=ext4load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile} uenvcmd=if run loadfdt; then if run loadimage; then run mmcargs; bootz ${loadaddr} - ${fdtaddr}; fi; fi; sync sudo umount /mnt Note: Update bootfile, dtbdir, and dtbfile to match the actual kernel and DTB versions on your rootfs.\nVerify U-Boot loads uEnv.txt Interrupt U-Boot and check the current boot command:\nprintenv bootcmd If uEnv.txt is not being imported automatically, set it manually and save:\nsetenv uenv_addr 0x81000000 setenv bootcmd \u0026#39;load mmc 0:1 ${uenv_addr} uEnv.txt; env import -t ${uenv_addr} ${filesize}; run uenvcmd\u0026#39; saveenv Note: uEnv.txt itself lives on the FAT32 partition (0:1), so plain load (FAT) is correct here. Only the kernel and DTB , which live on the ext4 rootfs partition (0:3) , need ext4load.\nMake sure to run saveenv to persist them to flash. Without this, your changes are lost on the next power cycle.\nBoot Flow Power ON └─→ ROM bootloader └─→ MLO (SPL) from FAT partition (mmc 0:1) └─→ u-boot.img └─→ load uEnv.txt from FAT (mmc 0:1) └─→ import variables └─→ run uenvcmd └─→ ext4load kernel + DTB from ext4 (mmc 0:3) └─→ bootz → Linux kernel U-Boot Auto Booting Using uEnv.txt Your browser does not support the video tag. Final Thoughts You now have a working U-Boot setup on your BeagleBone Black , from cross-compiling the source to autoboot the kernel via uEnv.txt. You can also use extlinux/extlinux.conf as an alternative to uEnv.txt\n","permalink":"https://www.ankitkdev.com/blog/configure-uboot-bbb/","summary":"\u003ch1 id=\"overview\"\u003eOverview\u003c/h1\u003e\n\u003cp\u003eU-Boot (Universal Boot Loader) is the go-to bootloader for embedded Linux systems. If you\u0026rsquo;re working with the BeagleBone Black (BBB), building U-Boot from source gives you full control over the boot process, from initializing hardware to loading your kernel. This guide walks you through cross-compiling and installing U-Boot on the BBB from scratch.\u003c/p\u003e\n\u003cp\u003eThis blog will explain how to compile and install U-Boot, and then either manually boot from the U-Boot prompt or use \u003cstrong\u003e\u003ccode\u003euEnv.txt\u003c/code\u003e\u003c/strong\u003e to autoboot the kernel.\nYou can also use \u003ccode\u003eextlinux/extlinux.conf\u003c/code\u003e to boot the kernel.\u003c/p\u003e","title":"Configure U-boot For Beaglebone Black"},{"content":"Overview The BeagleBone Black (BBB) uses an ARM Cortex-A8 32 bit processor, so the kernel must be cross-compiled on an x86 machine and then deployed to the board. This blog will assume that you already have a os[debian/ubuntu] installed on your sd card.\nWe’ll:\nset up toolchain fetch kernel source configure for BBB build kernel + modules deploy to SD card / board 1. Prerequisites Make sure you have:\nUbuntu (or any Linux host) Cross compiler for ARM (arm) Install required tools:\nsudo apt update sudo apt install gcc-arm-linux-gnueabihf build-essential bc bison flex libssl-dev u-boot-tools 2. Get Linux Kernel Source You can use mainline or TI’s kernel. For stability on BBB, We will use beagleboard/linux Kernel, since it already includes the board-specific patches and fixes.\ngit clone https://github.com/beagleboard/linux.git cd linux mkdir build Checkout a stable branch:\ngit checkout 6.6.20-ti-rt-arm32-r3 Note: Choose the kernel repository based on your board’s processor architecture. Since the BeagleBone Black uses a 32-bit ARM processor, we’ll use the corresponding ARM (armhf) kernel.\n3. Set Cross Compilation Environment export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- 4. Configure the default Board\u0026rsquo;s config file make bb.org_defconfig Optional: Customize configuration: make menuconfig\n5. Build the Kernel make O=build LOADADDR=0x82000000 -j$(nproc) | tee build.txt We use LOADADDR=0x82000000, the load address expected by U-Boot on the BeagleBone Black, so the kernel boots correctly.\nThis will Build the zImage, modules, and device tree blobs:\n6. Connect the sd card with host machine. check whether it mounted or not.\nlsblk sda 8:0 1 29.7G 0 disk ├─sda1 8:1 1 36M 0 part /run/media/ankit/BOOT ├─sda2 8:2 1 512M 0 part └─sda3 8:3 1 29.2G 0 part In our case sda3 has rootfs partition, mount it.\nsudo mkdir -p /mnt/rootfs sudo mount /dev/sda3 /mnt/rootfs 7. Install Modules make O=build INSTALL_MOD_PATH=/mnt/rootfs/ -j$(nproc) ➜ $ ls /mnt/rootfs/lib/modules/6.6.6 build modules.dep modules.weakdep config modules.dep.bin source kernel modules.devname symvers.xz modules.alias modules.drm System.map modules.alias.bin modules.modesetting systemtap modules.block modules.networking updates modules.builtin modules.order vdso modules.builtin.alias.bin modules.softdep vmlinuz modules.builtin.bin modules.symbols weak-updates modules.builtin.modinfo modules.symbols.bin Note: build is symlink that points to build directory of kernel source. copy that build/ dir inside it. If you want to compile module on BBB itself or maybe keep this as backup.\ncd /mnt/rootfs/lib/modules/6.6.6 rm build # dont add the /, it can delete the files in build/ dir. mkdir -p build sudo cp -r linux-src/build . 8. Copy the zImage and dtb blob files Get the Kernel release Version # make O=build/ kernelrelease make[1]: Entering directory '~/linux-src/build' 6.6.20 make[1]: Leaving directory '~/linux-src/build' Use this kernel Build verison while copying the zImage file.\nCopy the zImage file cd linux-src/build/arch/arm/boot/ sudo cp zImage /mnt/rootfs/boot/vmlinuz-6.6.20 Copy the dts file sudo mkdir -p /mnt/rootfs/boot/dtbs/6.6.20/ cd linux-src/build/arch/arm/boot/dts/ti/omap/ sudo cp * /mnt/rootfs/boot/dtbs/6.6.20/ 9. Build the Initramfs File Boot the BBB\nInstall dracut sudo apt install dracut sudo dracut -v -f --kver 6.6.20 /boot/initrd.img-6.6.20 this cmd will build Initramfs inside /boot.\n10. Update /boot/uEnv.txt file diff --git a/uEnv.txt b/uEnv.txt index b79d3c4..b88f904 100644 --- a/uEnv.txt +++ b/uEnv.txt @@ -1,6 +1,6 @@ #Docs: http://elinux.org/Beagleboard:U-boot_partitioning_layout_2.0 -uname_r=6.19.11-bone14 +uname_r=6.6.20 #uuid= #dtb= 11. Reboot the BBB $ sudo reboot $ uname -r 6.6.20 $ echo Voilà Voilà ","permalink":"https://www.ankitkdev.com/blog/build-linux-kernel-bbb/","summary":"\u003ch1 id=\"overview\"\u003eOverview\u003c/h1\u003e\n\u003cp\u003eThe BeagleBone Black (BBB) uses an ARM Cortex-A8 32 bit processor, so the kernel must be cross-compiled on an x86 machine and then deployed to the board. This blog will assume that you already have a os[debian/ubuntu] installed on your sd card.\u003c/p\u003e\n\u003cp\u003eWe’ll:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eset up toolchain\u003c/li\u003e\n\u003cli\u003efetch kernel source\u003c/li\u003e\n\u003cli\u003econfigure for BBB\u003c/li\u003e\n\u003cli\u003ebuild kernel + modules\u003c/li\u003e\n\u003cli\u003edeploy to SD card / board\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1-prerequisites\"\u003e1. Prerequisites\u003c/h2\u003e\n\u003cp\u003eMake sure you have:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUbuntu (or any Linux host)\u003c/li\u003e\n\u003cli\u003eCross compiler for ARM (arm)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eInstall required tools:\u003c/p\u003e","title":"Build and Install Linux Kernel For Beaglebone Black"},{"content":"Introduction Memory corruption can occur due to various bugs or defects: Uninitialized Memory Reads (UMR), Use After Free (UAF), Use After Return (UAR), double-free, memory leakage, or illegal Out Of Bounds (OOB) accesses that attempt to work upon (read/write/ execute) illegal memory regions.\nSince memory is dynamically allocated and freed via the kernel\u0026rsquo;s engine – the page allocator. This can lead to serious wastage (internal fragmentation) of memory. To mitigate this, the slab allocator (or slab cache) is layered upon it, serving two primary tasks – providing fragments of pages efficiently (within the kernel, allocation requests for small pieces of memory, from a few bytes to a couple of kilobytes), and serving as a cache for commonly used data structures.\nNote: memory is allocated in following hierarchy\nPage allocator → gives pages SLUB → splits pages into objects SLUB debug → adds metadata + checks This blog will explain to basics of debugging a slab memory corruption via SLUB debug.\nRequirements We will be using the code example from https://github.com/ankitkhushwaha/Linux-Kernel-Debugging-tutorials So make sure to clone it.\nEnable CONFIG_SLUB_DEBUG Following configs are needed to use this feature.\n$ grep SLUB_DEBUG /boot/config-6.18.7-200.fc43.x86_64 CONFIG_SLUB_DEBUG=y # CONFIG_SLUB_DEBUG_ON is not set This config implies that SLUB debugging is available but disabled by default (as CONFIG_ SLUB_DEBUG_ON is off). It is Usually disabled in production due to overhead; enable only for debugging.\nThe slub_debug Kernel Parameter To leverage SLUB debug features we need to boot the kernel with slub_debug parameter.\nFlag Description Null(empty after slub_debug=) Switch all Slub debugging On. F Sanity checks (consistency checks) on SLUB objects. Detects basic corruption of the freelist or slab structure. Z Red zoning around objects (guard zones). Detects out-of-bounds (OOB) writes into adjacent memory areas. P Poisoning (object and padding areas). Detects access to uninitialized memory or use-after-free (UAF) errors when reallocating an object. U User tracking (store user info for alloc/free). Provides stack traces of the last allocation and free operations in bug reports, aiding root cause analysis. T Trace all allocations/frees. Use only on single slabs due to high verbosity. Provides detailed, continuous tracing of memory operations for a specific cache. A Toggle failslab filter mark for the cache. Used for testing error handling by simulating allocation failures. O Switch debugging off for caches that would cause higher minimum slab orders. Prevents debugging from forcing large page allocations, which can cause memory allocation errors in low-memory situations. - Switch all debugging off (useful if the kernel is compiled with CONFIG_SLUB_DEBUG_ON). Disables all debugging for specified caches or globally. Note - Kernel also provides way to enable flags for Specific slub inside \u0026lsquo;/sys/kernel/slab/slabname\u0026rsquo; folder.\nUnderstanding the SLUB layer\u0026rsquo;s poison flags The poison flags defined by the kernel are defined as follows:\n// include/linux/poison.h /* ...and for poisoning */ #define POISON_INUSE 0x5a /* for use-uninitialised poisoning */ #define POISON_FREE 0x6b /* for use-after-free poisoning */ #define POISON_END 0xa5 /* end-byte of poisoning */ When you use the SLAB_POISON flag when creating a slab cache (typically via the kmem_cache_create() kernel API) or set poisoning to on via the kernel parameter slub_debug=P, the slab memory gets auto-initialized to the value 0x6b (which is ASCII k, corresponding to the POISON_FREE macro). In effect, when this flag is enabled, this (0x6b) is the value that valid but uninitialized slab memory regions are set to on creation. The POISON_INUSE value (0x5a equals ASCII Z) is used to denote padding zones, before or after red zones. The last legal byte of the slab memory object is set to POISON_END, 0xa5. Boot the Kernel Boot the kernel with slub_debug=FZPU\nAfter booting the kernel You should see something like this.\n$ cat /proc/cmdline BOOT_IMAGE=(hd0,gpt2)/vmlinuz-6.18.7-200.fc43.x86_64 root=UUID=94fc6fde-521c-4d20-9cba-84dba8146a75 ro rootflags=subvol=root slub_debug=FZPU quiet splash crashkernel=2G-64G:256M,64G-:512M Note: if kernel is build with KASAN support then it will catch the bug[discussed below] first. Try this in Production kernel built without KASAN support.\nReproduce the bug All test cases are defined in: ch5/kmembugs_test/kmembugs_test.c\n$ cd ch5/kmembugs_test $ sudo ./load_testmod # build \u0026amp; load the kernel module $ sudo ./run_tests # input the test number 5.2 [ 160.928464] testcase to run: 5.2 [ 160.928479] [Right Redzone overwritten] 0xffff8de137751680-0xffff8de137751683 @offset=1664. First byte 0x78 instead of 0xcc [ 160.928483] ============================================================================= [ 160.928485] BUG kmalloc-rnd-04-32 (Tainted: G OE ): Object corrupt [ 160.928487] ----------------------------------------------------------------------------- [ 160.928488] Allocated in dynamic_mem_oob_right+0x57/0xb0 [test_kmembugs] age=0 cpu=6 pid=7847 [ 160.928494] __kmalloc_cache_noprof+0x3ae/0x5c0 [ 160.928499] dynamic_mem_oob_right+0x57/0xb0 [test_kmembugs] [ 160.928501] dbgfs_run_testcase+0x2ee/0x3c0 [test_kmembugs] [ 160.928504] full_proxy_write+0x54/0x80 [ 160.928508] vfs_write+0xce/0x480 [ 160.928512] ksys_write+0x73/0xf0 [ 160.928514] do_syscall_64+0x7e/0x7f0 [ 160.928520] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 160.928524] Freed in rtw_iterate_vifs+0xa2/0xe0 [rtw88_core] age=2936 cpu=5 pid=1405 [ 160.928543] rtw_iterate_vifs+0xa2/0xe0 [rtw88_core] [ 160.928554] rtw_watch_dog_work+0x25a/0x320 [rtw88_core] [ 160.928562] process_one_work+0x192/0x350 [ 160.928566] worker_thread+0x25a/0x3a0 [ 160.928569] kthread+0xfc/0x240 [ 160.928571] ret_from_fork+0xf4/0x110 [ 160.928575] ret_from_fork_asm+0x1a/0x30 [ 160.928578] Slab 0xfffff6f104ddd440 objects=25 used=22 fp=0xffff8de137751de0 flags=0x17ffffc0000200(workingset|node=0|zone=2|lastcpupid=0x1fffff) [ 160.928583] Object 0xffff8de137751660 @offset=1632 fp=0xffff8de137751de0 [ 160.928585] Redzone ffff8de137751640: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc ................ [ 160.928587] Redzone ffff8de137751650: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc ................ [ 160.928588] Object ffff8de137751660: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk [ 160.928590] Object ffff8de137751670: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk. [ 160.928591] Redzone ffff8de137751680: 78 cc cc 78 cc cc cc cc x..x.... [ 160.928592] Padding ffff8de1377516d4: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZZZZZ [...] [ 160.928946] ---[ end trace 0000000000000000 ]--- [ 160.928948] FIX kmalloc-rnd-04-32: Restoring Right Redzone 0xffff8de137751680-0xffff8de137751683=0xcc [ 160.928950] FIX kmalloc-rnd-04-32: Object at 0xffff8de137751660 not freed Test case\nint dynamic_mem_oob_right(int mode) { volatile char *kptr, ch = 0; char *volatile ptr; size_t sz = 32; kptr = kmalloc(sz, GFP_KERNEL); if (unlikely(!kptr)) return -ENOMEM; ptr = (char *)kptr + sz + 3; // right OOB if (mode == READ) { ch = *(volatile char *)ptr; ch = kptr[sz+3]; } else if (mode == WRITE)\t{ *(volatile char *)ptr = \u0026#39;x\u0026#39;; // invalid, OOB right write kptr[sz] = \u0026#39;x\u0026#39;;\t// invalid, OOB right write } kfree((char *)kptr); return 0; } Test case does the following:\nperformed a dynamic memory allocation of 32 bytes memory. write a out of bound (OOB) region. First INFO line spits out the start and end of the corrupted memory region.\n[ 160.928479] [Right Redzone overwritten] 0xffff8de137751680-0xffff8de137751683 @offset=1664. First byte 0x78 instead of 0xcc Note that these kernel virtual addresses are hashed here, for security, preventing info leaks.\nSecond INFO line shows where the buggy access took place in the code – via the usual +0xoff_from_func/0xlen_of_func [modname] notation. (Here, it happens to be dynamic_mem_oob_right+0x57/0xb0.\nWe haven\u0026rsquo;t shown the full stack call trace here. Read it bottom-up, ignoring any lines that begin with \u0026lsquo;?\u0026rsquo;.\nWe have allocated a memory of 32 bytes shown by\n[ 160.928588] Object ffff8de137751660: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk [ 160.928590] Object ffff8de137751670: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk. The poison value 0x6b denotes the value that\u0026rsquo;s used to initialize the valid slab memory region, 0xa5 denotes the end poisoning marker byte, and 0x5a denotes use-uninitialized poisoning.\nThe last byte was initialized with POISON_END bit (0xa5) and the value 0x78 is our x character being (wrongly) written by the test case outside of the allocated memory.\n[ 160.928591] Redzone ffff8de137751680: 78 cc cc 78 cc cc cc cc x..x.... This tell us that Right redzone of concerned memory was being overwritten with value \u0026lsquo;0x78\u0026rsquo; in ascii \u0026lsquo;x\u0026rsquo;. In last Lines of logs We can see that Slab framework corrected the memory by restoring it to previous POISON_FLAG.\nConclusion while the kernel SLUB debug framework seems to catch most of the memory corruption issues on slab memory, it doesn\u0026rsquo;t seem to catch the read OOB accesses on slab memory. Though it is reliable tool to detect memory corruption in production kernels.\nIt can catch the following bugs- References https://www.kernel.org/doc/Documentation/vvm/slub.txt https://www.oreilly.com/library/view/linux-kernel-debugging/9781801075039/ https://blogs.oracle.com/linux/linux-slub-allocator-internals-and-debugging-2 ","permalink":"https://www.ankitkdev.com/blog/debug-slub-memory/","summary":"\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eMemory corruption can occur due to various bugs or defects: Uninitialized Memory\nReads (UMR), Use After Free (UAF), Use After Return (UAR), double-free, memory\nleakage, or illegal Out Of Bounds (OOB) accesses that attempt to work upon (read/write/\nexecute) illegal memory regions.\u003c/p\u003e\n\u003cp\u003eSince memory is dynamically allocated and freed via the kernel\u0026rsquo;s engine – the\npage allocator. This can lead to serious wastage (internal fragmentation) of memory.\nTo mitigate this, the slab allocator (or slab cache) is layered upon it, serving two\nprimary tasks – providing fragments of pages efficiently (within the kernel, allocation\nrequests for small pieces of memory, from a few bytes to a couple of kilobytes), and serving as a cache for commonly used data structures.\u003c/p\u003e","title":"Basics of debugging slab memory corruption via SLUB debug"},{"content":"Introduction I have been using Linux for the last two years and gradually developed an interest in systems programming and C. The mentorship prerequisites includes tasks like building and booting the Linux kernel, writing a basic kernel module, decoding stack traces, and modifying and booting a custom kernel build.\nSomehow, my application was accepted ;-). At first, the Linux Kernel seemed quite difficult to understand what was going on. We were initially told to choose two subsystems to work on.\nI initially attempted to work on syzbot-reported bug in the trace subsystem and submitted a patch for the kernel ring buffer. However, I later realized that starting directly with complex bugs without understanding subsystem internals slows progress.\nAnd I personally advise not to do this.\nI instead focused on beginner-friendly tasks such as fixing warnings in kselftest, addressing build issues with W=1, and backporting small fixes. These helped me understand the workflow and codebase more effectively. Backporting can also be considered as beginner task \u0026ndash; see Hanne-Lotta\u0026rsquo;s Blog - link.\nWhen submitting patches, it is important to clearly explain:\nwhat the patch changes why the change is needed how it fixes the issue The Patch Submission Flow Create the Patch: git format-patch HEAD~1 Check style: (Run the check and then apply fixes if needed)\n./scripts/checkpatch.pl --strict --fix-inplace hello.patch ./scripts/checkpatch.pl --strict --fix-inplace -f --fix hello.patch Find maintainers: ./scripts/get_maintainer.pl --separator=, --no-rolestats hello.patch Sending the patch git send-email --to=\u0026lt;Maintainer Email\u0026gt; --cc=\u0026lt;Mailing list\u0026gt; hello.patch (Note: Replace \u0026lt;Maintainer Email\u0026gt; and \u0026lt;CC Emails\u0026gt; with the actual emails obtained from get_maintainer.pl.)\nNote: Always make sure to double check the patch before sending it to the mailing list. I will recommend first sending the patch to your email or use git send-email using the --dry-run option.\nWriting a Changelog for Previous Versions:\nAlways write the changelog in patches except for the first version. see: https://lore.kernel.org/all/20251106095532.15185-1-ankitkhushwaha.linux@gmail.com/\nSubsystem-specific rules Each subsystem have different preference. for example: Networking subsystem: netdev\nFor the kselftest subsystem, it is helpful to mention the compiler details with which you are getting the error/warning. Installing the uapi header before compiling the test.\nmake headers_install see: discussion\nContributions During the mentorship total of 8 patch was accepted in mainline kernel.\n2fa98059fd5a0936d0951bd14f8990ae0aa5272a selftests: mptcp: Mark xerror and die_perror __noreturn 9580f6d47dd6156c6d16e988d28faa74e5a0b8ba selftests: tls: fix warning of uninitialized variable 0384c8ea96bfe49e82e624e53bfd5f80c3230ea9 selftests/mm/uffd: initialize char variable to Null af7273cc7ae01f5b3e34e62f59588ce79fe50f79 selftests/net: initialize char variable to null 3b12a53b64d0c86cf68cab772bd4137e451b17a5 selftest/mm: fix pointer comparison in mremap_test 6ae0f2072768fb3db7846cee08b611a96310930d docs: parse-headers.rst: Fix a typo 216158f063fe24fb003bd7da0cd92cd6e2c4d48b selftests/user_events: fix type cast for write_index packed member in perf_test afb8f6567a5b4bb4e673608048939fef854b8709 selftest: net: fix socklen_t type mismatch in sctp_collision test de4cbd704731778a2dc833ce5a24b38e5d672c05 ring buffer: Propagate __rb_map_vma return value to caller Tools Cscope:\ncd linux/ # Generate Cscope Database: make cscope -j8 # This will generate files like ls csocpe* cscope.files cscope.out cscope.out.in cscope.out.po # Enter Cscope Interactive Mode cscope -d This significantly improves symbol lookup compared to standard editor indexing.\nClosing Thoughts\nThe learning was very rewarding, especially the Office Hours meetings and Discord discussions. I learned that kernel development and becoming a kernel hacker isn\u0026rsquo;t an easy task that you can just pick up quickly. It requires consistent, dedicated effort. We need to acquire a deeper understanding of systems programming, architecture, and a willingness to troubleshoot complex issues.\nIt\u0026rsquo;s certainly a long but ultimately rewarding journey. I also learned about the development cycle of the Linux kernel. This program was a strong beginning, and the work will continue.\nI will recommend this mentorship program to anyone interested in Linux kernel development. You\u0026rsquo;ll learn the basics and find out if kernel development interests you in the long term. What I find most valuable is that you get a feel for the supportive open source community.\nI am very thankful to Shuah, David and Khalid for this wonderful mentorship program.\n","permalink":"https://www.ankitkdev.com/blog/linux-kernel-mentorship-program/","summary":"\u003ch3 id=\"introduction\"\u003eIntroduction\u003c/h3\u003e\n\u003cp\u003eI have been using Linux for the last two years and gradually developed an interest in systems programming and C. The mentorship prerequisites includes tasks like building and booting the Linux kernel, writing a basic kernel module, decoding stack traces, and modifying and booting a custom kernel build.\u003c/p\u003e\n\u003cp\u003eSomehow, my application was accepted ;-).\n\u003cimg alt=\"alt text\" loading=\"lazy\" src=\"/lfx-acceptance.png\"\u003e\u003c/p\u003e\n\u003cp\u003eAt first, the Linux Kernel seemed quite difficult to understand what was going on. We were initially told to choose two subsystems to work on.\u003c/p\u003e","title":"Linux Kernel Mentorship Program"},{"content":"Introduction I have been using Linux for the last two years and gradually developed an interest in systems programming and C. The mentorship prerequisites includes tasks like building and booting the Linux kernel, writing a basic kernel module, decoding stack traces, and modifying and booting a custom kernel build.\nSomehow, my application was accepted ;-). At first, the Linux Kernel seemed quite difficult to understand what was going on. We were initially told to choose two subsystems to work on.\nI initially attempted to work on syzbot-reported bug in the trace subsystem and submitted a patch for the kernel ring buffer. However, I later realized that starting directly with complex bugs without understanding subsystem internals slows progress.\nAnd I personally advise not to do this.\nI instead focused on beginner-friendly tasks such as fixing warnings in kselftest, addressing build issues with W=1, and backporting small fixes. These helped me understand the workflow and codebase more effectively. Backporting can also be considered as beginner task \u0026ndash; see Hanne-Lotta\u0026rsquo;s Blog - link.\nWhen submitting patches, it is important to clearly explain:\nwhat the patch changes why the change is needed how it fixes the issue The Patch Submission Flow Create the Patch: git format-patch HEAD~1 Check style: (Run the check and then apply fixes if needed)\n./scripts/checkpatch.pl --strict --fix-inplace hello.patch ./scripts/checkpatch.pl --strict --fix-inplace -f --fix hello.patch Find maintainers: ./scripts/get_maintainer.pl --separator=, --no-rolestats hello.patch Sending the patch git send-email --to=\u0026lt;Maintainer Email\u0026gt; --cc=\u0026lt;Mailing list\u0026gt; hello.patch (Note: Replace \u0026lt;Maintainer Email\u0026gt; and \u0026lt;CC Emails\u0026gt; with the actual emails obtained from get_maintainer.pl.)\nNote: Always make sure to double check the patch before sending it to the mailing list. I will recommend first sending the patch to your email or use git send-email using the --dry-run option.\nWriting a Changelog for Previous Versions:\nAlways write the changelog in patches except for the first version. see: https://lore.kernel.org/all/20251106095532.15185-1-ankitkhushwaha.linux@gmail.com/\nSubsystem-specific rules Each subsystem have different preference. for example: Networking subsystem: netdev\nFor the kselftest subsystem, it is helpful to mention the compiler details with which you are getting the error/warning. Installing the uapi header before compiling the test.\nmake headers_install see: discussion\nContributions During the mentorship total of 8 patch was accepted in mainline kernel.\n2fa98059fd5a0936d0951bd14f8990ae0aa5272a selftests: mptcp: Mark xerror and die_perror __noreturn 9580f6d47dd6156c6d16e988d28faa74e5a0b8ba selftests: tls: fix warning of uninitialized variable 0384c8ea96bfe49e82e624e53bfd5f80c3230ea9 selftests/mm/uffd: initialize char variable to Null af7273cc7ae01f5b3e34e62f59588ce79fe50f79 selftests/net: initialize char variable to null 3b12a53b64d0c86cf68cab772bd4137e451b17a5 selftest/mm: fix pointer comparison in mremap_test 6ae0f2072768fb3db7846cee08b611a96310930d docs: parse-headers.rst: Fix a typo 216158f063fe24fb003bd7da0cd92cd6e2c4d48b selftests/user_events: fix type cast for write_index packed member in perf_test afb8f6567a5b4bb4e673608048939fef854b8709 selftest: net: fix socklen_t type mismatch in sctp_collision test de4cbd704731778a2dc833ce5a24b38e5d672c05 ring buffer: Propagate __rb_map_vma return value to caller Tools Cscope:\ncd linux/ # Generate Cscope Database: make cscope -j8 # This will generate files like ls csocpe* cscope.files cscope.out cscope.out.in cscope.out.po # Enter Cscope Interactive Mode cscope -d This significantly improves symbol lookup compared to standard editor indexing.\nClosing Thoughts\nThe learning was very rewarding, especially the Office Hours meetings and Discord discussions. I learned that kernel development and becoming a kernel hacker isn\u0026rsquo;t an easy task that you can just pick up quickly. It requires consistent, dedicated effort. We need to acquire a deeper understanding of systems programming, architecture, and a willingness to troubleshoot complex issues.\nIt\u0026rsquo;s certainly a long but ultimately rewarding journey. I also learned about the development cycle of the Linux kernel. This program was a strong beginning, and the work will continue.\nI will recommend this mentorship program to anyone interested in Linux kernel development. You\u0026rsquo;ll learn the basics and find out if kernel development interests you in the long term. What I find most valuable is that you get a feel for the supportive open source community.\nI am very thankful to Shuah, David and Khalid for this wonderful mentorship program.\n","permalink":"https://www.ankitkdev.com/linux/linux-kernel-mentorship-program/","summary":"\u003ch3 id=\"introduction\"\u003eIntroduction\u003c/h3\u003e\n\u003cp\u003eI have been using Linux for the last two years and gradually developed an interest in systems programming and C. The mentorship prerequisites includes tasks like building and booting the Linux kernel, writing a basic kernel module, decoding stack traces, and modifying and booting a custom kernel build.\u003c/p\u003e\n\u003cp\u003eSomehow, my application was accepted ;-).\n\u003cimg alt=\"alt text\" loading=\"lazy\" src=\"/lfx-acceptance.png\"\u003e\u003c/p\u003e\n\u003cp\u003eAt first, the Linux Kernel seemed quite difficult to understand what was going on. We were initially told to choose two subsystems to work on.\u003c/p\u003e","title":"Linux Kernel Mentorship Program"},{"content":"Installation of ubuntu server on qemu-x86_64 At First I want to run the custom modules on linux. But this is something that is not good to do on host machine. So i searched a lot of stuff. Tinkering with buildroot, after some days i was able to run the build on qemu.\nBut i found out build actually didn’t have my basic tool like gnu make. Everytime i try to do something i need to find particular tool on menuconfig then make the build again. Learning was so slow.\nThen thankfully i found this article. Which explains to install the arch linux on qemu. I followed it booted the custom kernel in image. But i was not aware of updating the grub config. So i got kernel mismatch issues. I tried to install the arch linux on GNOME Boxes.\nEventually i decided to leave the arch linux for now -\u0026gt; package dependency in arch breaks easily :).\nNote : Most of cmds used are well explained in article given above. Check out that too!\nDownload the iso file You can download it from here.\nCreate qemu image qemu-img create -f raw \u0026lt;MY_IMAGE\u0026gt; 10G example: kush.img, myimage.qcow2\nthe creates a image file for the qemu.\nThis cmd create the image file with 10 Gb. You can choose according to your requirement.\nWorth mentioning is the alternative qcow2 image format. While slower than a raw formatted image, the qcow2 image size increases during VM usage. You set a limit in gigabytes on the size of the qcow2 image during creation.\nInstallting the Distro qemu-system-x86_64 \\ -enable-kvm \\ -cdrom \u0026lt;ubuntu_server_ISO\u0026gt; \\ -boot order=d \\ -drive file=\u0026lt;MY_IMAGE\u0026gt;,format=raw \\ -m 4G Do not give space between file=\u0026lt;MY_IMAGE\u0026gt;,format=raw. Otherwise it will fail.\nOption Description -enable-kvm Enable hypervisor support using the Linux KVM. -cdrom Points to what image will be inserted into the emulated CD slot. -boot Tells QEMU to boot from CD-ROM. -drive Specifies a drive on the system (e.g., image file and its format). -m Sets the amount of RAM allocated to the VM (the more, the better). After complete installtion it asks for reboot and remove the bootable medium -\u0026gt; you can safely kill the process.\nRunning the ubuntu-server qemu-system-x86_64 \\ -enable-kvm \\ -drive file=kush.img,format=raw,if=virtio \\ -m 4G \\ -nic user,hostfwd=tcp::2222-:22 \\ -serial stdio ","permalink":"https://www.ankitkdev.com/blog/install-ubuntu-server-on-qemu/","summary":"\u003ch1 id=\"installation-of-ubuntu-server-on-qemu-x86_64\"\u003eInstallation of ubuntu server on qemu-x86_64\u003c/h1\u003e\n\u003cp\u003eAt First I want to run the custom modules on linux. But this is something that is not good to do on host machine. So i searched a lot of stuff. Tinkering with buildroot, after some days i was able to run the build on qemu.\u003c/p\u003e\n\u003cp\u003eBut i found out build actually didn’t have my basic tool like \u003ccode\u003egnu make\u003c/code\u003e. Everytime i try to do something i need to find particular tool on \u003ccode\u003emenuconfig\u003c/code\u003e then make the build again. Learning was so slow.\u003c/p\u003e","title":"Install Ubuntu Server on Qemu"}]