<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://wassim31.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://wassim31.github.io/" rel="alternate" type="text/html" /><updated>2026-08-23T19:46:22+00:00</updated><id>https://wassim31.github.io/feed.xml</id><title type="html">Wassim Boussebha Abdessamed</title><subtitle>PhD researcher at IMT Atlantique working on AI infrastructure, distributed storage, and operating systems. Working close to the system with C, Go, Linux, eBPF, and Kubernetes.</subtitle><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><entry><title type="html">Preventing Segfaults in Shared Memory IPC: Using Semaphores to Signal Data Readiness</title><link href="https://wassim31.github.io/2026/08/23/preventing-segfaults-in-shared-memory-ipc-using-semaphores-to-signal-data-readiness.html" rel="alternate" type="text/html" title="Preventing Segfaults in Shared Memory IPC: Using Semaphores to Signal Data Readiness" /><published>2026-08-23T10:00:00+00:00</published><updated>2026-08-23T10:00:00+00:00</updated><id>https://wassim31.github.io/2026/08/23/preventing-segfaults-in-shared-memory-ipc-using-semaphores-to-signal-data-readiness</id><content type="html" xml:base="https://wassim31.github.io/2026/08/23/preventing-segfaults-in-shared-memory-ipc-using-semaphores-to-signal-data-readiness.html"><![CDATA[<p>Here’s a bug that’ll make you want to throw your laptop across the room: the reader process calls <code class="language-plaintext highlighter-rouge">shm_open()</code>, it succeeds. It calls <code class="language-plaintext highlighter-rouge">mmap()</code>, it also succeeds. Every syscall you bothered to check is green. And then, sometimes, not always, the process segfaults anyway, or prints garbage where the writer’s message was supposed to be.</p>

<p>If this is happening to you right now, your first move is probably to go re-check the shared memory setup. Right name, right size, right permissions. That’s not where the bug is. It’s not that the reader wasn’t allowed to look at the memory. It’s that it looked too early.</p>

<p>(There’s a different bug that looks similar from far away: the reader runs before the writer has even created the segment, so <code class="language-plaintext highlighter-rouge">shm_open()</code> itself returns <code class="language-plaintext highlighter-rouge">-1</code>. That one’s boring, you fix it by checking your return values and refusing to continue on a bad fd. It’s not what’s happening here, and I’m not going to spend more time on it, because the interesting case, and the one that actually costs people hours, is the one where every single syscall succeeds and the crash happens anyway.)</p>

<h2 id="whats-actually-sitting-in-that-memory">What’s actually sitting in that memory</h2>

<p>Here’s the setup. The writer creates the shared memory object and sizes it:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">shm_open</span><span class="p">(</span><span class="s">"/shared_memory"</span><span class="p">,</span> <span class="n">O_CREAT</span> <span class="o">|</span> <span class="n">O_RDWR</span><span class="p">,</span> <span class="mo">0666</span><span class="p">);</span>
<span class="n">ftruncate</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="mi">4096</span><span class="p">);</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="mi">4096</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span> <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ftruncate()</code> does two things here: it sets the segment to 4096 bytes, and since this is a freshly created object, it zero-fills those bytes on the way there. That’s not a courtesy, it’s the kernel refusing to hand your process leftover contents of some physical page it doesn’t know the history of. Fine. But “zero-filled” is not “contains the message the writer is about to send.” It’s zeros. Placeholder bytes that happen to be sitting there because something had to be.</p>

<p>And that’s the <em>good</em> case. If the segment already existed from an earlier run of your program, and you didn’t clean it up, <code class="language-plaintext highlighter-rouge">ftruncate()</code> to the same size it already has does nothing at all. No zeroing, no reset. Whatever was in there from last time is still in there, and it is very much not guaranteed to look like zeros.</p>

<p>Say the writer and reader agree on this layout:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="n">payload</span> <span class="p">{</span>
    <span class="kt">size_t</span> <span class="n">length</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">buffer</span><span class="p">[];</span>
<span class="p">}</span> <span class="n">payload</span><span class="p">;</span>
</code></pre></div></div>

<p>The writer’s job is eventually to set <code class="language-plaintext highlighter-rouge">length</code> and copy a string into <code class="language-plaintext highlighter-rouge">buffer</code>. The reader’s job is to read <code class="language-plaintext highlighter-rouge">length</code>, then copy that many bytes back out:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">size_t</span> <span class="n">length</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">size_t</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">string</span><span class="p">[</span><span class="n">length</span><span class="p">];</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">size_t</span><span class="p">),</span> <span class="n">length</span><span class="p">);</span>
</code></pre></div></div>

<p>Now picture the two processes starting at roughly the same time, which is the normal case, not some pathological edge case you have to work hard to trigger:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>writer                              reader
------                              ------
shm_open(O_CREAT)
ftruncate()
mmap()
                                     shm_open()   &lt;- segment exists, succeeds
                                     mmap()       &lt;- mapping is valid, succeeds
                                     length = *(size_t *)addr   &lt;- reads zeros or stale bytes
data-&gt;length = length
memcpy(data-&gt;buffer, ...)
</code></pre></div></div>

<p>Nothing in that reader column failed. There’s no line in there you could wrap in an <code class="language-plaintext highlighter-rouge">if</code> and catch. The reader just happened to read <code class="language-plaintext highlighter-rouge">length</code> a few instructions before the writer got around to setting it, and there was nothing stopping it from doing so.</p>

<p>So what does the reader actually get? If the segment was freshly zeroed, <code class="language-plaintext highlighter-rouge">length</code> comes out as <code class="language-plaintext highlighter-rouge">0</code>. <code class="language-plaintext highlighter-rouge">char string[length]</code> is a zero-length VLA, <code class="language-plaintext highlighter-rouge">memcpy()</code> copies nothing, and you get an empty string. Weird output, but the process survives.</p>

<p>If the segment had stale bytes from a previous run instead, <code class="language-plaintext highlighter-rouge">length</code> can be anything a <code class="language-plaintext highlighter-rouge">size_t</code> holds. Say it comes out enormous. <code class="language-plaintext highlighter-rouge">char string[length]</code> isn’t a heap allocation that can fail gracefully and hand you <code class="language-plaintext highlighter-rouge">NULL</code>. It’s a stack pointer bump, done on the spot, at the point of declaration. If that number is big enough, you blow through the rest of the stack before you’ve written a single byte of the string, and the process dies right there, on a line that doesn’t even mention <code class="language-plaintext highlighter-rouge">memcpy()</code>.</p>

<p>And if the garbage length is large but not stack-shattering large, <code class="language-plaintext highlighter-rouge">memcpy()</code> goes ahead and reads that many bytes starting at <code class="language-plaintext highlighter-rouge">addr + sizeof(size_t)</code>. Run past the end of the 4096-byte mapping and you get a segfault from touching unmapped memory. Or you don’t run quite that far, and instead you silently read whatever else happens to be mapped nearby, and hand your caller a string that isn’t garbage-looking at all, it’s just wrong. That second version is worse, because nothing crashes and nothing looks broken.</p>

<p>The thing worth sitting with here is that <code class="language-plaintext highlighter-rouge">shm_open()</code> returned a valid fd and <code class="language-plaintext highlighter-rouge">mmap()</code> returned a valid pointer both times, in both the empty-string case and the stack-overflow case. The syscalls did their job. The bug is entirely downstream of them, in the assumption that a successful mapping means there’s something meaningful behind it.</p>

<h2 id="sleep-is-not-a-fix">sleep() is not a fix</h2>

<p>Once you’ve figured out it’s a timing problem, the first instinct is almost always some version of “just wait a little before reading.” None of the usual ways of doing that actually close the race.</p>

<p>The most common one is a retry loop with <code class="language-plaintext highlighter-rouge">sleep()</code> in it: poll <code class="language-plaintext highlighter-rouge">length</code>, and if it still looks unset, sleep and check again. This appears to work, most of the time, because your writer probably finishes in a few milliseconds and your sleep is probably a full second, so in practice you never catch it in the act. That’s not the same thing as it being fixed. It’s a race where you’ve made the reader’s side slower, which changes the odds without touching the actual problem. Put the machine under load, or add anything at all to the writer’s startup, like reading a config file or allocating a buffer, and your sleep duration stops being generous enough. You’re back to the original bug, just less often, which honestly might be worse, because now it’s the kind of bug that only shows up in production.</p>

<p>A slightly smarter-sounding version checks whether the memory is still all zeros before reading it. This has the same timing problem as the sleep loop, plus a correctness problem of its own: zero is a perfectly legitimate value. If the writer’s real payload happens to produce a <code class="language-plaintext highlighter-rouge">length</code> of <code class="language-plaintext highlighter-rouge">0</code>, or the first eight bytes of a valid message happen to be zero, your “is it ready yet” check can’t tell that apart from “not ready yet.” And as covered above, if the segment wasn’t freshly zeroed, “non-zero” doesn’t mean “written by this run” either. Leftover bytes from an old run can easily be non-zero, so this check can happily report “looks ready” on a segment the current writer hasn’t touched at all.</p>

<p>And then there’s just assuming the writer runs first because you start it first. There’s no guarantee anywhere that backs this up. Process creation order and scheduler decisions aren’t something POSIX promises you control over, and even setting that aside, “started first” isn’t “finished writing.” If the writer does anything at all before it gets to the actual write, that’s a window where a reader that technically started later can still get there first.</p>

<p>All three of these are trying to guess readiness from either the contents of the memory or the passage of time. What you actually want is for the writer to tell you, explicitly, “I’m done,” and for the reader to be physically unable to proceed until it hears that. That’s what a semaphore gives you, and it’s the only one of these that isn’t a guess.</p>

<h2 id="a-semaphore-that-only-goes-one-way">A semaphore that only goes one way</h2>

<p>If you’ve used semaphores before, it was probably as a mutex: a binary semaphore that starts at 1, where a thread calls <code class="language-plaintext highlighter-rouge">sem_wait()</code> to grab it, does some work, and calls <code class="language-plaintext highlighter-rouge">sem_post()</code> to hand it back. Both sides call both functions. It’s symmetric, and it’s protecting a critical section.</p>

<p>That’s not what we’re building here, and thinking of this as “a lock” will make the rest of it confusing. What we want is a readiness signal, and it differs in two specific ways:</p>

<ul>
  <li>It starts at 0, not 1. Zero means “nothing to report yet.” A lock starts at 1 because the resource is free from the beginning; a readiness signal starts at 0 because the data isn’t ready from the beginning, and shouldn’t be treated as ready until someone says otherwise.</li>
  <li>The two sides don’t do the same thing. The writer only ever calls <code class="language-plaintext highlighter-rouge">sem_post()</code>. The reader only ever calls <code class="language-plaintext highlighter-rouge">sem_wait()</code>. Nobody waits and posts around the same operation the way a mutex would have both sides do. It only goes one direction: the writer speaks once, the reader listens once.</li>
</ul>

<p>The mechanics: <code class="language-plaintext highlighter-rouge">sem_wait()</code> decrements the semaphore’s counter. If the counter’s already at 0, it doesn’t go negative, it just blocks, parking the calling process until something else bumps the count back up. This is a genuine suspension, handled by the kernel, not a spin loop and not a <code class="language-plaintext highlighter-rouge">sleep()</code> in disguise. <code class="language-plaintext highlighter-rouge">sem_post()</code> increments the counter, and if there’s a process sitting in <code class="language-plaintext highlighter-rouge">sem_wait()</code>, it wakes exactly one of them.</p>

<p>Put that back into our reader and writer. The semaphore starts at 0. The reader hits <code class="language-plaintext highlighter-rouge">sem_wait()</code> before it has touched a single byte of the mapped memory, sees a count of 0, and blocks right there. It cannot execute the next line. It’s not choosing to wait, it’s stuck. Meanwhile the writer sets <code class="language-plaintext highlighter-rouge">length</code>, copies the string into <code class="language-plaintext highlighter-rouge">buffer</code>, and only after both of those are done does it call <code class="language-plaintext highlighter-rouge">sem_post()</code>. That’s what bumps the count and wakes the reader up. The reader’s <code class="language-plaintext highlighter-rouge">sem_wait()</code> returns, and only now, after the writer is provably finished, does the reader go read <code class="language-plaintext highlighter-rouge">length</code> and <code class="language-plaintext highlighter-rouge">buffer</code>.</p>

<p>Go back to the diagram from earlier. There’s no version of it anymore where the reader’s read of <code class="language-plaintext highlighter-rouge">length</code> can land before the writer’s write of <code class="language-plaintext highlighter-rouge">length</code>, because the line that does the reading is now behind a wall the writer built, and the writer only takes that wall down once it’s actually done.</p>

<h2 id="heres-the-fix-in-full">Here’s the fix, in full</h2>

<p>Complete writer and reader, error-checked this time. The writer owns creating both the shared memory object and the semaphore; the reader just opens what’s already there.</p>

<p><strong>writer.c</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;semaphore.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span>
<span class="cp">#define SHM_NAME "/shared_memory"
#define SEM_NAME "/semaphore"
#define SHM_SIZE 4096
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">payload</span> <span class="p">{</span>
    <span class="kt">size_t</span> <span class="n">length</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">buffer</span><span class="p">[];</span>
<span class="p">}</span> <span class="n">payload</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">shm_open</span><span class="p">(</span><span class="n">SHM_NAME</span><span class="p">,</span> <span class="n">O_CREAT</span> <span class="o">|</span> <span class="n">O_RDWR</span><span class="p">,</span> <span class="mo">0666</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"shm_open"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ftruncate</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SHM_SIZE</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"ftruncate"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">SHM_SIZE</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span> <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">==</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"mmap"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/* Initial value 0: nothing is ready until we say so. */</span>
    <span class="n">sem_t</span> <span class="o">*</span><span class="n">sem</span> <span class="o">=</span> <span class="n">sem_open</span><span class="p">(</span><span class="n">SEM_NAME</span><span class="p">,</span> <span class="n">O_CREAT</span><span class="p">,</span> <span class="mo">0666</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sem</span> <span class="o">==</span> <span class="n">SEM_FAILED</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"sem_open"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="s">"Hello world from Process A"</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">length</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">payload</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="n">payload</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">;</span>
    <span class="n">data</span><span class="o">-&gt;</span><span class="n">length</span> <span class="o">=</span> <span class="n">length</span><span class="p">;</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">buffer</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>

    <span class="cm">/* Only signal readiness after the payload is fully written. */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sem_post</span><span class="p">(</span><span class="n">sem</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"sem_post"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"writer: wrote %zu bytes and signaled the reader</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>

    <span class="n">munmap</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">SHM_SIZE</span><span class="p">);</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
    <span class="n">sem_close</span><span class="p">(</span><span class="n">sem</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>reader.c</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;semaphore.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span>
<span class="cp">#define SHM_NAME "/shared_memory"
#define SEM_NAME "/semaphore"
#define SHM_SIZE 4096
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">payload</span> <span class="p">{</span>
    <span class="kt">size_t</span> <span class="n">length</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">buffer</span><span class="p">[];</span>
<span class="p">}</span> <span class="n">payload</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">shm_open</span><span class="p">(</span><span class="n">SHM_NAME</span><span class="p">,</span> <span class="n">O_RDWR</span><span class="p">,</span> <span class="mo">0666</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"shm_open"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">SHM_SIZE</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span> <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">==</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"mmap"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">sem_t</span> <span class="o">*</span><span class="n">sem</span> <span class="o">=</span> <span class="n">sem_open</span><span class="p">(</span><span class="n">SEM_NAME</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sem</span> <span class="o">==</span> <span class="n">SEM_FAILED</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"sem_open"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"reader: waiting for data...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="cm">/* Blocks here until the writer calls sem_post(). No memory is
       touched before this line returns. */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sem_wait</span><span class="p">(</span><span class="n">sem</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"sem_wait"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">payload</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="n">payload</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">length</span> <span class="o">=</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">;</span>

    <span class="kt">char</span> <span class="o">*</span><span class="n">string</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">length</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">string</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"malloc"</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">buffer</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"reader: got %zu bytes: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">length</span><span class="p">,</span> <span class="n">string</span><span class="p">);</span>

    <span class="n">free</span><span class="p">(</span><span class="n">string</span><span class="p">);</span>
    <span class="n">munmap</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">SHM_SIZE</span><span class="p">);</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
    <span class="n">sem_close</span><span class="p">(</span><span class="n">sem</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The writer passes <code class="language-plaintext highlighter-rouge">O_CREAT</code> to both <code class="language-plaintext highlighter-rouge">shm_open()</code> and <code class="language-plaintext highlighter-rouge">sem_open()</code>; the reader passes neither, so if it happens to run before the writer has created either one, it fails immediately with a plain <code class="language-plaintext highlighter-rouge">ENOENT</code> instead of quietly creating something itself. The <code class="language-plaintext highlighter-rouge">shm_open()</code>/<code class="language-plaintext highlighter-rouge">mmap()</code> checks are what catch that case specifically; <code class="language-plaintext highlighter-rouge">sem_wait()</code> is what catches the one this whole article is about, where the segment exists but isn’t populated yet. The two don’t overlap and neither one covers for the other.</p>

<p>One more thing about the reader: it copies the payload into a heap buffer sized with <code class="language-plaintext highlighter-rouge">malloc()</code>, not a stack VLA like the earlier broken version. <code class="language-plaintext highlighter-rouge">length</code> here is coming from a cooperating process on the same machine rather than something adversarial, so this isn’t quite the same threat model as parsing untrusted input, but I still wouldn’t size a stack array off a number I read out of shared memory without thinking about it first. If you want the longer version of that argument, I wrote a whole piece on it: <a href="/2026/08/22/safe-length-based-data-sharing-in-c.html">Safe Length-Based Data Sharing in C</a>.</p>

<h2 id="the-pitfalls-that-get-you-anyway">The pitfalls that get you anyway</h2>

<p>POSIX named semaphores don’t go away when your process exits. They live in the kernel, backed by a file under <code class="language-plaintext highlighter-rouge">/dev/shm</code> on Linux, until something explicitly calls <code class="language-plaintext highlighter-rouge">sem_unlink()</code>, or the machine reboots. This bites people in a specific and annoying way: it can make the exact race this article is about disappear from your testing while still being sitting in your code.</p>

<p>Say you run the writer and reader once, cleanly. Writer posts, reader waits and consumes it, semaphore ends the run at 0. Now say you run the writer <em>twice</em> in a row, before ever running the reader, maybe because you’re testing something else entirely. Each run calls <code class="language-plaintext highlighter-rouge">sem_post()</code>. Since the semaphore already existed after the first run, the second run’s <code class="language-plaintext highlighter-rouge">sem_open(SEM_NAME, O_CREAT, ...)</code> just hands back the existing one, unchanged, because <code class="language-plaintext highlighter-rouge">O_CREAT</code> on an object that already exists ignores the initial-value argument you gave it. So after two writer runs, the count sits at 2.</p>

<p>Now the reader runs. Its <code class="language-plaintext highlighter-rouge">sem_wait()</code> sees a non-zero count and returns immediately, without ever actually blocking on anything. If you happen to be mid-refactor and there’s a bug that makes the <em>next</em> writer run crash before it writes anything, or skip the write entirely, you will not see it. The leftover count from an earlier run covers for it completely, and the reader sails through <code class="language-plaintext highlighter-rouge">sem_wait()</code> and reads whatever’s sitting in the segment from before. It’s the exact race from the top of this article, quietly back, except this time it’s hiding behind a semaphore that looks, from the outside, like it’s doing its job.</p>

<p>The fix is <code class="language-plaintext highlighter-rouge">sem_unlink(SEM_NAME)</code> and <code class="language-plaintext highlighter-rouge">shm_unlink(SHM_NAME)</code> between runs, so each run starts from a genuinely fresh semaphore at the value you meant, and a genuinely fresh, zeroed segment, instead of whatever the last run happened to leave behind.</p>

<p>The other thing worth deciding on purpose, not by accident: who owns <code class="language-plaintext highlighter-rouge">O_CREAT</code>. In the example above, the writer creates both the shared memory object and the semaphore, and the reader only ever opens what’s already there. That’s not arbitrary. If both sides pass <code class="language-plaintext highlighter-rouge">O_CREAT</code>, there’s no longer a clean answer to “who set the initial value,” because those creation arguments only take effect for whichever process’s open call happens to run first, and if both processes start around the same moment, that’s a race of its own. Pick one side to own creation, have it create both objects before doing anything else, and have the other side open them without <code class="language-plaintext highlighter-rouge">O_CREAT</code>, so a missing object fails loudly instead of getting created ambiguously by whichever process got there first.</p>

<h2 id="so-back-to-that-segfault">So, back to that segfault</h2>

<p>If you’re staring at a reader that segfaults sometimes, or hands back garbage sometimes, and every syscall you’re checking is returning success, it’s very likely this. Not a missing segment, not a bad permission bit, not a corrupted mapping. Just a reader that got to the memory before the writer was done with it, because nothing was stopping it from trying.</p>

<p>A semaphore initialized to 0, posted once by the writer after it’s actually finished, waited on once by the reader before it touches anything, closes that gap completely. Not by making the race less likely. By making it structurally impossible for the reader’s read to land before the writer’s write.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><category term="c" /><category term="linux" /><category term="ipc" /><category term="systemsprogramming" /><summary type="html"><![CDATA[mmap() succeeding doesn't mean the data behind it is valid. A close look at the race where a reader touches shared memory before the writer is done, and how a semaphore closes it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/face.jpg" /><media:content medium="image" url="https://wassim31.github.io/face.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Safe Length-Based Data Sharing in C</title><link href="https://wassim31.github.io/2026/08/22/safe-length-based-data-sharing-in-c.html" rel="alternate" type="text/html" title="Safe Length-Based Data Sharing in C" /><published>2026-08-22T10:00:00+00:00</published><updated>2026-08-22T10:00:00+00:00</updated><id>https://wassim31.github.io/2026/08/22/safe-length-based-data-sharing-in-c</id><content type="html" xml:base="https://wassim31.github.io/2026/08/22/safe-length-based-data-sharing-in-c.html"><![CDATA[<p>Let’s say two processes share a region of memory. One of them writes some data into it, the other reads it. Simple enough, until the reading side has to answer a question that sounds trivial but isn’t: <em>how much of this memory is actually mine to read?</em></p>

<h2 id="the-problem">The problem</h2>

<p>Here’s the kind of code that shows up when nobody has asked that question yet:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
<span class="n">strcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">source</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">source</code> is a pointer into the shared region. It looks like a string. It might even be a string, most of the time. So <code class="language-plaintext highlighter-rouge">strcpy()</code> gets called, and it works, in testing, on your machine, with the inputs you happened to try.</p>

<p>The problem is that <code class="language-plaintext highlighter-rouge">strcpy()</code> doesn’t know three things:</p>

<ul>
  <li>how large the memory region behind <code class="language-plaintext highlighter-rouge">source</code> actually is</li>
  <li>how large <code class="language-plaintext highlighter-rouge">buffer</code>, the destination, actually is</li>
  <li>whether there is a null terminator anywhere inside the valid region at all</li>
</ul>

<p>It doesn’t ask. It just starts reading byte by byte from <code class="language-plaintext highlighter-rouge">source</code>, copying each one into <code class="language-plaintext highlighter-rouge">buffer</code>, until it hits a <code class="language-plaintext highlighter-rouge">'\0'</code>. That’s the entire contract of <code class="language-plaintext highlighter-rouge">strcpy()</code>: keep going until you see a zero byte.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source:
+----------------------------------+
| H | e | l | l | o | ... | ???  |
+----------------------------------+
                              ^
                       where is '\0'?
</code></pre></div></div>

<p>If the writing process filled the shared region with exactly <code class="language-plaintext highlighter-rouge">"Hello"</code> and nothing else, and the region happens to end right after those five bytes, there is no <code class="language-plaintext highlighter-rouge">'\0'</code> inside the valid data. <code class="language-plaintext highlighter-rouge">strcpy()</code> doesn’t know that “valid data” ended, so it keeps reading whatever comes next: uninitialized memory, another process’s leftover bytes, or an unmapped page that segfaults on touch.</p>

<p>This isn’t a strcpy bug. <code class="language-plaintext highlighter-rouge">strcpy()</code> is doing exactly what it was designed to do. The bug is upstream of it: we handed it a pointer and implicitly assumed a length that was never actually communicated.</p>

<h2 id="the-key-idea-stop-asking-where-start-asking-how-much">The key idea: stop asking where, start asking how much</h2>

<p>The fix isn’t a smarter string function. It’s a different question. Instead of:</p>

<blockquote>
  <p>“Where is the <code class="language-plaintext highlighter-rouge">'\0'</code>?”</p>
</blockquote>

<p>the receiving side should be asking:</p>

<blockquote>
  <p>“How many bytes are valid, starting from this pointer?”</p>
</blockquote>

<p>That second question has an answer that doesn’t depend on scanning memory and hoping. It’s a number, and if the writer tells you that number up front, you never have to guess.</p>

<p>This means representing the data explicitly as a length followed by a payload, instead of a bare, unmarked stream of bytes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------+----------------------+
| length     | payload              |
+------------+----------------------+
             &lt;---- length bytes ---&gt;
</code></pre></div></div>

<p>This is usually called <strong>length-delimited</strong> or <strong>length-prefixed</strong> data, and it’s a genuinely old idea, older than C strings themselves. The boundary of the data isn’t a special byte value hidden somewhere in the payload. It’s a number, sitting right next to the data it describes.</p>

<h2 id="why-this-works-better-than-null-termination">Why this works better than null termination</h2>

<p>Length-prefixing isn’t just a workaround for the shared-memory case. It’s a strictly more general representation, because it works for arbitrary binary data, not just text.</p>

<p>Think about what a null terminator actually assumes: that the byte value <code class="language-plaintext highlighter-rouge">0x00</code> never legitimately appears inside your data. That’s a reasonable assumption for ASCII text. It’s not a safe assumption for, say, a serialized struct, an image, an encrypted blob, or a network packet.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----+----+----+----+----+
| 41 | 00 | FF | 42 | 00 |
+----+----+----+----+----+
</code></pre></div></div>

<p>The second byte here is <code class="language-plaintext highlighter-rouge">0x00</code>. Treat this as a C string, and anything reading it with <code class="language-plaintext highlighter-rouge">strlen()</code> or <code class="language-plaintext highlighter-rouge">strcpy()</code> would stop after just one byte, <code class="language-plaintext highlighter-rouge">0x41</code>, silently discarding the rest of the payload. That <code class="language-plaintext highlighter-rouge">0x00</code> isn’t a terminator. It’s just a byte that happens to be zero, sitting in the middle of a binary blob with four more bytes after it.</p>

<p>So <code class="language-plaintext highlighter-rouge">'\0'</code> can’t be a universal boundary marker for arbitrary data, because for binary data, every byte value including zero is a legitimate payload byte. Length, on the other hand, doesn’t care what values the bytes hold. It just says how many of them belong to you.</p>

<h2 id="introducing-memcpy">Introducing memcpy()</h2>

<p>Once you have an explicit length, the right copying primitive changes too.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">memcpy</span><span class="p">(</span><span class="n">destination</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">memcpy()</code> copies exactly <code class="language-plaintext highlighter-rouge">length</code> bytes. It doesn’t search for anything inside them, and it doesn’t care whether byte 40 happens to be zero. It just moves the number of bytes you told it to move, and stops. That makes it the natural fit for <code class="language-plaintext highlighter-rouge">[length][payload]</code> data, since the whole point of that representation was to make “how many bytes” an explicit, known value instead of something discovered by scanning.</p>

<p>But here’s the part that’s easy to skip past: <strong><code class="language-plaintext highlighter-rouge">memcpy()</code> is not automatically safe just because you used it instead of <code class="language-plaintext highlighter-rouge">strcpy()</code>.</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">length</code> is 1000, this overflows <code class="language-plaintext highlighter-rouge">buffer</code> just as badly as an unterminated <code class="language-plaintext highlighter-rouge">strcpy()</code> would. <code class="language-plaintext highlighter-rouge">memcpy()</code> trusts you completely. It will happily walk off the end of <code class="language-plaintext highlighter-rouge">source</code>, or write off the end of <code class="language-plaintext highlighter-rouge">buffer</code>, or both, if you hand it a length that doesn’t actually fit. Switching functions didn’t buy you safety. What buys you safety is validating the length before you ever call <code class="language-plaintext highlighter-rouge">memcpy()</code> at all.</p>

<h2 id="the-two-boundaries-youre-actually-protecting">The two boundaries you’re actually protecting</h2>

<p>This is the part worth slowing down for, because it’s easy to only think about half of it.</p>

<p>Every copy has two separate regions of memory involved, and each one has its own size that the length has to respect.</p>

<p><strong>The source boundary</strong>: how much valid data actually exists at the other end of that pointer.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------------------+
| valid source region       |
+---------------------------+
</code></pre></div></div>

<p><strong>The destination boundary</strong>: how much space you actually have to write into.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------------+
| destination    |
+----------------+
</code></pre></div></div>

<p>A copy is only safe when both of these hold at the same time:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>length &lt;= source_size
length &lt;= destination_capacity
</code></pre></div></div>

<p>It’s tempting to check only one of these and feel done. Check only the source side, and you can overflow a destination buffer that was smaller than you assumed. Check only the destination side, and you can read past the end of a source region that was shorter than the length claimed. Both failures look identical from the outside: memory corruption, a crash, or worse, neither. The length has to fit both ends, not just one.</p>

<h2 id="validating-the-length-against-the-region-itself">Validating the length against the region itself</h2>

<p>Now bring this back to the shared-memory scenario we started with. Say the shared region is <code class="language-plaintext highlighter-rouge">SIZE</code> bytes total, and it’s laid out as a length field followed by the payload:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------------------+----------------------+
| length               | payload              |
+----------------------+----------------------+
</code></pre></div></div>

<p>Before touching the payload at all, the receiver has to check that the claimed payload actually fits inside the region it was given. The condition you want is:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">length</span> <span class="o">&lt;=</span> <span class="n">SIZE</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">length</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// safe to read `length` bytes of payload</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It’s tempting to write this the other way around, checking <code class="language-plaintext highlighter-rouge">sizeof(length) + length &lt;= SIZE</code> instead. Don’t. That version adds two numbers before comparing, and if <code class="language-plaintext highlighter-rouge">length</code> is attacker-controlled or corrupted, the addition can overflow and wrap around to a small number that sails right past the check while <code class="language-plaintext highlighter-rouge">length</code> itself is enormous. The subtraction form avoids this: <code class="language-plaintext highlighter-rouge">SIZE - sizeof(length)</code> is a fixed, known-safe value computed once, so you’re only ever comparing an untrusted number against it, never adding an untrusted number into something that could wrap.</p>

<h2 id="turning-length-delimited-data-into-a-c-string">Turning length-delimited data into a C string</h2>

<p>Length-delimited data never needed a <code class="language-plaintext highlighter-rouge">'\0'</code> to begin with; the length told you exactly where it ends. But sometimes the code on the receiving end genuinely wants an ordinary C string, because it’s about to hand the data to a function that expects one.</p>

<p>That’s fine, as long as you understand it as a conversion step, not the native representation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">string</span><span class="p">[</span><span class="n">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
<span class="n">string</span><span class="p">[</span><span class="n">length</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">+ 1</code> and the manual assignment matter. The length-delimited payload is exactly <code class="language-plaintext highlighter-rouge">length</code> bytes, no more:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Length-delimited:
+----------------------+
| H e l l o            |
+----------------------+
</code></pre></div></div>

<p>A C string needs one extra byte tacked on at the end, the terminator, which was never part of the original data:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C string:
+-------------------------+
| H e l l o | \0         |
+-------------------------+
</code></pre></div></div>

<p>The shared representation on the wire, or in the shared region, stays length-based the whole time. The null terminator only gets added at the very last moment, locally, when you’re producing a C string for some function that specifically requires one. It’s a local convenience, not the source of truth for where the data ends.</p>

<h2 id="why-char-stringlength-alone-is-still-dangerous">Why <code class="language-plaintext highlighter-rouge">char string[length]</code> alone is still dangerous</h2>

<p>Notice that the snippet above uses <code class="language-plaintext highlighter-rouge">length + 1</code> on the stack. If <code class="language-plaintext highlighter-rouge">length</code> comes from shared or external data, you cannot trust it just because it’s sitting right there in a length field. A corrupted region, or one written by a process you don’t fully trust, can set <code class="language-plaintext highlighter-rouge">length</code> to whatever it wants, and an enormous value can blow the stack before you’ve written a single byte, trigger allocation failures your code never checked for, or drive reads and writes to addresses that have nothing to do with your actual data.</p>

<p>The length has to be validated against a real, known bound, like the region size we checked above, <em>before</em> it’s used to size an allocation or drive a copy. Once that check is done, it’s often simpler and safer to copy into a fixed-size buffer you already control, and reject or truncate anything that doesn’t fit, rather than sizing a new buffer dynamically off a number you just received.</p>

<h2 id="a-quick-word-on-strncpy">A quick word on strncpy()</h2>

<p>At this point someone always says: just use <code class="language-plaintext highlighter-rouge">strncpy()</code> instead of <code class="language-plaintext highlighter-rouge">strcpy()</code>, problem solved. It’s worth being clear about why that’s not the whole fix.</p>

<p><code class="language-plaintext highlighter-rouge">strncpy()</code> takes a maximum number of bytes to copy, which sounds like what we want. But it still thinks in terms of strings: if the source runs out of bytes before hitting <code class="language-plaintext highlighter-rouge">'\0'</code>, it pads the rest of the destination with zero bytes, and if the source doesn’t contain a <code class="language-plaintext highlighter-rouge">'\0'</code> within the given max length, it won’t add one for you either. You can walk away with an unterminated buffer, having done exactly what the function documented, and still be back at square one.</p>

<p><code class="language-plaintext highlighter-rouge">strncpy()</code> is a safer string function. It is not a length-delimited-data function. It still frames the problem in terms of a maximum scan for a terminator, not an explicit number of valid bytes. The actual fix was never “pick a better string function.” It’s moving away from string-shaped thinking entirely, from an implicit length discovered by scanning to an explicit length that’s known up front.</p>

<h2 id="the-takeaway">The takeaway</h2>

<p>In C, a pointer tells you where the bytes are. It does not tell you how many of them are valid.</p>

<p>That single sentence is the whole article, if you need to remember one thing from it. Everything else follows from taking it seriously:</p>

<ul>
  <li><strong>Pointer</strong> = where the data starts.</li>
  <li><strong>Length</strong> = how much of it is actually yours to read.</li>
  <li><strong>Buffer capacity</strong> = how much you can safely write, a separate number from the length.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">'\0'</code></strong> = a string terminator, useful for text, not a universal marker for where arbitrary data ends.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">memcpy()</code></strong> = copies exactly the number of bytes you tell it to, nothing more, nothing less, and trusts you completely to have gotten that number right.</li>
  <li><strong>Validation</strong> = the actual step that makes any of this safe, checked against both the source region and the destination buffer, before a single byte moves.</li>
</ul>

<p>None of this is exotic. It’s the same idea underneath length-prefixed protocol messages, <code class="language-plaintext highlighter-rouge">struct</code>-based binary formats, and most serious binary parsers you’ll come across. The pointer was never going to tell you how many bytes were valid. You have to bring that number with you.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><category term="c" /><category term="systemsprogramming" /><category term="memory" /><category term="security" /><summary type="html"><![CDATA[Don't ask where the '\0' is. Ask how many bytes are valid. A progressive look at why strcpy() fails on shared data, and how length-delimited buffers fix it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/face.jpg" /><media:content medium="image" url="https://wassim31.github.io/face.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Saving the state of an interactive container</title><link href="https://wassim31.github.io/2024/09/27/saving-the-state-of-an-interactive-container.html" rel="alternate" type="text/html" title="Saving the state of an interactive container" /><published>2024-09-27T16:16:35+00:00</published><updated>2024-09-27T16:16:35+00:00</updated><id>https://wassim31.github.io/2024/09/27/saving-the-state-of-an-interactive-container</id><content type="html" xml:base="https://wassim31.github.io/2024/09/27/saving-the-state-of-an-interactive-container.html"><![CDATA[<p>Sometimes we need to start a Docker container in an interactive shell for testing purposes. During the session you might write some application code, download some big chunks of files, or configure the environment according to your use case. But starting the container interactively means it gets destroyed once an interrupt is done (0 or -1), and all of that work goes with it.</p>

<h2 id="starting-the-container">Starting the container</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">--mount</span> <span class="nb">type</span><span class="o">=</span><span class="nb">bind</span>,source<span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/work,target<span class="o">=</span>/work <span class="nt">-p</span> 8888:8888 opencvcourses/opencv:440
</code></pre></div></div>

<p>What each flag does:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-it</code> starts an interactive shell. This switch is always needed to start the container, otherwise it will start and stop instantly.</li>
  <li><code class="language-plaintext highlighter-rouge">--rm</code> specifies to kill the container after it is exited.</li>
  <li><code class="language-plaintext highlighter-rouge">--mount</code> creates persistent storage to save all the work.</li>
  <li><code class="language-plaintext highlighter-rouge">-p</code> exposes a container’s port to the host.</li>
</ul>

<p><strong>Note:</strong> run this command in the parent directory of the <code class="language-plaintext highlighter-rouge">work</code> folder.</p>

<h2 id="saving-the-state">Saving the state</h2>

<p>You can save the state by committing the changes to the pulled image:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker ps
docker commit &lt;container_id&gt; repo/testimage:version3
</code></pre></div></div>

<p>That’s it. Next time you run the image <code class="language-plaintext highlighter-rouge">repo/testimage:version3</code>, everything is exactly where you left it.</p>

<h2 id="is-this-good-practice">Is this good practice?</h2>

<p>While this works, this approach is considered poor practice by the Docker community. The best way to go is using a Dockerfile.</p>

<p>And yeah, I think that’s the right way for production, for sure. But many of us are just using Docker locally as a slimmed down version of VirtualBox, and just want the damn state saved, exactly as it is. And we don’t want extra volumes on our local drives either.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><summary type="html"><![CDATA[Sometimes we need to start a Docker container in an interactive shell for testing, but an interrupt destroys everything. Here is how to save its state.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/images/posts/saving-the-state-of-an-interactive-container/cover.jpeg" /><media:content medium="image" url="https://wassim31.github.io/images/posts/saving-the-state-of-an-interactive-container/cover.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Install OpenCV 4.5 on Ubuntu 22.04</title><link href="https://wassim31.github.io/2024/09/27/install-opencv-45-on-ubuntu-2204.html" rel="alternate" type="text/html" title="Install OpenCV 4.5 on Ubuntu 22.04" /><published>2024-09-27T05:50:45+00:00</published><updated>2024-09-27T05:50:45+00:00</updated><id>https://wassim31.github.io/2024/09/27/install-opencv-45-on-ubuntu-2204</id><content type="html" xml:base="https://wassim31.github.io/2024/09/27/install-opencv-45-on-ubuntu-2204.html"><![CDATA[<p>OpenCV (Open Source Computer Vision Library) is a library of programming functions mainly for real-time computer vision.</p>

<p>But its installation can be very tricky in an environment like Linux, so let’s follow a correct installation process.</p>

<h2 id="download-the-sources">Download the sources</h2>

<p>The most recent release at the time of writing is <a href="https://github.com/opencv/opencv/releases/tag/4.5.1">4.5.1</a>.</p>

<p>Create a tmp folder for all the archives:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> ~/opencv4.5-tmp <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/opencv4.5-tmp
</code></pre></div></div>

<p>Download the OpenCV sources and the opencv-contrib sources:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://github.com/opencv/opencv/archive/4.5.1.zip <span class="nt">-O</span> opencv.zip
wget https://github.com/opencv/opencv_contrib/archive/4.5.1.zip <span class="nt">-O</span> opencv_contrib.zip
</code></pre></div></div>

<p>Unzip both archives:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unzip opencv.zip
unzip opencv_contrib.zip
</code></pre></div></div>

<p>Move the files to simpler directory names:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mv </span>opencv-4.5.1/ opencv
<span class="nb">mv </span>opencv_contrib-4.5.1/ opencv_contrib
</code></pre></div></div>

<h2 id="build-and-install">Build and install</h2>

<p>Make a build directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>opencv <span class="o">&amp;&amp;</span> <span class="nb">mkdir </span>build <span class="o">&amp;&amp;</span> <span class="nb">cd </span>build
</code></pre></div></div>

<p>Copy and run the following command (install cmake first if it is not available on your system):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmake <span class="nt">-D</span> <span class="nv">CMAKE_BUILD_TYPE</span><span class="o">=</span>DEBUG <span class="se">\</span>
      <span class="nt">-D</span> <span class="nv">CMAKE_INSTALL_PREFIX</span><span class="o">=</span>~/opencv4.5-custom <span class="se">\</span>
      <span class="nt">-D</span> <span class="nv">OPENCV_EXTRA_MODULES_PATH</span><span class="o">=</span>~/opencv4.5-tmp/opencv_contrib/modules <span class="se">\</span>
      <span class="nt">-D</span> <span class="nv">OPENCV_GENERATE_PKGCONFIG</span><span class="o">=</span>ON <span class="se">\</span>
      <span class="nt">-D</span> <span class="nv">BUILD_EXAMPLES</span><span class="o">=</span>ON ..
</code></pre></div></div>

<p>Make the project:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make <span class="nt">-j4</span>
</code></pre></div></div>

<p>Install OpenCV:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>make <span class="nb">install</span>
</code></pre></div></div>

<p>Ensure it is updated in the library storage:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ldconfig
</code></pre></div></div>

<h2 id="configure-a-c-project-to-work-with-opencv">Configure a C++ project to work with OpenCV</h2>

<p>Open your editor of choice (vim in my case), create a folder <code class="language-plaintext highlighter-rouge">~/projects/HelloOpenCV</code>, and put your code in <code class="language-plaintext highlighter-rouge">main.cpp</code>.</p>

<p>First, let’s try to compile the application with g++ as usual:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g++ <span class="nt">-Wall</span> <span class="nt">-o</span> main main.cpp
</code></pre></div></div>

<p>We see that it cannot find our library headers.</p>

<p>For that we need to provide the path to the headers and the linker flags. The best way to find them all in one place is the pkg-config utility. Remember we provided an additional argument to our cmake generation? So let’s execute the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">PKG_CONFIG_PATH</span><span class="o">=</span><span class="nv">$PKG_CONFIG_PATH</span>:/home/parallels/opencv4.5-custom/lib/pkgconfig
pkg-config <span class="nt">--cflags</span> <span class="nt">--libs</span> opencv4
</code></pre></div></div>

<p>Now add all the flags to the compilation command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g++ <span class="nt">-Wall</span> <span class="nt">-o</span> main main.cpp <span class="si">$(</span>pkg-config <span class="nt">--cflags</span> <span class="nt">--libs</span> opencv4<span class="si">)</span>
</code></pre></div></div>

<p>Or a more explicit version for our particular sample:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g++ <span class="nt">-Wall</span> <span class="nt">-o</span> main main.cpp <span class="se">\</span>
   <span class="nt">-I</span>/home/parallels/opencv4.5-custom/include/opencv4 <span class="se">\</span>
   <span class="nt">-L</span>/home/parallels/opencv4.5-custom/lib <span class="se">\</span>
   <span class="nt">-lopencv_highgui</span> <span class="nt">-lopencv_videoio</span> <span class="nt">-lopencv_imgcodecs</span> <span class="nt">-lopencv_core</span>
</code></pre></div></div>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><summary type="html"><![CDATA[OpenCV installation can be tricky on Linux. A correct process for building OpenCV 4.5 from source, for both Python and C++.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/face.jpg" /><media:content medium="image" url="https://wassim31.github.io/face.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Real-time Disk Size Expanding in a Linux virtual machine</title><link href="https://wassim31.github.io/2024/07/05/real-time-disk-size-expanding-in-a-virtual-machine-on-linux-machine.html" rel="alternate" type="text/html" title="Real-time Disk Size Expanding in a Linux virtual machine" /><published>2024-07-05T15:14:35+00:00</published><updated>2024-07-05T15:14:35+00:00</updated><id>https://wassim31.github.io/2024/07/05/real-time-disk-size-expanding-in-a-virtual-machine-on-linux-machine</id><content type="html" xml:base="https://wassim31.github.io/2024/07/05/real-time-disk-size-expanding-in-a-virtual-machine-on-linux-machine.html"><![CDATA[<p>I was compiling the Linux kernel from source in a VM running AlmaLinux with a 20GB logical volume, and I reached the maximum size. I had to expand my disk size without losing my data, or the progress of a 12-hour compilation process (yes, heavy kernel and weak hardware).</p>

<p>This same technique is used in storage management by cloud service providers like AWS and Azure.</p>

<p><strong>Linux distribution:</strong> AlmaLinux 9.4 on VMware Workstation 7</p>

<p>When you expand the disk size of your Linux virtual machine on VMware, you must adjust the partitions within the system to utilize the additional space. This guide will walk you through the steps required to resize the partitions and filesystems using bash.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>Before you begin, ensure you have:</p>

<ul>
  <li>Expanded the disk size of your VM in VMware.</li>
  <li>Root or sudo access to your Linux VM.</li>
</ul>

<h2 id="step-1-identify-the-new-disk-size">Step 1: Identify the new disk size</h2>

<p>First, verify the current disk and partition sizes using the <code class="language-plaintext highlighter-rouge">lsblk</code> command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lsblk
</code></pre></div></div>

<p>The output will show all block devices and their partitions. Identify the disk you have expanded. For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NAME               MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sr0                 11:0    1  988M  0 rom  
nvme0n1            259:0    0  100G  0 disk 
├─nvme0n1p1        259:1    0    1G  0 part /boot
└─nvme0n1p2        259:2    0   19G  0 part 
  ├─almalinux-root 253:0    0   17G  0 lvm  /
  └─almalinux-swap 253:1    0    2G  0 lvm  [SWAP]
</code></pre></div></div>

<h2 id="step-2-resize-the-partition">Step 2: Resize the partition</h2>

<p>Use the <code class="language-plaintext highlighter-rouge">growpart</code> utility to resize the partition. If it’s not already installed, you can install it with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>cloud-utils-growpart
</code></pre></div></div>

<p>Then resize the partition <code class="language-plaintext highlighter-rouge">nvme0n1p2</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>growpart /dev/nvme0n1 2
</code></pre></div></div>

<h2 id="step-3-resize-the-physical-volume-pv">Step 3: Resize the physical volume (PV)</h2>

<p>Next, resize the physical volume to recognize the expanded partition:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pvresize /dev/nvme0n1p2
</code></pre></div></div>

<h2 id="step-4-verify-the-physical-volume-size">Step 4: Verify the physical volume size</h2>

<p>Verify the changes using the <code class="language-plaintext highlighter-rouge">pvs</code> command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pvs
</code></pre></div></div>

<h2 id="step-5-resize-the-logical-volume-lv">Step 5: Resize the logical volume (LV)</h2>

<p>Assuming you want to allocate all the new space to the root logical volume (<code class="language-plaintext highlighter-rouge">almalinux-root</code>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>lvextend <span class="nt">-l</span> +100%FREE /dev/almalinux/root
</code></pre></div></div>

<h2 id="step-6-determine-the-filesystem-type">Step 6: Determine the filesystem type</h2>

<p>Before resizing the filesystem, determine the filesystem type. You can do this with either of these commands:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">df</span> <span class="nt">-Th</span>
</code></pre></div></div>

<p>or</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lsblk <span class="nt">-f</span>
</code></pre></div></div>

<h2 id="step-7-resize-the-filesystem">Step 7: Resize the filesystem</h2>

<p>Depending on the filesystem type, use the appropriate command.</p>

<p>For ext4:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>resize2fs /dev/almalinux/root
</code></pre></div></div>

<p>For xfs:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>xfs_growfs /
</code></pre></div></div>

<h2 id="step-8-verify-the-changes">Step 8: Verify the changes</h2>

<p>Finally, verify that the new space is available:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lsblk
<span class="nb">df</span> <span class="nt">-h</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>By following these steps, you can effectively utilize the additional disk space after expanding your Linux VM disk on VMware, without losing any data or any running work.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><summary type="html"><![CDATA[I was compiling the Linux kernel in a VM with a full 20GB logical volume. Here is how to expand the disk without losing your data, step by step.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/face.jpg" /><media:content medium="image" url="https://wassim31.github.io/face.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">CAFE BABE, if you know, you know. JVM enthusiast.</title><link href="https://wassim31.github.io/2023/12/30/cafe-babe-if-you-know-you-know-jvm-enthusiast.html" rel="alternate" type="text/html" title="CAFE BABE, if you know, you know. JVM enthusiast." /><published>2023-12-30T22:04:20+00:00</published><updated>2023-12-30T22:04:20+00:00</updated><id>https://wassim31.github.io/2023/12/30/cafe-babe-if-you-know-you-know-jvm-enthusiast</id><content type="html" xml:base="https://wassim31.github.io/2023/12/30/cafe-babe-if-you-know-you-know-jvm-enthusiast.html"><![CDATA[<p>Each program has a magic word in its machine code representation that defines its identity among multiple other files.</p>

<h2 id="go-see-it-yourself">Go see it yourself</h2>

<p>If you open a compiled Java file, aka <code class="language-plaintext highlighter-rouge">file.class</code>, with a hex editor like the <code class="language-plaintext highlighter-rouge">xxd</code> tool in Linux, you’ll find the first hex words are:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CAFE BABE
</code></pre></div></div>

<h2 id="but-why-cafe-babe">But why CAFE BABE?</h2>

<p>Why is it so magical that it has to be added to each class file? That’s probably the most important question to be asked.</p>

<p>Well, James Gosling explained why:</p>

<blockquote>
  <p>We used to go to lunch at a place called St Michael’s Alley. According to local legend, in the deep dark past, the Grateful Dead used to perform there before they made it big. It was a pretty funky place that was definitely a Grateful Dead Kinda Place. When Jerry died, they even put up a little Buddhist-esque shrine. When we used to go there, we referred to the place as Cafe Dead. Somewhere along the line, it was noticed that this was a HEX number. I was re-vamping some file format code and needed a couple of magic numbers: one for the persistent object file, and one for classes. I used CAFEDEAD for the object file format, and in grepping for 4 character hex words that fit after “CAFE” (it seemed to be a good theme) I hit on BABE and decided to use it. At that time, it didn’t seem terribly important or destined to go anywhere but the trash can of history. So CAFEBABE became the class file format, and CAFEDEAD was the persistent object format. But the persistent object facility went away, and along with it went the use of CAFEDEAD. It was eventually replaced by RMI.</p>
</blockquote>

<p>So every Java class file in the world starts with the name of a cafe where the Grateful Dead used to play. If you know, you know.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><category term="java" /><category term="linux" /><category term="programming" /><category term="reverse" /><summary type="html"><![CDATA[Each program has a magic word in its machine code that defines its identity. For Java class files, it's CAFE BABE, and the story behind it is great.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/images/posts/cafe-babe-if-you-know-you-know-jvm-enthusiast/cover.png" /><media:content medium="image" url="https://wassim31.github.io/images/posts/cafe-babe-if-you-know-you-know-jvm-enthusiast/cover.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How your program’s functions are handled in memory?</title><link href="https://wassim31.github.io/2023/12/29/how-your-programs-functions-are-handled-in-memory.html" rel="alternate" type="text/html" title="How your program’s functions are handled in memory?" /><published>2023-12-29T15:40:54+00:00</published><updated>2023-12-29T15:40:54+00:00</updated><id>https://wassim31.github.io/2023/12/29/how-your-programs-functions-are-handled-in-memory</id><content type="html" xml:base="https://wassim31.github.io/2023/12/29/how-your-programs-functions-are-handled-in-memory.html"><![CDATA[<p>Because I always forget how the kernel is handling the program’s functions in the call stack, I’ll write about it so I can come back after months, or someone else will :)</p>

<p>So hello, let’s do it in C on an x86-64 bits machine. Why? Because it’s my favorite language.</p>

<h2 id="from-c-to-assembly">From C to assembly</h2>

<p>First, when you compile your C code:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-c</span> main main.c
</code></pre></div></div>

<p>you get an object file <code class="language-plaintext highlighter-rouge">main.o</code> that will be linked later (manually or dynamically) with other already compiled standard code (like your famous <code class="language-plaintext highlighter-rouge">printf()</code>), and you get an executable file, ELF64 in my case.</p>

<p>When you disassemble it, either with objdump or gdb, you can see the assembly code of your program:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gdb ./main
disassemble <span class="k">function</span>
</code></pre></div></div>

<p><img src="/images/posts/how-your-programs-functions-are-handled-in-memory/functions.png" alt="Disassembly of main and function showing the function prologue" /></p>

<p>You’ll find some sections like <code class="language-plaintext highlighter-rouge">.text</code>, <code class="language-plaintext highlighter-rouge">.bss</code> or <code class="language-plaintext highlighter-rouge">.data</code>, the heap, and the stack. These are the sections of the virtual address space of your program.</p>

<p>What concerns us is the <code class="language-plaintext highlighter-rouge">.text</code> section: it contains the code, the instructions to be executed. For example <code class="language-plaintext highlighter-rouge">int x = 3</code> will have the equivalent of <code class="language-plaintext highlighter-rouge">movl $0x3,-0x4(%rbp)</code>.</p>

<p>Okay, good. What concerns us now is how the functions <code class="language-plaintext highlighter-rouge">main()</code> and <code class="language-plaintext highlighter-rouge">function()</code> are handled in the stack, the section where the functions reside.</p>

<h2 id="the-registers-involved">The registers involved</h2>

<p>The CPU has some special registers called <code class="language-plaintext highlighter-rouge">%rbp</code> and <code class="language-plaintext highlighter-rouge">%rsp</code> (the base pointer and the stack pointer) to manipulate the scope of the functions:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">%rbp</code> is used to access the memory words by adding values to it, like the example we had: <code class="language-plaintext highlighter-rouge">movl $0x3,-0x4(%rbp)</code></li>
  <li><code class="language-plaintext highlighter-rouge">%rsp</code> always points to the top of the stack.</li>
</ul>

<h2 id="the-function-prologue">The function prologue</h2>

<p>Here is the process, step by step:</p>

<ol>
  <li>
    <p>The first thing we need to do is push the value of the old <code class="language-plaintext highlighter-rouge">%rbp</code>, because it’s used by other functions to do the same thing. In this case that’s the <code class="language-plaintext highlighter-rouge">main()</code> function, which called <code class="language-plaintext highlighter-rouge">function()</code>.</p>
  </li>
  <li>
    <p>Then we push the return address to the stack, so the <code class="language-plaintext highlighter-rouge">main()</code> function resumes its execution where it stopped (in this case it will return to line 13). This information is retrieved from register <code class="language-plaintext highlighter-rouge">r14</code> in ARM processors, for example.</p>
  </li>
  <li>
    <p>Then we’ll set the new value of the base pointer <code class="language-plaintext highlighter-rouge">%rbp</code> (the one we said is used for accessing memory words in the scope) to the value of <code class="language-plaintext highlighter-rouge">%rsp</code>, so the top of the stack.</p>
  </li>
</ol>

<p>This process of handling <code class="language-plaintext highlighter-rouge">%rbp</code> and <code class="language-plaintext highlighter-rouge">%rsp</code> is called the <strong>function prologue</strong>, and it’s done at each function start, as you can see in the assembly code.</p>

<p>This is how the initialization of the function scope is done, similarly across multiple architectures and systems.</p>

<h2 id="calling-conventions">Calling conventions</h2>

<p>Now we’ll use the function call stack by putting the local variables, the function parameters, etc. These specific details follow calling conventions:</p>

<p><a href="https://lnkd.in/eUNNAiXH">https://lnkd.in/eUNNAiXH</a></p>

<p>One of the conventions to follow on x64 architectures, for example, is <strong>System V</strong>. For function parameter handling, the registers are used like this: the first parameter is placed in <code class="language-plaintext highlighter-rouge">rdi</code>, the second in <code class="language-plaintext highlighter-rouge">rsi</code>, the third in <code class="language-plaintext highlighter-rouge">rdx</code>, and then <code class="language-plaintext highlighter-rouge">rcx</code>, <code class="language-plaintext highlighter-rouge">r8</code> and <code class="language-plaintext highlighter-rouge">r9</code>. Only the 7th argument and onwards are passed on the stack. The left most parameter is passed first on the stack, then the old value of the base pointer, and then it’s followed by the local variables of the function.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><category term="linux" /><category term="c" /><category term="x86" /><category term="memory" /><summary type="html"><![CDATA[Because I always forget how the kernel handles a program's functions in the call stack, I wrote it down. Function prologue, %rbp, %rsp, and calling conventions.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/images/posts/how-your-programs-functions-are-handled-in-memory/cover.png" /><media:content medium="image" url="https://wassim31.github.io/images/posts/how-your-programs-functions-are-handled-in-memory/cover.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What the heck is locality of reference, and why will I waste brain cells bytes to know it?</title><link href="https://wassim31.github.io/2023/09/25/what-the-heck-is-locality-of-reference-why-i-will-waste-brain-cells-bytes-to-know-it.html" rel="alternate" type="text/html" title="What the heck is locality of reference, and why will I waste brain cells bytes to know it?" /><published>2023-09-25T23:51:05+00:00</published><updated>2023-09-25T23:51:05+00:00</updated><id>https://wassim31.github.io/2023/09/25/what-the-heck-is-locality-of-reference-why-i-will-waste-brain-cells-bytes-to-know-it</id><content type="html" xml:base="https://wassim31.github.io/2023/09/25/what-the-heck-is-locality-of-reference-why-i-will-waste-brain-cells-bytes-to-know-it.html"><![CDATA[<p>Locality of Reference is a critical Computer Science topic, and it helps improve your software’s performance if you, the programmer, are aware of it.</p>

<p>Programs that tend to exhibit good locality are those constituted of components that tend to reference data items that are near other recently referenced data items, or that were recently referenced themselves.</p>

<h2 id="the-two-types-of-locality">The two types of locality</h2>

<ol>
  <li>
    <p><strong>Temporal locality</strong>: if a main memory word is referenced once, and there are potential references in the near future, it should be in the cache memory.</p>
  </li>
  <li>
    <p><strong>Spatial locality</strong>: if a memory location is referenced once, then the program is likely to reference a nearby memory location in the near future. A good example is a programming block which is logically distinguishable, that is: traversing a contiguous array of data using a loop statement.</p>
  </li>
</ol>

<h2 id="okay-thank-you-for-giving-me-information-i-can-get-from-wikipedia-how-the-heck-will-that-help-me-improve-my-programming-skills-and-performance">Okay, thank you for giving me information I can get from Wikipedia. How the heck will that help me improve my programming skills and performance?</h2>

<p>Answer: we have the following example that performs a matrix values sum.</p>

<p><img src="/images/posts/what-the-heck-is-locality-of-reference-why-i-will-waste-brain-cells-bytes-to-know-it/img1.png" alt="Row-wise vs column-wise matrix sum example" /></p>

<p>The <code class="language-plaintext highlighter-rouge">sumarraycols()</code> function in Figure 6.19(a) computes the same result as the <code class="language-plaintext highlighter-rouge">sumarrayrows()</code> function in Figure 6.18(a). The only difference is that we have interchanged the <code class="language-plaintext highlighter-rouge">i</code> and <code class="language-plaintext highlighter-rouge">j</code> loops.</p>

<p>But what impact does interchanging the loops have on locality?</p>

<p>The <code class="language-plaintext highlighter-rouge">sumarraycols()</code> function suffers from poor spatial locality because it scans the array column-wise instead of row-wise, since C arrays are laid out in memory row-wise.</p>

<p>The examples are retrieved from the book: Computer Systems - A Programmer’s Perspective.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><category term="softwareengineering" /><category term="cpu" /><category term="softwaredevelopment" /><summary type="html"><![CDATA[Locality of Reference is a critical Computer Science topic that helps improve your software's performance, if you're aware of it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/face.jpg" /><media:content medium="image" url="https://wassim31.github.io/face.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How the heck a C program is compiled from human comprehensible language to 0’s and 1’s</title><link href="https://wassim31.github.io/2023/02/28/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s.html" rel="alternate" type="text/html" title="How the heck a C program is compiled from human comprehensible language to 0’s and 1’s" /><published>2023-02-28T01:38:09+00:00</published><updated>2023-02-28T01:38:09+00:00</updated><id>https://wassim31.github.io/2023/02/28/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s</id><content type="html" xml:base="https://wassim31.github.io/2023/02/28/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s.html"><![CDATA[<p>You may have wondered how the programming language lexemes are turned into a series of 0’s and 1’s, aka binary files?</p>

<p>Let’s discover that together, step by step:</p>

<p><img src="/images/posts/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s/pic.png" alt="Compilation pipeline from source code to running process" /></p>

<p>Figure 2.11 - Book reference: Operating System Concepts.</p>

<h2 id="from-binary-on-disk-to-a-running-process">From binary on disk to a running process</h2>

<p>Usually programs, <code class="language-plaintext highlighter-rouge">a.out</code> or <code class="language-plaintext highlighter-rouge">program.exe</code> (games, word processors, web browsers…), reside on your disk as binary executable files. In order to execute one of them, you need to fetch it from disk and put it in memory, where it waits until it’s scheduled to run. It then becomes a running program, also known as a “process”, which has a specific amount of memory addresses and is executed by the units of the CPU’s core with the help of specific registers like program counters, IR, and general-purpose registers.</p>

<p>But we don’t write operating systems and enterprise systems in binary, right?</p>

<p>We need a human readable language. Let’s choose a compiled programming language like C to understand the process of compilation.</p>

<h2 id="compiling-and-assembling">Compiling and assembling</h2>

<p>After writing your <code class="language-plaintext highlighter-rouge">program.c</code> source code in human readable C, the source code is compiled and assembled into a relocatable object file <code class="language-plaintext highlighter-rouge">program.o</code>: machine code that can be disassembled and turned into geek-readable assembly. (We will talk about the phases of compilation, lexing, parsing, AST… in future articles.)</p>

<p>So we have now an object file. But we may have called several functions like <code class="language-plaintext highlighter-rouge">printf()</code>, which were also written by system programmers before us, to give us a layer of abstraction and increase our productivity. They are compiled, aka turned into an object file too, as part of GCC.</p>

<h2 id="linking">Linking</h2>

<p>We now need to link all those object files and make them a single binary executable file that can be loaded into memory. This mechanism is done by what we call a “linker”. (This is static linking.)</p>

<h2 id="loading">Loading</h2>

<p>The mechanism of loading the binary executable file into memory, so it can be executed by one of the CPU’s cores, is done by the loader. It’s also responsible for address reallocation in memory.</p>

<h2 id="dynamic-linking">Dynamic linking</h2>

<p>You may have heard about DLL (dynamically linked libraries) files in Windows, right? Now we speak about dynamic linking. Actually, most systems allow programs to dynamically link libraries even if the program is already loaded and started execution. That’s good from a memory optimization perspective, because the programmer may include a library and not use it during runtime. I am sure we all did that before.</p>

<p>And you know that modern systems allow multiple processes to share a DLL? Yes, they do!</p>

<p>And now you have a binary executable program in memory, also known as a process, with its own memory addresses.</p>

<p>I will talk deeper next time about the compilation phases, and how the CPU fetches, decodes, and executes instructions and data from the bounded virtual memory of the process.</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><summary type="html"><![CDATA[You may have wondered how programming language lexemes are turned into a series of 0's and 1's, aka binary files. Let's discover that together, step by step.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/images/posts/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s/cover.jpeg" /><media:content medium="image" url="https://wassim31.github.io/images/posts/how-the-heck-a-c-program-is-compiled-from-human-comprehensible-language-to-0s-and-1s/cover.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why the heck the _state variable in task_struct structure is 4 bytes</title><link href="https://wassim31.github.io/2023/02/28/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes.html" rel="alternate" type="text/html" title="Why the heck the _state variable in task_struct structure is 4 bytes" /><published>2023-02-28T01:34:32+00:00</published><updated>2023-02-28T01:34:32+00:00</updated><id>https://wassim31.github.io/2023/02/28/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes</id><content type="html" xml:base="https://wassim31.github.io/2023/02/28/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes.html"><![CDATA[<p>According to Robert Love, the author of the book Linux Kernel Development, and the Linux kernel source:</p>

<p>In the Linux kernel, specifically in the PCB (process control block), there is a structure that represents the process in the kernel: <code class="language-plaintext highlighter-rouge">task_struct</code>.</p>

<h2 id="the-six-state-flags">The six state flags</h2>

<p>The process state is represented by a combination of six state flags:</p>

<ul>
  <li><strong>TASK_RUNNING</strong>: the process is currently running or ready to run.</li>
  <li><strong>TASK_INTERRUPTIBLE</strong>: the process is waiting for a specific event to occur and can be interrupted by a signal.</li>
  <li><strong>TASK_UNINTERRUPTIBLE</strong>: the process is waiting for a specific event to occur and cannot be interrupted by a signal.</li>
  <li><strong>__TASK_STOPPED</strong>: the process has been stopped (e.g. by a SIGSTOP signal) and can be resumed later.</li>
  <li><strong>__TASK_TRACED</strong>: the process is being traced by another process (e.g. a debugger).</li>
  <li><strong>TASK_DEAD</strong>: the process has terminated and is waiting to be reaped by its parent process.</li>
</ul>

<h2 id="combining-flags">Combining flags</h2>

<p>Each of these flags can be set or cleared to represent the different states a process can be in. For example, a process that is both <strong>TASK_INTERRUPTIBLE</strong> and <strong>TASK_UNINTERRUPTIBLE</strong> might be waiting for a disk I/O operation to complete, and can be interrupted by a signal but cannot be killed until the I/O operation is finished.</p>

<p>In other words, a process can have multiple states at once, depending on which combination of flags is set.</p>

<h2 id="so-why-4-bytes">So why 4 bytes?</h2>

<p>This state is saved in an <strong>unsigned integer</strong>. That means 32 bits, which means 2³² possible values. But we only need 2⁶, so 64 possible states as a maximum.</p>

<p><img src="/images/posts/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes/img1.png" alt="The _state field in task_struct" /></p>

<p>The reason is the following:</p>

<p>There are some predefined functions that manage the process’s state, such as <strong>READ_ONCE/WRITE_ONCE</strong>, that oblige this variable to be an unsigned int. It used to be a volatile long. Here is the commit:</p>

<p><a href="https://github.com/torvalds/linux/commit/2f064a59a11ff9bc22e52e9678bc601404c7cb34#diff-f8d8a1568ae83bbff6f40f9c70559a4f7dbf426a397131ba9d4fbfb947ea5222R669">https://github.com/torvalds/linux/commit/2f064a59a11ff9bc22e52e9678bc601404c7cb34#diff-f8d8a1568ae83bbff6f40f9c70559a4f7dbf426a397131ba9d4fbfb947ea5222R669</a></p>

<p>Thank you for reading &lt;3</p>]]></content><author><name>Wassim Boussebha Abdessamed</name><email>wassim.boussebha@imt-atlantique.fr</email></author><summary type="html"><![CDATA[The Linux process state needs six flags, so why is it stored in an unsigned integer with 2^32 possible values? The answer is in READ_ONCE/WRITE_ONCE.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://wassim31.github.io/images/posts/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes/cover.jpeg" /><media:content medium="image" url="https://wassim31.github.io/images/posts/why-the-heck-the-state-variable-in-taskstruct-structure-is-4-bytes/cover.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>