Mind Dump on Ticki's blog
http://ticki.github.io/tags/mind-dump/index.xml
Recent content in Mind Dump on Ticki's blogHugo -- gohugo.ioen-usThe Trump Effect
http://ticki.github.io/blog/the-trump-effect/
Wed, 16 Nov 2016 00:00:00 +0000http://ticki.github.io/blog/the-trump-effect/
<p>So, many have noted the disparity in standards set for the candidates. Indeed, Trump has said and done certain things which would end any other campaign, yet it has had relatively small effect on Trump’s campaign.</p>
<p>This is my take on why.</p>
<h1 id="defining-the-trump-effect">Defining the Trump effect</h1>
<p>Here’s how I would define the Trump effect:</p>
<blockquote>
<p>The Trump effect is the phenomenon in which a group or individual lowers the standard such that diverging or abnormal behavior does not hurt one’s reputation to the same extent as other groups or individuals acting similarly.</p>
</blockquote>
<p>Essentially humans judge an individual based on their expected behavior, and if poor behavior is expected, it is naturally to assume they’re behaving well if they beat their expectation.</p>
<p>An analogy is children: No one expects a child to behave like an adult, but if they do, they have overperformed their expected behavior.</p>
<h1 id="trump-s-campaign">Trump’s campaign</h1>
<p>When Trump announced his campaign, it was declared a joke. Joke candidates are expected to behave so, and thus it is no surprise when they do.</p>
<p>Trump has consistently behaved unpresidential, but the damage done by such behavior was relatively limited.</p>
<p>As an example, take Trump mocking a disabled reporter:</p>
<p><img src="http://ticki.github.io/thumb/the-trump-effect.png" alt="Image of Trump's imitation of the reporter" /></p>
<p>In any other campaign, this would have severely damaged it, or even ended it. With Trump, it didn’t have this effect, at all.</p>
<p>The second example is his comments on a woman accusing him of rape:</p>
<blockquote>
<p>Take a look. You look at her, look at her words. You tell me what you think. I don’t think so. I don’t think so.</p>
</blockquote>
<p>It is quite clear what he tries to say here: The woman in question apparently wasn’t attractive enough for him to rape.</p>
<p>These comments are absolutely terrifying, but did they end his campaign? No, they didn’t.</p>
<h2 id="the-myth-of-nature-defying-trump">The myth of nature defying Trump</h2>
<p>To be clear, they did hurt him, but they didn’t nearly do it as strongly as it would for any other campaign.</p>
<p>Nate Silver did a quite interesting article on this <a href="https://fivethirtyeight.com/features/trump-isnt-teflon/">“Trump isn’t teflon”</a>.</p>
<p>Trump’s action tends to hurt him, and not the opposite, as some have claimed. But there is a major difference in the degree to which the news affects the campaign.</p>
<h2 id="overperforming-the-expectations">Overperforming the expectations</h2>
<p>The week when <a href="http://edition.cnn.com/videos/politics/2016/08/31/donald-trump-in-mexico-murray-dnt-tsr.cnn">Trump visited Mexico</a>, he performed exceptionally well in the polls, even beating Clinton.</p>
<p>But why?</p>
<p>He didn’t did the exceptional thing of acting presidential and normal. Overperforming the expectations makes people greatly exaggerate the valuation.</p>
<p>Hillary Clinton was expected to behave presidentially, Trump isn’t. As such, even a single week of acting normally results in a massive upswing in the polls.</p>
<h2 id="compare-to-howard-dean">Compare to Howard Dean</h2>
<p>Howard Dean ran in 2004. For comparison, his campaign was declared ended after his infamous awkward speech <a href="https://www.youtube.com/watch?v=l6i-gYRAwM0">“Dean’s scream speech”</a>.</p>
<p>The fact that this over-enthusiastic speech could end his campaign is insane when you look it in comparison to the Trump candidacy.</p>
<h1 id="lowering-the-standard">Lowering the standard</h1>
<p>The question is just: how is the low standard achieved?</p>
<p>One could say that all that was need is to consistently act poorly, but I doubt this is enough.</p>
<p>If one consistently acts poorly and then apologizes for it, they don’t etablish a low standard, because they admit they were underperforming themself.</p>
<p>Compare Governor of Maine, Paul LePage, to president-elect, Donald Trump. LePage consistently acts poorly, like Trump. But he is still widely unpopular (in fact, very much so, he has historically low approval ratings), and he is still put to a high standard.</p>
<p>Why?</p>
<p>LePage apologizes for his actions, and I think this is where they differ. Trump never apologizes for his actions, as such he never admits actually being wrong and thus pushing up the standards again (“I didn’t mean to do this”).</p>
On Random-Access Compression
http://ticki.github.io/blog/on-random-access-compression/
Sun, 23 Oct 2016 23:25:15 +0200http://ticki.github.io/blog/on-random-access-compression/<script type="text/javascript"
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<p>This post will contains an algorithm I came up with, doing efficient rolling compression. It's going to be used in <a href="https://github.com/ticki/tfs">TFS</a>.</p>
<h1 id="what-is-rolling-compression">What is rolling compression?</h1>
<p>Consider that you have a large file and you want to compress it. That's easy enough and many algorithms exists for doing so. Now, consider that you want to read or write a small part of the file.</p>
<p>Most algorithms would require you to decompress, write, and recompress the whole file. Clearly, this gets expensive when the file is big.</p>
<h1 id="clusterbased-compression">Cluster-based compression</h1>
<p>A cluster is some small fixed-size block (often 512, 1024, or 4096 bytes). We can have a basic cluster allocator by linking unused clusters together. Cluster-centric compression is interesting, because it can exploit the allocator.</p>
<p>So, the outline is that we compress every <span class="math">\(n\)</span> adjacent clusters to some <span class="math">\(n' < n%>\)</span>, then we can free the excessive clusters in this compressed line.</p>
<h1 id="copyonwrite">Copy-on-write</h1>
<p>Our algorithm is not writable, but it can be written by allocating, copying, and deallocating. This is called copy-on-write, or COW for short. It is a common technique used in many file systems.</p>
<p>Essentially, we never write a cluster. Instead, we allocate a new cluster, and copy the data to it. Then we deallocate the old cluster.</p>
<p>This allows us to approach everything much more functionally, and we thus don't have to worry about make compressible blocks uncompressible (consider that you overwrite a highly compressible cluster with random data, then you extend a physical cluster containing many virtual clusters, these wouldn't be possible to have in one cluster).</p>
<h1 id="physical-and-virtual-clusters">Physical and virtual clusters</h1>
<p>Our goal is really fit multiple clusters into one physical cluster. Therefore, it is essential to distinguish between physical (the stored) and virtual (the compressed) clusters.</p>
<p>A physical cluster can contain up to 8 virtual clusters. A pointer to a virtual cluster starts with 3 bits defining the index into the physical cluster, which is defined by the rest of the pointer.</p>
<p>The allocated physical cluster contains 8 bitflags, defining which of the 8 virtual clusters in the physical cluster are used. This allows us to know how many virtual clusters we need to go over before we get the target decompressed cluster.</p>
<p>When the integer hits zero (i.e. all the virtual clusters are freed), the physical cluster is freed.</p>
<p>Since an active cluster will never have the state zero, we use this blind state to represent an uncompressed physical cluster. This means we maximally have one byte in space overhead for uncompressible clusters.</p>
<p><figure><img src="http://ticki.github.io/img/virtual_physical_random_access_compression_diagram.svg" alt="A diagram"></figure></p>
<h1 id="the-physical-cluster-allocator">The physical cluster allocator</h1>
<p>The cluster allocator is nothing but a linked list of clusters. Every free cluster links to another free cluster or NIL (no more free clusters).</p>
<p>This method is called SLOB (Simple List Of Objects) and has the advantage of being complete zero-cost in that there is no wasted space.</p>
<p><figure><img src="http://ticki.github.io/img/slob_allocation_diagram.svg" alt="Physical allocation is simply linked list of free objects."></figure></p>
<h1 id="the-virtual-cluster-allocator">The virtual cluster allocator</h1>
<p>Now we hit the meat of the matter.</p>
<p>When virtual cluster is allocated, we read from the physical cluster list. The first thing we will check is if we can fit in our virtual cluster into the cluster next to the head of the list (we wrap if we reach the end).</p>
<p>If we can fit it in <em>and</em> we have less than 8 virtual clusters in this physical cluster, we will put it into the compressed physical cluster at the first free virtual slot (and then set the respective bitflag):</p>
<p><figure><img src="http://ticki.github.io/img/allocating_compressed_virtual_page_into_next_diagram.svg" alt="We try to fit it into the next cluster."></figure></p>
<p>If we cannot, we pop the list and use the fully-free physical cluster to store etablish a new stack of virtual clusters. It starts as uncompressed:</p>
<p><figure><img src="http://ticki.github.io/img/pop_and_create_new_uncompressed_cluster_diagram.svg" alt="We pop the list and put the virtual cluster in the physical uncompressed slot."></figure></p>
<h1 id="properties-of-this-approach">Properties of this approach</h1>
<p>This approach to writable random-access compression has some very nice properties.</p>
<h2 id="compression-miss">Compression miss</h2>
<p>We call it a compression miss when we need to pop from the freelist (i.e. we cannot fit it into the cluster next to the head). When you allocate you can maximally have one compression miss, and therefore allocation is constant-time.</p>
<h2 id="every-cluster-has-a-sister-cluster">Every cluster has a sister cluster</h2>
<p>Because the "next cluster or wrap" function is bijective, we're sure that we try to insert a virtual cluster to every cluster at least once. This wouldn't be true if we used a hash function or something else.</p>
<p>This has the interesting consequence that filled clusters won't be tried to allocate in multiple times.</p>
<h1 id="limitations">Limitations</h1>
<p>A number of limitations are in this algorithms. The first and most obvious one is the limitation on the compression ratio. This is a minor one: it limits the ratio to maxmially slightly less than 1:8.</p>
<p>A more important limitation is fragmentation. If I allocate many clusters and then deallocate some of them such that many adjacent physical clusters only contain one virtual cluster, this row will have a compression ratio of 1:1 until they're deallocated. Note that it is very rare that this happens, and will only marginally affect the global compression ratio.</p>
<h1 id="update-an-idea">Update: An idea</h1>
<p>A simple trick can improve performance in some cases. Instead of compressing all the virtual clusters in a physical cluster together, you should compress each virtual cluster seperately and place them sequentially (with some delimiter) in the physical cluster.</p>
<p>If your compression algorithm is streaming, you can much faster iterate to the right delimiter, and then only decompress that virtual cluster.</p>
<p>This has the downside of making the compression ratio worse. One solution is to have an initial dictionary (if using a dictionary-based compression algorithm).</p>
<p>Another idea is to eliminate the cluster state and replace it by repeated delimiters. I need to investigate this some more with benchmarks and so on in order to tell if this is actually superior to having a centralized cluster state.</p>
A Hoare Logic for Rust
http://ticki.github.io/blog/a-hoare-logic-for-rust/
Sat, 24 Sep 2016 00:00:00 +0000http://ticki.github.io/blog/a-hoare-logic-for-rust/<script type="text/javascript"
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<p>Lately, I've been working on a Hoare-logic-based model of the Rust MIR, which I will introduce in the post. This is a minor step towards a memory model of Rust, and it allows formalization of programs and their behavior.</p>
<p>This project was born out of the effort to formalize <a href="https://github.com/redox-os/redox">the Redox kernel</a> and <a href="https://github.com/redox-os/ralloc/tree/skiplist">the ralloc memory allocator</a> as well as coming up with a <a href="https://github.com/rust-lang/rfcs/issues/1447">Rust memory model</a>.</p>
<p>Here I will walk through the techniques, axioms, and transformations in detail. I've divided this post into three parts:</p>
<ol>
<li>An introduction to Hoare logic: An gentle introduction for the beginners (can be skipped if you're already familiar with Hoare logic).</li>
<li>Applying Hoare logic to the Rust MIR: Notably dropping structured programming in favour of a lower-level goto-based representation, and how it helps simplifying certain things.</li>
<li>Reasoning about pointers: Pointers are notoriously hard to reason about. Here we try to formalize their behavior and give various insight on how they can be reasoned about. Priory to this part, we assume that pointers doesn't exist.</li>
</ol>
<p>This blog post is not a formal specification or a paper, but rather a mere introduction to the subject and proposed axioms.</p>
<p><em>If the math doesn't show up properly, reload the page.</em></p>
<h1 id="an-introduction-to-hoare-logic">An introduction to Hoare logic</h1>
<p>So, what is Hoare logic? Well, it's a set of axioms and inference rules allowing one to reason about <em>imperative programs</em> in a rigorous manner.</p>
<p>The program is divided into so called <strong>Hoare triples</strong>, denoted <span class="math">\(\{P\} \ S \ \{Q\}\)</span>. <span class="math">\(P\)</span> is called the "precondition". Informally, if <span class="math">\(P\)</span> is satisfied, then after <span class="math">\(S\)</span> (the statement or instruction) has been executed, <span class="math">\(Q\)</span> (the postcondition) should be true. In other words, <span class="math">\(P\)</span> is true before <span class="math">\(S\)</span>, and <span class="math">\(Q\)</span> should be true after.</p>
<p>In fact, we can view <span class="math">\(S\)</span> as a function on the state space, going from <span class="math">\(\sigma\)</span> satisfying property <span class="math">\(P(\sigma)\)</span> to a state <span class="math">\(S(\sigma) = \sigma'\)</span> satisfying the postcondition, <span class="math">\(Q(\sigma')\)</span>.</p>
<p>Thus a Hoare triple can be seen as a 3-tuple</p>
<p><span class="math">\[(P, f, Q)\]</span></p>
<p>satisfying:</p>
<p><span class="math">\[P(\sigma) \to Q(f(\sigma))\]</span></p>
<p>It turns out that this interpretation is a strong one, and we will use it throughout the post to derive the Hoare rules, some of which follows directly from this interpretation.</p>
<p><span class="math">\[\frac{\forall \sigma.P(\sigma) \to Q(f(\sigma))}{\{P\}\ f\ \{Q\}} \qquad \frac{\{P\}\ f\ \{Q\}}{\forall \sigma.P(\sigma) \to Q(f(\sigma))}\]</span></p>
<p>(on a side note, this notation should be understood as: what is below the line is true if what is above is true)</p>
<h2 id="an-example">An example</h2>
<p>Suppose we have the program,</p>
<pre><code class="language-rust">// a = 4
a += 2;
// a = 6
</code></pre>
<p>This is expressed by the Hoare triple</p>
<p><span class="math">\[\{a = 4\} \ a \gets a + 2 \ \{a = 6\}\]</span></p>
<p>So far, we have only introduced the notation, which in itself is worthless, what's really the core is the rules that allows us to reason about valid Hoare triples. We need a way to essentially construct new Hoare triples from old ones.</p>
<h2 id="rules-and-axioms">Rules and axioms</h2>
<h3 id="empty-statement-rule">Empty statement rule</h3>
<p>The empty statement rule states that: Let <span class="math">\(S\)</span> be any statement which carries no side-effect, then <span class="math">\(\{P\} \ S \ \{P\}\)</span>, or in inference line notation:</p>
<p><span class="math">\[\frac{S \text{ is pure}}{\{P\} \ S \ \{P\}}\]</span></p>
<p>This rule is relatively simple: If the state is not changed, the invariants are neither. Note that this is only true for effect-less statements, since the statement could otherwise change variables or in other ways invalidate the postcondition.</p>
<p>In fact, we can express it in terms of the identity function, <span class="math">\(f(x)=x\)</span>. Then,</p>
<p><span class="math">\[P(x) \to P(f(x)) = P(x)\]</span></p>
<p>Hence, the triple is valid.</p>
<h3 id="composition-rule">Composition rule</h3>
<p>The composition rule allows you to concatenate two statements (into a Hoare triple) if the first statement's postcondition is equal to the second statement's precondition:</p>
<p><span class="math">\[\frac{\{P\}\ S\ \{Q\}, \quad \{Q\}\ T\ \{R\}}{\{P\}\ S;T\ \{R\}}\]</span></p>
<p>It is left as an exercise for the reader to verify the correctness of the rule above.</p>
<h3 id="strengthening-and-weakening-conditions">Strengthening and weakening conditions</h3>
<p><span class="math">\[\frac{P_1 \to P_2,\quad \{P_2\}\ S\ \{Q_2\},\quad Q_2 \to Q_1}{\{P_1\}\ S\ \{Q_1\}}\]</span></p>
<p>So, what's going on here? Well, <span class="math">\(P_1\)</span> implies <span class="math">\(P_2\)</span>, so we can replace the precondition by a stronger version which implies the old one. The same cannot be applied to postcondition, because the strengthened precondition might not yield the strengthened postcondition after the statement. We can however replace it by a weaker postcondition (i.e. one which is implied by original postcondition).</p>
<p>We can always weaken guarantees, but never assumptions, since the assumption is what the guarantee relies on. Assumptions can be made stronger, however.</p>
<p>It is left as an exercise for the reader to verify the correctness of the rule above.</p>
<h3 id="the-assignment-axiom">The assignment axiom</h3>
<p>This axiom is the most important. It allows for reasoning about preconditions in the case of assignments. It is absolutely essential to Hoare logic.</p>
<p><span class="math">\[\frac{}{\{P[x \gets E]\}\ x \gets E\ \{P\}}\]</span></p>
<p><span class="math">\(P[x \gets E]\)</span> denotes replacing every free (unbound) <span class="math">\(x\)</span> with <span class="math">\(E\)</span>.</p>
<p>Let's say <span class="math">\(P\)</span> involves some assertion about <span class="math">\(x\)</span>, then we can move it over the assignment (to the precondition) replacing <span class="math">\(x\)</span> with the right-hand-side of the assignment, because every occurence of <span class="math">\(x\)</span> represents said value anyway, so substituting the value <span class="math">\(x\)</span> represents for <span class="math">\(x\)</span> won't change the structure.</p>
<p>Let's say we have the statement, <span class="math">\(x \gets x + 2\)</span>, with the postcondition <span class="math">\(\{x = 6\}\)</span>, we can then derive the Hoare triple:</p>
<p><span class="math">\[\{x + 2 = 6\}\ x \gets x + 2\ \{x = 6\}\]</span></p>
<p>One thing that is surprising, but also incredibly important, is that you substitute it into the precondition and not the postcondition. To see why such a rule (<span class="math">\(\{P\}\ x \gets E\ \{P[x \gets E]\}\)</span>) would be wrong, observe how you could derive <span class="math">\(\{x = 1\}\ x \gets 2\ \{2 = 1\}\)</span>, which is clearly false.</p>
<p>It is also worth noting that, in this context, expressions cannot carry side-effects. We'll cover this in detail in part two.</p>
<h3 id="conditional-rule">Conditional rule</h3>
<p>So far, we have only covered a simple language without loops, conditionals, and other forms of branches.</p>
<p>The first (and simplest) form of branches is a conditional non-cyclic branch (<code>if</code>). These behaves in a very simple way:</p>
<p><span class="math">\[\frac{\{C \land P\}\ B\ \{Q\},\quad \{\neg C \land P\}\ E\ \{Q\}}{\{P\}\ \textbf{if } C \textbf{ then } B \textbf{ else } E \textbf{ end}\ \{Q\}}\]</span></p>
<p>As complex this looks, it's actually relatively simple:</p>
<ol>
<li>In your <code>if</code> statement's body, you can safely assume the <code>if</code> condition to be true.</li>
<li>If both branches shares their postcondition (<span class="math">\(Q\)</span>), then the <code>if</code> statement does as well.</li>
</ol>
<p>As an example, consider the code,</p>
<pre><code class="language-rust">if x == 4 {
// I can safely assume that x = 4 here.
...
x = 2;
// Now x = 2.
} else {
// I can safely assume that x ≠ 4 here.
...
x = 2;
// Now x = 2.
}
// All branches share postcondition, so the whole if-statement does as well: x = 2
</code></pre>
<h3 id="the-loop-rule">The loop rule</h3>
<p>The loop rule reads,</p>
<p><span class="math">\[\frac{\{I \land C\}\ B\ \{I\}}{\{I\}\ \textbf{while } C \textbf{ do } B \textbf{ done}\ \{I \land \neg C\}}\]</span></p>
<p><span class="math">\(I\)</span> is called the <em>loop invariant</em>, i.e. the condition which is true before and after the loop. The loop will terminate when <span class="math">\(\neg C\)</span>, hence the postcondition of the loop.</p>
<p>As a simple example, take the following code:</p>
<pre><code class="language-rust">let mut x = 3;
let mut y = 4;
// Precondition: x == 3 (loop invariant)
while y < 100 {
// Precondition: y < 100 && x == 3
y += 1;
// Posttcondition: x == 3 (loop invariant)
}
// Postcondition: !(y < 100) ⇒ y >= 100
</code></pre>
<h1 id="applying-hoare-logic-to-the-mir">Applying Hoare logic to the MIR</h1>
<p>The Rust MIR is in many ways an interesting language. It can be seen as an extremely stripped-down version of Rust. What we'll work with is the MIR from the last compiler pass.</p>
<h2 id="the-rust-mir">The Rust MIR</h2>
<p>The Rust MIR has no structural control flow. It directly resembles the CFG of the program.</p>
<p>There are three concepts we must be familiar with to understand the Rust MIR:</p>
<ol>
<li><strong>Functions</strong>: A graph.</li>
<li><strong>Basic blocks</strong>: The nodes in the graph.</li>
<li><strong>Terminators</strong>: The edges in the graph.</li>
</ol>
<p>We'll not get into the representation of scopes and type information the MIR contains.</p>
<h3 id="functions">Functions</h3>
<p>Taking aside the type information, functions have two components: A set of variables and a Control Flow Graph.</p>
<p>The function starts with a bunch of variable declarations (arguments, temporaries, and variables). There's one implicit variable, the <code>return</code> variable, which contains the return values.</p>
<p>Secondly, there's a set of basic blocks, as well as a starting block.</p>
<h3 id="basic-blocks">Basic blocks</h3>
<p>Basic blocks are the nodes of the CFG. They each represent a series of statements. In our model, we can wlog. assume that a statement is simply an assignment, <span class="math">\(x \gets y\)</span>, where <span class="math">\(y\)</span> is an operand. In other words, a basic block is of the form <span class="math">\((x_1 \gets y_1; x_2 \gets y_2; \ldots; x_n \gets y_n, t)\)</span> with <span class="math">\(t\)</span> being the terminator.</p>
<p>In fact, we can go even further: A statement is a single assignment. This can be shown by simply constructing a map between the two graphs (by using the goto terminator to chain).</p>
<p>Note that there are two kinds of assignments. Up until now, we have only considered the <em>simple assignment</em> <span class="math">\(x \gets y\)</span> with <span class="math">\(y\)</span> being a simple, effectless expression. There's actually a second form of assignment, the function call assignment, <span class="math">\(x \gets f(y)\)</span>.</p>
<p>In such an assignment, the function can change the state of the program, and thus care must be taken, since you cannot always use the assignment axiom. We'll get back to that later on.</p>
<h3 id="terminators">Terminators</h3>
<p>Terminators are what binds basic blocks together. Every basic block has an associated terminator, which takes one of the following forms:</p>
<ol>
<li>Return from the current function: <span class="math">\(\textbf{return}\)</span>. The return value is stored in the <code>return</code> variable.</li>
<li>Calling a diverging function ("transferring" to the function), <span class="math">\(f(x)\)</span>.</li>
<li>Non-conditionally jumping to another block <span class="math">\(\textbf{goto}(b)\)</span>.</li>
<li>Jumping to another block if a condition is true, <span class="math">\(\textbf{if}_c(b_1, b_2)\)</span>.</li>
</ol>
<p>(there is a few - in the implementation - we ignore in our model for simplification purposes)</p>
<p>Notice how none of these are structural. All are based around gotos. Not only does this simplify our analysis, but it's also more near to the machine representation.</p>
<p>As an example, let's write a program that finds the 10th Fibonacci number:</p>
<p><figure><img src="https://i.imgur.com/gk6b2ZQ.png" alt="Tenth Fibonacci number"></figure></p>
<p>First of all, the program starts by assigning starting values. Then it enters a loop with a conditional branch in the end (is 10 reached yet?). In this loop we do the classic, add the two numbers and shift one down. When the loops ends, we assign the return value, and then return from the function.</p>
<h2 id="reasoning-about-the-mir">Reasoning about the MIR</h2>
<h3 id="unconditional-gotos">Unconditional gotos</h3>
<p>The first rule is the non-structural equivalent of the composition rule. All it says is that for a goto-statement to be valid, the precondition of the target basic block must be true:</p>
<p><span class="math">\[\frac{\{P\}\ b_1\ \{Q\}, \quad \{Q\}\ b_2\ \{R\}}{\{P\}\ b_1; \textbf{goto}(b_2)\ \{R\}}\]</span></p>
<h3 id="conditional-gotos">Conditional gotos</h3>
<p>Conditional gotos are interesting in that it allows us to reason about both while-loops and if-statements in only run rule.</p>
<p><span class="math">\[\frac{\{P \land C\}\ b_1\ \{Q\},\quad \{P \land \neg C\}\ b_2\ \{Q\}}{\{P\}\ \textbf{if}_C(b_1, b_2)\ \{Q\}}\]</span></p>
<p>It is the non-structural equivalent of the conditional rule, we described earlier.</p>
<h3 id="function-calls">Function calls</h3>
<p>Functions take the form <span class="math">\(f(x) \stackrel{\text{def}}{=} \{P(x)\}\ b\ \{Q(x)\}\)</span>, i.e. an initial starting block, <span class="math">\(b\)</span>, and a precondition and postcondition, respectively.</p>
<p>The rule of correctness for function calls reads,</p>
<p><span class="math">\[\frac{f(x) = \{P(x)\}\ b\ \{Q(x, \textbf{return})\}}{\{P(y) \land R[x \gets f(y)]\}\ x \gets f(y)\ \{Q(y, x) \land R\}}\]</span></p>
<p>This one is a big one. Let's break it up:</p>
<ul>
<li>The assumption (above the inference line) states that <span class="math">\(f(x)\)</span> is a Hoare triple with the precondition and postcondition being terms depending on the argument.</li>
<li>The postcondition depends on the return value of <span class="math">\(f(x)\)</span> as well.</li>
<li>The conclusion (below the inference line) consists of a Hoare triple with an assignment to <span class="math">\(x\)</span>.</li>
<li>The postcondition of the assignment is <span class="math">\(Q(y, x)\)</span> which express that the return value of the function is assigned to <span class="math">\(x\)</span>, and the argument is <span class="math">\(y\)</span>. This is logically joined with <span class="math">\(R\)</span>, which is carried over to the other side:</li>
<li>The precondition consists of <span class="math">\(R[x \gets f(y)]\)</span>, in a similar manner to the assignment axiom, as well as <span class="math">\(P(y)\)</span>, the precondition of the function.</li>
</ul>
<p>Note that this rule will be modified later when we introduce pointers into our model.</p>
<p>Take this imaginary program:</p>
<pre><code class="language-rust">fn subtract_without_overflow(a: u32, b: u32) -> u32 {
// Precondition: b ≤ a
a - b
// Postcondition: return ≤ a
}
fn main() {
let mut n = 0;
let mut res;
while n < 10 {
res = subtract_without_overflow(10, n);
// Postcondition: res < 10 (this is what we're going to prove!)
n += 1;
}
}
</code></pre>
<p>We know here that the condition for the loop is <span class="math">\(x < 10\)</span>, as such we set:</p>
<p><span class="math">\[\begin{align*}
x &= \mathtt{res}\\
y &= (10, n)\\
R &= [\mathtt{res} < 10]\\
P((a, b)) &= [b \leq a]\\
Q((a, b), r) &= [r \leq a]
\end{align*}\]</span></p>
<p>Plug it all in, and get:</p>
<p><span class="math">\[\frac{f(a, b) = \{n \leq a\}\ S\ \{f(a, b) \leq a\}}{\{n \leq 10 \land f(10, n) < 10\}\ x \gets f(10, n)\ \{f(10, n) \leq 10 \land \mathtt{res} < 10\}}\]</span></p>
<p>The desired result is obtained: the precondition implies that <span class="math">\(n < 10\)</span>, which is also the loop condition.</p>
<!-- -->
<p>Thus, we can conclude that there is no overflow in the program. Cool, no?</p>
<h3 id="dont-repeat-yourself">Don't repeat yourself!</h3>
<p>The rest of the rules are exactly matching the "classical" Hoare logic axioms. In other words, the assignment axiom, skip axiom, and consequence axiom remains unchanged.</p>
<h1 id="reasoning-about-pointers">Reasoning about pointers</h1>
<p>This is a tricky subject. Pointers are notorious for being hard to reason about. In fact, they are probably the single hardest subject in program verification.</p>
<h2 id="approach-1-global-reasoning">Approach 1: Global reasoning</h2>
<p>We could simply consider memory as one big array, in which pointers are indexes, but it turns out such a model is not only non-local, but also very messy, as such we need to derive a more expressive and convenient model to be able to reason about pointers without too much hassle.</p>
<h2 id="approach-2-relational-alias-analysis">Approach 2: Relational alias analysis</h2>
<p>To start with, I'll introduce a model I call "relational alias analysis". We define an equivalence relation, <span class="math">\(\sim\)</span>, on the set of variables. This equivalence relation tells if two variables are <em>aliased</em> (i.e. pointers to the same location).</p>
<h3 id="aliasing-variables">Aliasing variables</h3>
<p>The first axiom reads,</p>
<p><span class="math">\[\frac{\{x \sim y\}}{\{x = y\}}\]</span></p>
<p>i.e. if two variables are aliased, they're equal.</p>
<p>This is perhaps more of a definition than an axiom. None the less, it describes the semantics of our alias relation.</p>
<p>Then we describe the behavior of alias asignments:</p>
<p><span class="math">\[\frac{}{\{A = \textbf{alias}(a)\}\ a \stackrel{\text{alias}}{\gets} b\ \{\textbf{alias}(a) = A \cup \{b\}\}}\]</span></p>
<p>(<span class="math">\(\textbf{alias}(x)\)</span> defines the equivalence class of <span class="math">\(x\)</span> under <span class="math">\(\sim\)</span>)</p>
<p>This allows for declaring a variable to be aliased with another variable.</p>
<h3 id="assignment-axiom-for-aliased-values">Assignment axiom for aliased values</h3>
<p>Preconditions and postconditions can contain statements on the value behind the pointer, which has the unfortunate consequence that the old assignment axiom schema is no longer valid.</p>
<p>In fact, we simply need to observe that previously, we had <span class="math">\(\textbf{alias}(x) = \{x\}\)</span>. Now that we introduced aliased values, the situation changed, and the equivalence class can be arbitrarily large.</p>
<p>We put,</p>
<p><span class="math">\[\frac{}{\{P[\textbf{alias}(x) \gets E]\}\ x \gets E\ \{P\}}\]</span></p>
<p>Note that <span class="math">\(P[A \gets E]\)</span> means that we replace every element <span class="math">\(a \in A\)</span> with <span class="math">\(E\)</span>.</p>
<p>In other words, we do the same as before except that we assign the value to <em>all</em> the aliased variables.</p>
<h3 id="insufficiency">Insufficiency</h3>
<p>This model allows reasoning about aliases, but <em>not</em> pointers in general. In fact, it cannot reason about <code>noalias</code> pointers, deallocation, and pointer arithmetics.</p>
<h2 id="approach-3-separation-logic">Approach 3: Separation logic</h2>
<p>Separation logic was originally introduced by JC Reynolds in one of the most cited program verification papers ever. It is more complex than the alternative model we just presented, but also more expressive in some cases.</p>
<h3 id="modeling-memory">Modeling memory</h3>
<p>Our model of memory consists of multiple new notations. First of all, the model becomes memory aware. We use <span class="math">\(p \mapsto x\)</span> to denote that some pointer, <span class="math">\(p\)</span>, maps to the value <span class="math">\(x\)</span>.</p>
<p>We use the notation <span class="math">\(\mathcal{H}(p)\)</span> to denote pointer reads. The reason we keep the notation function-like is because it is, in fact, just a function! It simply maps pointers to values. We can define,</p>
<p><span class="math">\[\frac{p \mapsto x}{\mathcal{H}(p) = x}\]</span></p>
<p>We denote pointer writes by <span class="math">\(p \stackrel{\text{ptr}}{\gets} x\)</span>.</p>
<h4 id="disjointness">Disjointness</h4>
<p>The first feature of separation logic is the notion of "separate conjunction", denotes <span class="math">\(P * Q\)</span>.</p>
<p>This asserts that <span class="math">\(P\)</span> and <span class="math">\(Q\)</span> are both true and independent, i.e. their "heaps" are disjointed and not affected by the statement of the Hoare triple. In particular, let <span class="math">\(A\)</span> be the domain of <span class="math">\(\mathcal{H}\)</span>, then let <span class="math">\(\{A_1, A_2\}\)</span> be some semipartition of <span class="math">\(A\)</span> (<span class="math">\(A_1 \cap A_2 = \emptyset\)</span>), then put <span class="math">\(A_1 = \textbf{ref}(P)\)</span> and <span class="math">\(A_b = \textbf{ref}(Q)\)</span> (<span class="math">\(\textbf{ref}(P)\)</span> denotes all the locations that are referenced in <span class="math">\(P\)</span>, e.g. <span class="math">\(\textbf{ref}([\mathcal{H}(x) = 3]) = \{x\}\)</span>)</p>
<p>We can then put <span class="math">\(P * Q\)</span>. This might seem useless at first (how much different from <span class="math">\(\land\)</span> is it?), but it is incredibly important: If <span class="math">\(P\)</span> and <span class="math">\(Q\)</span> are dependent, not by sharing a free variable, but instead share a variable through aliasing (say <span class="math">\(P\)</span> has <span class="math">\(x\)</span> free and <span class="math">\(Q\)</span> has <span class="math">\(y\)</span> free, and <span class="math">\(x \sim y\)</span>).</p>
<p>All this will be formally defined in the next subsection.</p>
<h4 id="the-frame-rule">The frame rule</h4>
<p>The frame rule is the most important component of separation logic. It reads,</p>
<p><span class="math">\[\frac{\textbf{mut}(C) \cap \textbf{free}(R) = \emptyset,\quad \{P\}\ C\ \{Q\}}{\{P * R\}\ C\ \{Q * R\}}\]</span></p>
<p><span class="math">\(\textbf{mut}(C)\)</span> means the set of variables <span class="math">\(C\)</span> "mutates" (changes) when executed. For example, <span class="math">\(\textbf{mut}(a \gets b) = \{a\}\)</span>.</p>
<p>What the rule says is that if <span class="math">\(C\)</span> never changes the "environment" from <span class="math">\(R\)</span>, then you can safely join the precondition and postcondition with <span class="math">\(R\)</span> of some Hoare triple with <span class="math">\(C\)</span>.</p>
<h3 id="the-behavior-of-byreference-assignments">The behavior of by-reference assignments</h3>
<p>The next thing we need is a way to reason about assignments behind pointers, or "pointer writes". We use the term "by-reference assignments" to signify the similarities between normal assignments.</p>
<p>Starting by defining by-reference assignment, we add a rule allowing us to write to valid pointers:</p>
<p><span class="math">\[\frac{}{\{P * p \mapsto \bullet\}\ p \stackrel{\text{ptr}}{\gets} x\ \{P * p \mapsto x\}}\]</span></p>
<p>Next, we need to specify the semantics of <em>reading</em> from a pointer:</p>
<p><span class="math">\[\frac{\{P \land p \mapsto x\}\ k \gets \mathcal{H}(p)\ \{Q\}}{\{P \land p \mapsto x\}\ k \gets x\ \{Q\}}\]</span></p>
<p>In other words, writing the data read from a pointer to a variable is equivalent to writing the value it's pointing to. This is more of a definition than an actual rule, because it is obvious, ignoring the notation.</p>
<h3 id="allocation">Allocation</h3>
<p>Allocation is what introduces a new heap store/pointer into the heap. And its behavior is relatively straight-forward:</p>
<p><span class="math">\[\frac{p \notin \textbf{free}(P)}{\{P\}\ p \gets \textbf{alloc}(s)\ \{P * p \to \bullet\}}\]</span></p>
<p>Namely, if <span class="math">\(p\)</span> is not contained in <span class="math">\(P\)</span>, allocation creates a new, separate pointer. <span class="math">\(\bullet\)</span> denotes that the pointer is uninitialized or the value is unknown.</p>
<h3 id="deallocation">Deallocation</h3>
<p>As an example, take the dealloc function. This function obviously requires that there is no usage of the pointer later on (i.e. no use-after-free). We can express this in a relatively simple way:</p>
<p><span class="math">\[\frac{}{\{P * p \mapsto x\}\ \textbf{dealloc}(p)\ \{P\}}\]</span></p>
<p>The <span class="math">\(*\)</span> here express the independence of the content and validity of the pointer <span class="math">\(p\)</span>, which is really where separation logic shines: We can express pointer relation, and in this case, make sure that there is no usage of <span class="math">\(p\)</span> after the free.</p>
<h3 id="pointers-on-the-stack">Pointers on the stack</h3>
<p>In a formal model, the stack and the heap are not semantically different. In fact, we can interpret function calls as allocating the arguments onto the heap and deallocating them again when returning.</p>
<h3 id="detecting-memory-leaks">Detecting memory leaks</h3>
<p>In this model, it is surprisingly easy to prove your program leak-free. You simply have to put that the heap is empty in the postcondition and propagate it forward.</p>
<h2 id="future-work-and-whats-next">Future work and what's next</h2>
<p>Currently, I am writing a theorem extractor, which will generate the statement of correctness for some arbitrary program. This can then be fed into SMT solver and shown to be true.</p>
<p>Another aspect is the compilation itself, which must be a verified process, as such I am working on a compiler and formal proof of correctness of said compiler.</p>
<p>Lastly, I can formally verify Ralloc and Redox.</p>
<h2 id="conclusion-and-final-words">Conclusion and final words</h2>
<p>We have seen how a modest set of rules can create an elegant way to reason about the complex behavior of programs. Rust already has a very interesting form of static analysis, but it is decidable and much simpler, as a result, there is a lot of things it can not reason about, like raw pointers. We need a more advanced model (like the one we proposed in this post) to reason about such things.</p>