<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fauzan Febriansyah</title>
    <description>The latest articles on DEV Community by Fauzan Febriansyah (@fauzanfebrian).</description>
    <link>https://dev.to/fauzanfebrian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1160753%2F7c30035f-ede5-4486-9d07-d7792f44efd1.png</url>
      <title>DEV Community: Fauzan Febriansyah</title>
      <link>https://dev.to/fauzanfebrian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fauzanfebrian"/>
    <language>en</language>
    <item>
      <title>Fix ASIX AX88179 Ethernet Crash Loop on Linux When Using a UGREEN USB-C Hub</title>
      <dc:creator>Fauzan Febriansyah</dc:creator>
      <pubDate>Sun, 29 Mar 2026 13:34:55 +0000</pubDate>
      <link>https://dev.to/fauzanfebrian/fix-asix-ax88179-ethernet-crash-loop-on-linux-when-using-a-ugreen-usb-c-hub-22am</link>
      <guid>https://dev.to/fauzanfebrian/fix-asix-ax88179-ethernet-crash-loop-on-linux-when-using-a-ugreen-usb-c-hub-22am</guid>
      <description>&lt;p&gt;You buy a new USB-C hub. You plug it into a Windows partition, and the Gigabit Ethernet adapter lights up instantly via plug-and-play magic. You reboot into Linux, and the interface is dead. &lt;code&gt;Wired cable unplugged&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are running the &lt;strong&gt;UGREEN UNO 7-in-1 Hub (Model 45155)&lt;/strong&gt; featuring the &lt;strong&gt;ASIX AX88179 chipset&lt;/strong&gt; on a modern kernel (6.1+ on &lt;strong&gt;Ubuntu 24.04&lt;/strong&gt; or &lt;strong&gt;Linux Mint 22&lt;/strong&gt;), standard user-space debugging is a waste of time. Restarting Network Manager or disabling USB autosuspend won't save you.&lt;/p&gt;

&lt;p&gt;This isn't a configuration typo. It is a fundamental architectural conflict between a proprietary hardware gimmick and strict Linux kernel module dependencies. Here is the pointer arithmetic behind the USB-C hub crash loop, and how to rewrite the rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 The Root Cause: Why Your USB Ethernet Adapter is Dead on Linux
&lt;/h2&gt;

&lt;p&gt;USB network adapters are composite devices, and this specific UGREEN hub exposes exactly how fragile Linux's handling of these configurations can be. Bridging the gap between "broken" and "functional" requires looking at how the drivers interact with the metal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Generic Blindness (&lt;code&gt;cdc_ncm&lt;/code&gt;):&lt;/strong&gt; If you let the system default to the generic Linux standard driver, it connects without throwing kernel panics. But the connection is a ghost. Run &lt;code&gt;ethtool&lt;/code&gt; and you get &lt;code&gt;Speed: Unknown!&lt;/code&gt;. The generic driver assumes the hardware is smart enough to power on its own Physical Layer (PHY). It isn't. ASIX chips are built cheap; they sit in the dark, waiting for the OS to send a proprietary wake-up command. The generic driver doesn't know the command, so the hardware stays asleep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Interrupt Storm (&lt;code&gt;ax88179_178a&lt;/code&gt;):&lt;/strong&gt; Force the native Linux driver, and the kernel immediately spits out &lt;code&gt;-110&lt;/code&gt; (Timeout) and &lt;code&gt;-32&lt;/code&gt; (Broken Pipe) errors, entering an infinite Ethernet crash loop. Here is the punchline: The UGREEN UNO features a "Smart LED Emoji Face." That LCD is driven by an internal microcontroller that constantly spams the USB bus with interrupt polling requests to sync its animations. When the native Linux driver tries to execute a simple read of the Ethernet chip's MAC address, it gets drowned in this hidden hardware interrupt storm. The driver times out, panics, forcefully drops the connection, and the cycle repeats.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ The Fix: Compiling the Official ASIX Linux Driver via DKMS
&lt;/h2&gt;

&lt;p&gt;To actually fix this Gigabit Ethernet adapter issue, we have to abandon the broken in-tree modules. We force the hardware into its native configuration, compile the official ASIX driver from source, and cryptographically sign it to bypass Secure Boot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Automating the Fetch
&lt;/h3&gt;

&lt;p&gt;Install the compilation toolchain, bypass the web portal, and drop the source code into a staging directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the required Linux headers and compilation toolchain&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; dkms build-essential linux-headers-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; git curl wget

&lt;span class="c"&gt;# Pull the latest official ASIX AX88179 Linux driver and extract it&lt;/span&gt;
curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://www.asix.com.tw/en/product/USBEthernet/Super-Speed_USB_Ethernet/AX88179A | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s1"&gt;'Linux kernel'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'data-href="\K[^"]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 | xargs &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; wget &lt;span class="nt"&gt;-O&lt;/span&gt; /tmp/ax88179a_linux.tar.bz2 &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;# Extract into a properly named staging directory&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /usr/src/ax_usb_nic-4.0.0
&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; /tmp/ax88179a_linux.tar.bz2 &lt;span class="nt"&gt;-C&lt;/span&gt; /usr/src/ax_usb_nic-4.0.0 &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Arbitration and Blacklists
&lt;/h3&gt;

&lt;p&gt;We need udev rules to block the generic driver, and blacklists to suppress the broken in-tree driver. The ASIX Makefile handles this device arbitration for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/src/ax_usb_nic-4.0.0

&lt;span class="c"&gt;# Install ASIX udev rules and hardware blacklists&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;make udev_install
&lt;span class="nb"&gt;sudo &lt;/span&gt;make blacklist_install

&lt;span class="c"&gt;# Purge the conflicting network modules from active memory&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe &lt;span class="nt"&gt;-r&lt;/span&gt; ax88179_178a cdc_ncm cdc_ether cdc_mbim

&lt;span class="c"&gt;# Rebuild the boot image so the kernel respects the blacklist on the next boot&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-initramfs &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Compiling via DKMS (Dynamic Kernel Module Support)
&lt;/h3&gt;

&lt;p&gt;Running a standard &lt;code&gt;make install&lt;/code&gt; is how you break your internet connection the next time &lt;code&gt;apt&lt;/code&gt; updates your Ubuntu kernel. We use DKMS so the custom driver recompiles itself automatically during system updates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Patch the Makefile for DKMS compatibility&lt;/span&gt;
&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/TARGET = ax88179_178a/TARGET = ax_usb_nic/g'&lt;/span&gt; /usr/src/ax_usb_nic-4.0.0/Makefile

&lt;span class="c"&gt;# Generate the DKMS config file&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'cat &amp;gt; /usr/src/ax_usb_nic-4.0.0/dkms.conf &amp;lt;&amp;lt;EOF
PACKAGE_NAME="ax_usb_nic"
PACKAGE_VERSION="4.0.0"
CLEAN="make clean"
MAKE="make KERNELRELEASE=\$kernelver"
BUILT_MODULE_NAME="ax_usb_nic"
DEST_MODULE_LOCATION="/updates/dkms"
AUTOINSTALL="yes"
EOF'&lt;/span&gt;

&lt;span class="c"&gt;# Build and install the ASIX driver&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dkms add &lt;span class="nt"&gt;-m&lt;/span&gt; ax_usb_nic &lt;span class="nt"&gt;-v&lt;/span&gt; 4.0.0
&lt;span class="nb"&gt;sudo &lt;/span&gt;dkms build &lt;span class="nt"&gt;-m&lt;/span&gt; ax_usb_nic &lt;span class="nt"&gt;-v&lt;/span&gt; 4.0.0
&lt;span class="nb"&gt;sudo &lt;/span&gt;dkms &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; ax_usb_nic &lt;span class="nt"&gt;-v&lt;/span&gt; 4.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Bypassing the Secure Boot Bouncer
&lt;/h3&gt;

&lt;p&gt;If Secure Boot is enforcing signatures in your BIOS, the kernel will block your custom driver (&lt;code&gt;Key was rejected by service&lt;/code&gt;). Because we used DKMS, the system generated a Machine Owner Key (MOK). We need to inject that cryptographic signature into the newly compiled binary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sign the new module with the DKMS key&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;kmodsign sha512 /var/lib/shim-signed/mok/MOK.priv /var/lib/shim-signed/mok/MOK.der &lt;span class="si"&gt;$(&lt;/span&gt;modinfo &lt;span class="nt"&gt;-n&lt;/span&gt; ax_usb_nic&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Force the Linux kernel to load the signed driver&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe ax_usb_nic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📝 System Status
&lt;/h2&gt;

&lt;p&gt;Unplug the UGREEN hub. Wait five seconds for the hardware capacitors to drain. Plug it back in.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;ethtool enx...&lt;/code&gt; using your specific predictable interface name. The interface powers up the PHY cleanly, bypassing the LCD interrupt storm. You will see &lt;code&gt;Speed: 1000Mb/s&lt;/code&gt; alongside &lt;code&gt;Link detected: yes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No magic code. Just a resilient networking stack engineered to survive the next Linux kernel update.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>kernel</category>
      <category>networking</category>
    </item>
    <item>
      <title>From npm install to insmod: My Journey into Linux Kernel Module</title>
      <dc:creator>Fauzan Febriansyah</dc:creator>
      <pubDate>Tue, 23 Dec 2025 04:38:31 +0000</pubDate>
      <link>https://dev.to/fauzanfebrian/from-npm-install-to-insmod-my-journey-into-linux-kernel-module-2lm3</link>
      <guid>https://dev.to/fauzanfebrian/from-npm-install-to-insmod-my-journey-into-linux-kernel-module-2lm3</guid>
      <description>&lt;p&gt;For a long time, I treated the OS as a black box. I knew how to write APIs in NestJS, but I had no idea how the server actually listened to a port. That changed when I joined the &lt;a href="https://instagram.com/cusdeb_com" rel="noopener noreferrer"&gt;CusDeb Operating System Camp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The goal was simple: stop relying on high-level abstractions and understand the machine. I wanted to move from just being a user of the OS to an engineer who could build inside it.&lt;/p&gt;

&lt;p&gt;Here is what I learned when I finally broke the "Black Box" and accepted a challenge from an AI to build a self-destructing kernel module.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Kernel is just an Express.js App
&lt;/h2&gt;

&lt;p&gt;When I first looked at a Character Device Driver, I was overwhelmed by structs and pointers. But then I realized: The Kernel is just a Server.&lt;/p&gt;

&lt;p&gt;Coming from web development, I mapped the concepts, and suddenly, the "magic" disappeared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Port&lt;/strong&gt; (&lt;code&gt;alloc_chrdev_region&lt;/code&gt;): You reserve a Major/Minor number. This is just like opening port 80. The Kernel needs to know where to route traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Server&lt;/strong&gt; (&lt;code&gt;cdev_init&lt;/code&gt;): This is effectively app.listen(). It tells the Kernel to start accepting traffic for your specific Device ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Controller&lt;/strong&gt; (&lt;code&gt;file_operations&lt;/code&gt;): This is where your business logic lives. When a user runs cat /dev/mydevice, the Kernel routes it to your &lt;code&gt;.read&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Response&lt;/strong&gt; (&lt;code&gt;copy_to_user&lt;/code&gt;): You can't just return json. You have to manually copy bytes across the boundary to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I treated the Kernel as an API, I stopped fearing the C code.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Challenger: &lt;em&gt;Gemini 3.0&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;The CusDeb Camp gave me the foundation, how to build the kernel and run User-Mode Linux (UML). But I didn't want to just copy-paste a "Hello World."&lt;/p&gt;

&lt;p&gt;I asked my AI thought partner, &lt;strong&gt;Gemini 3.0&lt;/strong&gt;, for a "Boss Fight." I told it: "Don't give me code. Give me a problem."&lt;/p&gt;

&lt;p&gt;It gave me &lt;a href="https://github.com/fauzanfebrian/linux-kernel-hacking/blob/main/docs/challenges/create-voidbox-module.md" rel="noopener noreferrer"&gt;The VoidBox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Spec:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read-Once: A secure drop-box. Users write secrets in.&lt;/li&gt;
&lt;li&gt;Incinerate: When read, the data is returned to User Space and immediately zeroed out in Kernel Memory.&lt;/li&gt;
&lt;li&gt;Thread-Safe: Use &lt;code&gt;mutex&lt;/code&gt; to prevent race conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn't a tutorial. This was a specification sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Holy Trinity of Kernel Hacking
&lt;/h2&gt;

&lt;p&gt;Building the VoidBox forced me to confront three things I usually ignore in TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Boundary (User vs. Kernel)
&lt;/h3&gt;

&lt;p&gt;In JS, variables are just variables. In Kernel space, you never trust a pointer from User Space.&lt;/p&gt;

&lt;p&gt;You cannot access the user's buffer directly. You must use &lt;code&gt;copy_from_user&lt;/code&gt; and &lt;code&gt;copy_to_user&lt;/code&gt;. If you try to dereference a user pointer directly, you might crash the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrency is Manual
&lt;/h3&gt;

&lt;p&gt;In Node.js, we are spoiled by the single-threaded Event Loop. We rarely worry about two requests modifying a variable at the exact same nanosecond.&lt;/p&gt;

&lt;p&gt;In the Kernel, that protection is gone. If two processes try to write to &lt;code&gt;/dev/voidbox&lt;/code&gt; simultaneously, they will corrupt the memory.&lt;/p&gt;

&lt;p&gt;I had to implement a mutex lock.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;mutex_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;void_device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... Critical Section ...&lt;/span&gt;
&lt;span class="c1"&gt;// Copy data, update length&lt;/span&gt;
&lt;span class="n"&gt;mutex_unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;void_device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I didn't lock it, the memory will be corrupted.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Filesystem" is a Lie
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/dev/voidbox&lt;/code&gt; looks like a file. You can &lt;code&gt;echo&lt;/code&gt; into it. You can &lt;code&gt;cat&lt;/code&gt; it. But it's not a file. It's a stream.&lt;/p&gt;

&lt;p&gt;I learned the hard way that if you don't manually update the cursor position (&lt;code&gt;*ppos&lt;/code&gt;), the &lt;code&gt;cat&lt;/code&gt; command will read forever, looping your data infinitely until you kill the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Logic: Self-Destructing Memory
&lt;/h2&gt;

&lt;p&gt;The logic I wrote was ruthless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Write&lt;/strong&gt;: I checked if the box was full. If it was, I returned -EBUSY. I threw an I/O Error on purpose because this is a secure box. No overwriting allowed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Read&lt;/strong&gt;: I first checked whether any data existed. If it did, I copied the data to user space, then immediately incinerated it &lt;code&gt;void_device.data[0] = '\0';&lt;/code&gt; and reset the stored length to 0, enforcing strict read-once semantics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n67rgazasy571gvb3sk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n67rgazasy571gvb3sk.png" alt="VoidBox Demo" width="596" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above shows the result. I wrote "NUS Msc..." into the void. I read it once. I tried to read it again, and it was gone. Total erasure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am still a "wannabe" Systems Engineer. My Makefile is messy, and the pointers still scare me.&lt;/p&gt;

&lt;p&gt;But the "Black Box" is open. I know that &lt;code&gt;insmod&lt;/code&gt; isn't magic, it's just loading a library. I know that &lt;code&gt;cat&lt;/code&gt; isn't just reading a file, it's triggering a driver function.&lt;/p&gt;

&lt;p&gt;If you are a web developer, stop fearing the low level. Spin up a User-Mode Linux instance and break things. It's the only way to see how the machine actually works.&lt;/p&gt;

&lt;p&gt;If you want to check my messy code, here is the repo: &lt;a href="https://github.com/fauzanfebrian/linux-kernel-hacking/blob/main/docs/lab-logs/2025-12-20-void-box.md" rel="noopener noreferrer"&gt;github.com/fauzanfebrian/linux-kernel-hacking&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>kernelmodule</category>
      <category>learning</category>
    </item>
    <item>
      <title>From NestJS to malloc: My Nightly Battle with Memory</title>
      <dc:creator>Fauzan Febriansyah</dc:creator>
      <pubDate>Tue, 09 Dec 2025 03:47:18 +0000</pubDate>
      <link>https://dev.to/fauzanfebrian/from-nestjs-to-malloc-my-nightly-battle-with-memory-460n</link>
      <guid>https://dev.to/fauzanfebrian/from-nestjs-to-malloc-my-nightly-battle-with-memory-460n</guid>
      <description>&lt;p&gt;By day, I am a Software Engineer. I build scalable systems using TypeScript and NestJS. I am used to &lt;code&gt;Garbage Collectors&lt;/code&gt; and strict typing that only exists before compilation.&lt;/p&gt;

&lt;p&gt;By night, I leave that comfort zone. I am on a mission to understand &lt;code&gt;First Principles.&lt;/code&gt; I want to stop being just a coder and become a Systems Engineer.&lt;/p&gt;

&lt;p&gt;I used to be ignorant about memory. I thought, "Who cares? Hardware is cheap.". I was wrong. Here is what I learned when I stopped relying on abstractions and started looking at the raw bytes.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Memory Padding: Your Data is Wasting Space
&lt;/h2&gt;

&lt;p&gt;In TypeScript, we don't care how an object is stored. In C, if you don't care, you pay the price.&lt;/p&gt;

&lt;p&gt;Look at this struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 8 bytes&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// 4 bytes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How big is this? Math says 1 + 8 + 4 = 13 bytes. If you say 13, you are wrong. The answer is 24 bytes. &lt;/p&gt;

&lt;p&gt;Why? Because the CPU is picky, it fetches data in aligned chunks (usually 8 bytes), it cannot grab that double easily if it is stuck between two chunks. So, the compiler adds an invisible padding, empty bytes to align it.&lt;/p&gt;

&lt;p&gt;In this simple struct, we wasted 11 bytes just for padding. I learned that just by reordering the struct (putting int before double), I could reduce the padding to only 3 bytes. High-level languages hide this from you. C forces you to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. "Don't Reinvent the Wheel" is Bad Advice
&lt;/h2&gt;

&lt;p&gt;People say: Never rewrite the standard library. I say: Rewrite it, or you will never understand how it works.&lt;/p&gt;

&lt;p&gt;I spent days rebuilding realloc and memcpy from scratch. You think you understand Dynamic Memory Allocation? You don't. Not until you manually calculate byte size, malloc a new block, loop through the old block to copy bytes one by one, and then free the old pointer.&lt;/p&gt;

&lt;p&gt;If you only use the built-in functions, you are just a consumer, not an engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Paradox: Why "One Loop" is Slower than "Two Loops"
&lt;/h2&gt;

&lt;p&gt;This was a hard lesson for me. I had a challenge: remove duplicates from an array. My first attempt was simple: "I want to be efficient. I will do it in one pass."&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Loop through the array.&lt;/li&gt;
&lt;li&gt;If I find a unique item, use realloc to grow the array and add it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It sounded smart. But actually, it was garbage.&lt;/p&gt;

&lt;p&gt;The Problem: realloc is expensive. Very expensive. It often has to find a new memory block, copy everything to the new place, and delete the old one. My "efficient" loop was triggering heavy memory operations constantly.&lt;/p&gt;

&lt;p&gt;The Better Way (Instructor's Method):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Loop once just to count the unique items.&lt;/li&gt;
&lt;li&gt;malloc once with the exact size.&lt;/li&gt;
&lt;li&gt;Loop again to fill it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Iterating twice is actually faster here. &lt;/p&gt;

&lt;p&gt;Think about packing a suitcase. My way was: put one sock in a small bag. Bag is full? Buy a bigger bag, move the sock, add a shirt. Bag is full? Buy a bigger bag... The better way: Lay everything on the floor. See exactly how big it is. Buy the right suitcase once. Pack it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pointers to Pointers (The Real Headache)
&lt;/h2&gt;

&lt;p&gt;You think pointers are hard? Try ***ptr (Pointer to Pointer to Pointer).&lt;br&gt;
I struggled with this when trying to allocate 2D arrays by reference. I kept getting "Segmentation Faults" because I was writing *(ref[i]) instead of (*ref)[i]. The brackets matter.&lt;/p&gt;

&lt;p&gt;But you need this. It simplifies memory management. If you want to swap rows in a matrix, you don't move the values. You just swap the pointers using an XOR operation. It is cleaner and faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Discipline Over Syntax
&lt;/h2&gt;

&lt;p&gt;I finished this learning phase by building a School Management System.&lt;br&gt;
The code wasn't complex. The logic was standard. The hard part was the discipline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every malloc needs a free.&lt;/li&gt;
&lt;li&gt;If I remove a Student from a Course, I must update the Average Grade immediately.&lt;/li&gt;
&lt;li&gt;No memory leaks allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In NestJS, I structure my code to be readable. In C, I structure my data to survive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am still a "wannabe" Systems Engineer. My code is not perfect. My repo is just a "scratchpad", no documentation, just raw learning.&lt;/p&gt;

&lt;p&gt;But at least now, I am not ignorant. I know what happens under the hood.&lt;/p&gt;

&lt;p&gt;If you want to check my journey or laugh at my code, here is the repo: &lt;a href="https://github.com/fauzanfebrian/c-memory-playground" rel="noopener noreferrer"&gt;github.com/fauzanfebrian/c-memory-playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>performance</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
