﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Eugene Bobukh's Blog</title>
    <atom:link
      href="https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/feed.xml</link>
    <description>Practical Science</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:07:03 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Genetic Algorithm, in Reverse Mode</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/genetic-algorithm-in-reverse-mode</link>
      <pubDate>Mon, 24 Jul 2017 19:42:01 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/?p=426</guid>
      <description><![CDATA[Today I would like to discuss running genetic algorithm… backwards.

Yes, this is possible....]]></description>
      <content:encoded><![CDATA[Today I would like to discuss running <a href="https://en.wikipedia.org/wiki/Genetic_algorithm">genetic algorithm</a>… backwards.

Yes, this is possible. Occasionally it is practical, when you need not the best, but the worst solution to a problem.

And I think there are more uses for it. It seems to be helpful with maintaining genetic pool diversity, with avoiding being stuck in local maximum – and therefore with finding <strong>better</strong> solutions to problems.

Some variations of this idea are apparently known, but not widely. And there is certain bridging between this method and simulated annealing. So I figured it is worth sharing my observations -- and <a href="https://msdnshared.blob.core.windows.net/media/2017/07/Code.zip">the code accompanying them</a>.
<p style="text-align: center">***</p>
<a href="https://en.wikipedia.org/wiki/Genetic_algorithm">Genetic (or evolutionary) algorithms</a> are not first class citizens in the world of optimization methods. The primary reason for that is their ability to make tough problems look deceivingly simple. A basic implementation of a genetic algorithm needs less than a screen of code; the idea behind it is obvious and intuition-friendly. With that, it is very tempting to ignore nature and inherent complexity of a problem being solved and let the CPU cycle, hoping it would eventually crank out a solution.

Only when it fails after millions of seconds one realizes that subtle details rule everything here. How is a solution represented as a vector? What are crossbreeding and mutation strategies? How do you choose the winner and how (or if) do you run a tournament? What do you do to avoid convergence to a sub-optimal maximum? These hyper-parameters are difficult to quantify to enable a systematic search within their space. Yet wrong choices slow things down by orders of magnitude. That’s why genetic algorithms often remain an art (if not a magic) to a large degree -- larger than many alternatives.

Yet they aren't useless. They tolerate poorly defined or implicit fitness functions. They are somewhat less inhibited by the curse of dimensionality. People do solve real problems with them, sometimes quite productively:
<ul>
 	<li><a href="https://en.wikipedia.org/wiki/Evolved_antenna">Classic “NASA antenna” case</a></li>
 	<li><a href="https://www.researchgate.net/publication/257925221_Fuzz_in_the_Dark_Genetic_Algorithm_for_Black-Box_Fuzzing">Genetic algorithm driven fuzzer</a></li>
 	<li><a href="https://www.clbme.bas.bg/bioautomation/2014/vol_18.3/files/18.3_03.pdf">Prosthetic operations optimization</a></li>
</ul>
They do have their uses, and they are fun to play with.

And one fun aspect is that you can run them backwards.

That’s it, you can replace “good” with “bad” – and run not for the best, but for the worst solution. Besides interesting philosophical implications, this also offers a strategy of dealing with local optima convergence.
<p style="text-align: center">***</p>
This issue – converging not to a global, but to a local sub-optimal solution – is inherent to many optimization methods. When a problem has multiple solutions, an algorithm can arrive to *<strong>a</strong>* solution, and then sit there forever, despite a better solution possibly existing couple valleys away. Steven Skiena described that situation very vividly:
<blockquote>“Suppose you wake up in a ski lodge, eager to reach the top of the neighboring peak. Your first transition to gain altitude might be to go upstairs to the top of the building. And then you are trapped. To reach the top of the mountain, you must go downstairs and walk outside, but this violates the requirement that each step has to increase your score.”</blockquote>
Genetic algorithms are not immune against this. Large body of scientific research addressing it is developed, to name a few examples:
<ul>
 	<li><a href="https://www.ijetae.com/files/Volume2Issue5/IJETAE_0512_10.pdf">An Overview of methods maintaining Diversity in Genetic Algorithms</a></li>
 	<li><a href="https://lazytoad.com/lti/pub/aaai84.html">Maintaining Diversity in Genetic Search </a></li>
</ul>
Sometimes the strategy is to run the algorithm multiple times from new random seeds, hoping to converge to a different solution each time. Sometimes people introduce artificial "species pressure" whereas solutions too similar to each other can't cross-breed and converge to the same point. Or they artificially separate the search space into isolated zones. Or punish for staying too close to an already learned local maxima.
<p style="text-align: center">***</p>
So how does running algorithm in a “reverse mode” may help with avoiding local maxima?

To illustrate, let’s pretend we are looking for the highest point on the US elevation map:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map0.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-216" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map0-300x188.jpg" /></a>

Assume, for a moment, that somehow (maybe because of an unlucky initial placements, maybe because the map is more complex than this) all genetic pool solutions congregated around a wrong local maximum of Appalachian Mountains:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map20.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-226" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map20-300x188.jpg" /></a><a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map10.jpg"></a>

[Important: the arrows do not imply continuous movement. Genetic algorithm does not work like a gradient descent. The arrows serve purely illustrational purposes of indicating (most likely discontinuous) state transitions]

What happens if you switch the evolution sign at this moment? When the least fit wins, and the worst solution is rewarded, the genetic pool starts running away from the local maximum, initially in all directions:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map30.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-235" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map30-300x188.jpg" /></a>

That <strong>diversifies</strong> it. Soon, the pool may have very little in common with the solution it initially described. After a while, it may even converge to a local minimum, which I would guess is near the Mississippi delta (though there is no point necessarily in keeping the evolution “in reverse” that long enough):

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map40.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-245" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map40-300x188.jpg" /></a>

Now let’s flip the sign of the evolution back to normal. Again, the solutions will start scattering away in all direction from the local minima:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map50.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-255" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map50-300x188.jpg" /></a>

And, if some of them arrives to a point elevated enough, eventually everyone would re-converge there:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Map60.jpg"><img width="300" height="188" class="alignnone size-medium wp-image-257" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/Map60-300x188.jpg" /></a>

In a two-dimensional case, this is almost trivial. But in higher dimensionality, where mountains are more difficult to discover, these fourth-and-back cycles may need to continue until you are happy with the solution. Or at least happier than in the beginning -- I'm not promising miracles here :))

Why would this work better than restarting each time from a random seed?

It may not always do, though my testing suggests that sometimes it does. The main benefit here is in having the control over the degree of the digression. If other maximum is expected far away or nearby, the backwards-evolution can run for longer or shorter period, as needed. In that respect, this approach is similar to simulated annealing which is able to adjust annealing schedule to throw just the right degree of chaos into the computation.

But all the above are just my expectations. Would they work in practice?

Lets find out by playing it against a toy problem that is easy to visualize.

Welcome the <strong>Scary Parking Lot Problem.</strong>
<p style="text-align: center">***</p>
Imagine that this is a map of a parking lot at night. There are a few lights producing illuminated patches here and there, and the rest of it is mostly dark:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9.Parking.png"><img width="240" height="240" class="alignnone size-full wp-image-267" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9.Parking.png" /></a>

The darker the area, the scarier it is in it. Our goal is to find the least scary path from the top left to the bottom right. To be precise, we want to minimize the integral of Fear = 1/(1 + Brigthness) along the path. If asked, I would’ve probably charted it like this:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9.Parking-Human.png"><img width="240" height="240" class="alignnone size-full wp-image-276" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9.Parking-Human.png" /></a>

This problem is well suited for genetic algorithm. Encode a path as a sequence of &lt;<strong>x</strong>, <strong>y</strong>&gt; points. A genome is represented then as a sequence &lt;<strong>x<sub>0</sub></strong>, <strong>y<sub>0</sub></strong>, <strong>x<sub>1</sub></strong>, <strong>y<sub>1</sub></strong>, ..., <strong>x<sub>N</sub></strong>, <strong>y<sub>N</sub></strong>&gt; (I used <strong>N</strong> = <strong>20</strong>). Initialize the pool randomly with <strong>K</strong> =<strong>16</strong> instances colored in green (blue shows the best solution found so far), and kick off the algorithm.

Iteration 0:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration0.png"><img width="240" height="240" class="alignnone size-full wp-image-285" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration0.png" /></a>

In just 500 iterations, the algorithm already hints at a decent approximation:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration500.png"><img width="240" height="240" class="alignnone size-full wp-image-295" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration500.png" /></a>

1000 iterations:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration1000.png"><img width="240" height="240" class="alignnone size-full wp-image-305" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration1000.png" /></a>

4000:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration4000.png"><img width="240" height="240" class="alignnone size-full wp-image-306" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration4000.png" /></a>

By 9000, it is mostly straightening out small knots:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration9000.png"><img width="240" height="240" class="alignnone size-full wp-image-315" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration9000.png" /></a>

18000 generations – final or nearly final:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration18000.png"><img width="240" height="240" class="alignnone size-full wp-image-317" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/9-Iteration18000.png" /></a>

[The red figure at the bottom left is the "cost", or the total fear factor, of the solution -- the smaller, the better].
<p style="text-align: center">***</p>
I tried it on a few dozen randomly generated “parking lots” and discovered that genetic algorithm solves this problem so well that it is actually difficult to find a field that would produce a grossly imperfect result. It took quite many experiments until I discovered this “foam bubbles” setup with intentionally harsh contrast and narrow illuminated zones. Constantly forced to make difficult right-or-left choices, and having no gradient feedback, the algorithm responded with some mistakes here.

Start:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration0.png"><img width="240" height="240" class="alignnone size-full wp-image-325" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration0.png" /></a>

500 iterations:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration500.png"><img width="240" height="240" class="alignnone size-full wp-image-335" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration500.png" /></a>

2000:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration2000.png"><img width="240" height="240" class="alignnone size-full wp-image-345" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration2000.png" /></a>

7000:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration70001.png"><img width="240" height="240" class="alignnone size-full wp-image-445" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration70001.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration7000.png"></a>

18000 (final):

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration180002.png"><img width="240" height="240" class="alignnone size-full wp-image-436" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration180002.png" /></a>

The solution is stuck. Wrapped around a bubble, it won't change to anything better no matter how long you run it. Not coincidentally, it has also lost its genetic diversity.
<p style="text-align: center">***</p>
Now let’s try to untangle it by enabling the reversals. Every 6000 iterations we will put the evolution in the “backwards mode” for 210 steps.

For the first 6000 generations it is the same game, converging to the already familiar solution:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6000.png"><img width="240" height="240" class="alignnone size-full wp-image-366" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6000.png" /></a>

But then, at 6000, the reversal begins. “Bad” solutions are in favor now. Lets’ watch how they develop, spreading away from the original path.

6100:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6100.png"><img width="240" height="240" class="alignnone size-full wp-image-375" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6100.png" /></a>

6200:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6200.png"><img width="240" height="240" class="alignnone size-full wp-image-385" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration6200.png" /></a>

At 6210 the sign changes back to normal and we start re-converging – to a different maximum this time!

At 1200:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration12000.png"><img width="240" height="240" class="alignnone size-full wp-image-387" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration12000.png" /></a>

After 12000, another reversal cycle follows, ending up with the 3rd solution at 18K:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration180001.png"><img width="240" height="240" class="alignnone size-full wp-image-395" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-Iteration180001.png" /></a>

Of all three, the 2<sup>nd</sup> is the best:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1-TheBest.png"><img width="240" height="240" class="alignnone size-full wp-image-396" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1-TheBest.png" /></a>

Its cost is 63279.6. It is still not perfect, but the control (without reversals) was 79896.5. The new solution is 20.8% cheaper!
<p style="text-align: center">***</p>
But maybe this works just for this one parking lot?

Let’s repeat the experiment 40 times with different “bubble sets”, each randomly generated:
<table style="border-collapse: collapse;border-style: solid;border-width: 1" border="1" cellpadding="4">
<tbody>
<tr>
<th style="padding: 4px"></th>
<th style="padding: 4px">Control (18K iterations)</th>
<th style="padding: 4px">Experiment (18K iterations, with 2 reversals 210 steps each)</th>
</tr>
<tr>
<td style="padding: 4px">Average cost of the best solution relative to a random path</td>
<td style="padding: 4px">0.13448</td>
<td style="padding: 4px">0.11605</td>
</tr>
<tr>
<td style="padding: 4px">Standard deviation</td>
<td style="padding: 4px">0.034179</td>
<td style="padding: 4px">0.024659</td>
</tr>
<tr>
<td style="padding: 4px">Student t-score of the improvement</td>
<td style="padding: 4px">N/A</td>
<td style="padding: 4px">2.76637863954974</td>
</tr>
<tr>
<td style="padding: 4px">How many times the solution found was better than control?</td>
<td style="padding: 4px">N/A</td>
<td style="padding: 4px">30 (out of 40)</td>
</tr>
</tbody>
</table>
&nbsp;

A one-sided t-score of 2.77 with 40 degrees of freedom corresponds (<a href="https://en.wikipedia.org/wiki/Student%27s_t-distribution#Table_of_selected_values">https://en.wikipedia.org/wiki/Student%27s_t-distribution#Table_of_selected_values</a>) to roughly 99.5% chance that the difference is not accidental. In other words, that the experiment with reversals indeed performed better than the same 18K iterations straight. And for 30 parking lots (out of 40), the solution found was better than the control.

How does it compare to simply doing three runs with 6K steps, each starting from a random seed, and then selecting the best solution? It is about the same, or maybe very slightly better. Restarts from the scratch produced an improvement t-score of 2.5538656109400, with 24 runs out of 40 doing better than control.
<p style="text-align: center">***</p>
Finally, why 210 reversal steps? Why not more or less?

This is where the power of the idea materializes. Reversals let you <strong>control</strong> their duration – and adjust it as needed for the task being solved. By playing with those durations, I produced the following plot of improvement t-score (relative to the control):

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/t-Score.png"><img width="480" height="289" class="alignnone size-full wp-image-415" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/t-Score.png" /></a>

Blue curve are the results with reversals, orange is the result of random reset (for fixed RNG seeding). Obviously, there is fluctuation here, so I can’t strictly say that reversals are better than random resets. But it does not take away the fact that reversals produced <strong>some</strong> solutions better than anything else tried did.

Finally, here is another plot, showing the number of “parking lots” where solutions with reversals or random reset were better than control:

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/SolutionsCount.png"><img width="480" height="289" class="alignnone size-full wp-image-405" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/SolutionsCount.png" /></a>
<p style="text-align: center">***</p>
So, is this method a magic bullet? Probably not. It is just a tool, among many other invented by people to deal with practical problems. But maybe it would be helpful for some.

To summarize:
<ul>
 	<li>Reversing genetic algorithm during its run is possible</li>
 	<li>…at least, for finding deliberately bad “anti-solutions” to a problem</li>
 	<li>…or simply for fun</li>
 	<li>More importantly, sometimes it can help combat the loss of pool’s genetic diversity</li>
 	<li>Short controlled reversals can produce solutions notably better than those from simple uninterrupted runs of the same duration</li>
 	<li>They are at least comparable in quality to solutions achieved by restarting the algorithm from scratch the same number of times as the number of reversals</li>
 	<li>Duration and frequency of reversal cycles are controllable numeric parameters that could be adjusted to suit a problem at hands</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2017/07/Code.zip">Link to the code.</a>]]></content:encoded>
    </item>
    <item>
      <title>Is Pluto a Planet?</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/is-pluto-a-planet</link>
      <pubDate>Wed, 25 May 2016 03:07:02 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/?p=155</guid>
      <description><![CDATA[Is Pluto a planet?

And how is that question related to Data Science?

For sure, physical...]]></description>
      <content:encoded><![CDATA[<strong>Is Pluto a planet?</strong>

And how is that question related to Data Science?

For sure, physical properties of Pluto do not change if we call it a "planet", a "dwarf planet", a "candelabrum", or a "sea cow". Pluto stays the same Pluto regardless of all that. For physics or astronomy, naming does not really matter.

Yet we don't expect to find publications on Pluto in the Journal of Marine Biology. They belong to astronomy or planetary science. So, for information storage and retrieval naming does matter, and does a lot.

From that standpoint, the distinction is material. When we study "real" planets like Mars or Venus we often mention features that only "real" planets tend to have -- such as atmospheres, tectonic activity, or differentiated internal structure. Small asteroids and "boulders" rarely if ever have any of that. Reflecting those physical differences, the vocabularies would likely be different as well.

Thus, we may classify something as a "planet" if language used with respect to that object follows the same patterns as language used with "real" planets. Simply because it would be easier to store, search and use the information when it's organized that way.

But is that difference detectable? And if yes, is it consistent?

That calls out for an experiment:
<ol>
	<li>Collect a body of texts concerning an object that we confidently consider a planet -- say, Mars.</li>
	<li>Collect a body of texts (preferably from the same source) associated with "definitely not a planet" -- say, a comet or an asteroid.</li>
	<li>Do the same for Pluto.</li>
	<li>Using text mining algorithms, compute vocabulary similarity between each pair of subjects and check whether Pluto's texts language better resembles that of Mars -- or of a non-planetary object.</li>
</ol>
I ran that experiment. The results suggest that Pluto indeed is a planet with respect to how we talk about it. The details are provided below.

&nbsp;

<strong>Algorithm Outline</strong>

Assuming the documents are represented as a collection of .txt files, for each planetary body:
<ol>
	<li>Read all text from the files.</li>
	<li>Split it into tokens using a set of splitters like ' ' (whitespace), '.' (dot) or '-' (dash) (42 in total, see <a href="https://msdnshared.blob.core.windows.net/media/2016/05/Program.cs_.txt">the code</a> for the full list).</li>
	<li>Drop empty tokens.</li>
	<li>Drop tokens that are too short. In these experiments, 2 was the threshold, but I saw virtually the same results with 1 or 3.</li>
	<li>Drop tokens consisting only of digits (e.g., "12345")</li>
	<li>Drop the so-called "stop words", which are very common tokens like "a" or "the" that rarely carry any significant information. I used the <a href="https://meta.wikimedia.org/wiki/Stop_word_list/google_stop_word_list#English"> list from Wikipedia</a>, extended with a few terms like "fig" (a short for "figure"), or "et" and "al" common in scientific context.</li>
	<li>Run <a href="https://tartarus.org/~martin/PorterStemmer/">Porter suffix stemming algorithm</a> on each token. The algorithm maps grammatical variations of the same word into common root (e.g., "read", "reads", "reading" -&gt; "read"). Published in 1980, the algorithm is still considered a "golden standard" of suffix stemming, not least because of its high performance. While the algorithm description is clear and relatively straightforward, the implementation is very sensitive to getting all the details exactly right. So I used publicly available <a href="https://tartarus.org/~martin/PorterStemmer/csharp3.txt">C# implementation</a> by Brad Patton from <a href="https://tartarus.org/~martin/PorterStemmer">Martin Porter's page</a> (greatest thanks to the author!)</li>
	<li>Again drop tokens that were too short, if any at this point.</li>
	<li>Add the resulting keywords to the collection of words counts. Thus, if <strong>A</strong> is such a collection, then <strong>A</strong>[<strong>a</strong>] is be the count of word <strong>a</strong> in <strong>A</strong>.</li>
	<li>While performing steps 1-9 above, also maintain a count of documents that use each word. This is done to be able to give less common words that carry more information (e.g., "exosphere" vs. "small") more impact on documents similarity comparison, via assigning them the so called "<a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency_2">tf-idf</a>" weight. After a few experiments, I arrived to the following definition of <strong>IDF</strong> weight per word:</li>
</ol>
<a href="https://msdnshared.blob.core.windows.net/media/2016/05/IDF.png"><img class=" size-full wp-image-165 aligncenter" src="https://msdnshared.blob.core.windows.net/media/2016/05/IDF.png" alt="IDF" /></a>
        <ul>While the extra length term deviates from more <a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency_2">traditional definitions</a>, I found the discriminative power of this approach to be better than several other variations tested.</ul>
<ol start="11">
        <li>Using the formula above, also compute and store the <strong>idf</strong>[<strong>a</strong>] collection for all words in the corpus.</li>
</ol>

When these collections (<strong>A</strong>[<strong>a</strong>] and <strong>idf</strong>[<strong>a</strong>]) are produced for each topic (i.e., one pair for Mars, another for Pluto), compute corpus pairwise cosine similarities:
<a href="https://msdnshared.blob.core.windows.net/media/2016/05/J.png"><img class=" size-full wp-image-175 aligncenter" src="https://msdnshared.blob.core.windows.net/media/2016/05/J.png" alt="J" /></a>

[Again, several other variations were tested, with this one producing the best classes discrimination in tests]

The resulting metric, <strong>J</strong>(<strong>A</strong>,<strong>B</strong>), is the vocabulary similarity between the sets of documents <strong>A</strong> and <strong>B</strong>, ranging from <strong>0</strong> (totally different) to <strong>1</strong> (exactly the same).

The code is <a href="https://msdnshared.blob.core.windows.net/media/2016/05/Program.cs_.txt">here</a> if you want to examine it, except for the Porter algorithm which as I mentioned was adopted verbatim from <a href="https://tartarus.org/~martin/PorterStemmer/csharp3.txt">C# implementation</a> by Brad Patton.

OK. The code is written. How do we know it really works? That's what testing is for.

&nbsp;

<strong>Test 1. Classical Music vs. Chemistry</strong>

For this test, two bodies of text were chosen.

The first consisted of Wikipedia articles on inert gases Helium, Neon, Argon, Krypton and Xenon, sections starting after the table of content, and continuing (but not containing) to "References" or "See Also". The second corpus consisted of articles on classical music composers Bach, Beethoven, Mozart and Rachmaninoff, similarly pre-filtered.

Two test subjects were Wikipedia <a href="https://en.wikipedia.org/wiki/Oxygen">article</a> about Oxygen (gas), and Wikipedia <a href="https://en.wikipedia.org/wiki/Oxyg%C3%A8ne">article</a> about Oxygene, a famous musical album of Jean Michelle Jarre, the composer. The question was: can this code properly attribute those test articles as belonging to gases or music?

After fixing couple of bugs, some fine-tuning and experimentation, the code kicked in and produced the following output:
<table>
<tbody>
<tr>
<td></td>
<td><strong>Gases</strong></td>
<td><strong>Composers</strong></td>
<td><strong>Oxygen (gas)</strong></td>
<td><strong>Oxygene (album)</strong></td>
</tr>
<tr>
<td width="110"><strong>Gases</strong></td>
<td width="154">100% (0.237)</td>
<td width="154">2.33% (0.092)</td>
<td width="154"><strong>23.3% (0.197)</strong></td>
<td width="155">1.38% (0.244)</td>
</tr>
<tr>
<td width="110"><strong>Composers</strong></td>
<td width="154">2.33% (0.092)</td>
<td width="154">100% (0.225)</td>
<td width="154">3.61% (0.152)</td>
<td width="155"><strong>5.74% (0.348)</strong></td>
</tr>
<tr>
<td width="110"><strong>Oxygen (gas)</strong></td>
<td width="154"><strong>23.3% (0.197)</strong></td>
<td width="154">3.61% (0.152)</td>
<td width="154">100% (0.347)</td>
<td width="155">2.25% (0.189)</td>
</tr>
<tr>
<td width="110"><strong>Oxygene (album)</strong></td>
<td width="154">1.38% (0.244)</td>
<td width="154"><strong>5.74% (0.348)</strong></td>
<td width="154">2.25% (0.189)</td>
<td width="155">100% (0.587)</td>
</tr>
</tbody>
</table>
&nbsp;

The percentage is the degree of similarity <strong>J</strong>(<strong>A</strong>, <strong>B</strong>). The number in parenthesis is the support metric of how many unique words entered the intersection set, relative to word count of the smaller set. Highlighted is the largest similarity (except to self) in each row. As you can see, the method correctly attributed Oxygen (gas) to gases, and Oxygen (musical album) to Music/Composers.

And yes, I'm explicitly computing the distance matrix twice -- from <strong>A</strong> to <strong>B</strong> and from <strong>B</strong> to <strong>A</strong>.  That is done on purpose as an additional built-in test for implementation correctness.

OK, this was pretty simple. Music and chemistry are very different. Can we try it with subjects somewhat closer related?

&nbsp;

<strong>Test 2. Different Types of Celestial Bodies</strong>, from Wikipedia again

Now we will use seven articles, with all the text up to "References" or "See also" sections:
<ul>
	<li><a href="https://en.wikipedia.org/wiki/Sirius">Sirius</a> and <a href="https://en.wikipedia.org/wiki/Betelgeuse">Betelgeuse</a> (both are stars)</li>
	<li><a href="https://en.wikipedia.org/wiki/Mars">Mars</a> and <a href="https://en.wikipedia.org/wiki/Venus">Venus</a> (both are "first class" citizens in the world of planets)</li>
	<li><a href="https://en.wikipedia.org/wiki/67P/ChuryumovвЂ“Gerasimenko">Comet 67P/Churyumov–Gerasimenko</a> and <a href="https://en.wikipedia.org/wiki/Halley's_Comet">Halley's Comet</a> (both are comets, definitely not planets)</li>
	<li><a href="https://en.wikipedia.org/wiki/Pluto">Pluto</a> (why not give it a try?)</li>
</ul>
The resulting similarity matrix:
<table>
<tbody>
<tr>
<td></td>
<td><strong>67P</strong></td>
<td><strong>Betelgeuse</strong></td>
<td><strong>Halley Comet</strong></td>
<td><strong>Mars</strong></td>
<td><strong>Pluto</strong></td>
<td><strong>Sirius</strong></td>
<td><strong>Venus</strong></td>
</tr>
<tr>
<td width="98"><strong>67P</strong></td>
<td width="121">100% (0.481)</td>
<td width="122">0.76% (0.259)</td>
<td width="122"><strong>2.8% (0.23)</strong></td>
<td width="122">1.78% (0.286)</td>
<td width="122">0.696% (0.234)</td>
<td width="122">0.297% (0.202)</td>
<td width="122">1.13% (0.252)</td>
</tr>
<tr>
<td width="98"><strong>Betelgeuse</strong></td>
<td width="121">0.76% (0.259)</td>
<td width="122">100% (0.307)</td>
<td width="122">1.27% (0.183)</td>
<td width="122">2.66% (0.133)</td>
<td width="122">1.66% (0.159)</td>
<td width="122"><strong>6.9% (0.204)</strong></td>
<td width="122">2.86% (0.167)</td>
</tr>
<tr>
<td width="98"><strong>Halley Comet</strong></td>
<td width="121"><strong>2.8% (0.23)</strong></td>
<td width="122">1.27% (0.183)</td>
<td width="122">100% (0.393)</td>
<td width="122">2.05% (0.195)</td>
<td width="122">2.12% (0.159)</td>
<td width="122">1.48% (0.15)</td>
<td width="122">1.97% (0.164)</td>
</tr>
<tr>
<td width="98"><strong>Mars</strong></td>
<td width="121">1.78% (0.286)</td>
<td width="122">2.66% (0.133)</td>
<td width="122">2.05% (0.195)</td>
<td width="122">100% (0.289)</td>
<td width="122">3.87% (0.175)</td>
<td width="122">1.76% (0.19)</td>
<td width="122"><strong>9.11% (0.191)</strong></td>
</tr>
<tr>
<td width="98"><strong>Pluto</strong></td>
<td width="121">0.696% (0.234)</td>
<td width="122">1.66% (0.159)</td>
<td width="122">2.12% (0.159)</td>
<td width="122"><strong>3.87% (0.175)</strong></td>
<td width="122">100% (0.326)</td>
<td width="122">1.07% (0.155)</td>
<td width="122">2.68% (0.15)</td>
</tr>
<tr>
<td width="98"><strong>Sirius</strong></td>
<td width="121">0.297% (0.202)</td>
<td width="122"><strong>6.9% (0.204)</strong></td>
<td width="122">1.48% (0.15)</td>
<td width="122">1.76% (0.19)</td>
<td width="122">1.07% (0.155)</td>
<td width="122">100% (0.448)</td>
<td width="122">2.01% (0.171)</td>
</tr>
<tr>
<td width="98"><strong>Venus</strong></td>
<td width="121">1.13% (0.252)</td>
<td width="122">2.86% (0.167)</td>
<td width="122">1.97% (0.164)</td>
<td width="122"><strong>9.11% (0.191)</strong></td>
<td width="122">2.68% (0.15)</td>
<td width="122">2.01% (0.171)</td>
<td width="122">100% (0.334)</td>
</tr>
</tbody>
</table>
&nbsp;

What do we see? Stars are correctly matched against stars. Comets -- against comets (although not very strongly). "True" planets are matched to "true" planets with much greater confidence. And Pluto? Seems like the closest match among the given choices is... Mars. With the next being... Venus!

So at least Wikipedia, when discussing Pluto, uses more "planet-like" language rather than "comet-like" or "star-like".

To be scientifically honest -- if I add Ceres to that set, the method correctly groups it with Pluto, sensing the "dwarfness" in both. But the next classification choice for each is still strongly a "real" planet rather than anything else. So at least from the standpoint of this classifier, a "dwarf planet" is a tight subset of a "planet" rather than anything else.

Now let's put it to real life.

&nbsp;

<strong>Test 3. Scientific Articles</strong>

<a href="https://www.hou.usra.edu/meetings/lpsc2016/">The 47th Lunar and Planetary Science Conference</a> held in March 2016 featured over two thousand great talks and poster presentations on the most recent discoveries about many bodies of the Solar System, including Mars, Pluto, and 67P/Churyumov–Gerasimenko comet. The program and abstracts of the conference are available <a href="https://www.hou.usra.edu/meetings/lpsc2016/programAbstracts/view/">here</a>. What if we use them for a test?

This was more difficult than it seems. For ethical reasons, I did not want to scrape the whole site, choosing to use instead a small number (12 per subject) of purely randomly chosen PDF abstracts. Since the document count was low, I decided not to bother with PDF parsing IFilter and to copy relevant texts manually. That turned out to be a painful exercise requiring great attention to detail. I needed to exclude authors lists (to avoid accidentally matching on people or organization names), references, and manually fix random line-breaking hyphens in some documents. For any large-scale text retrieval system, this process would definitely need serious automation to work well.

But finally, the results were produced:
<table>
<tbody>
<tr>
<td width="140"></td>
<td><strong>67P</strong></td>
<td><strong>Mars</strong></td>
<td><strong>Pluto</strong></td>
</tr>
<tr>
<td><strong>67P</strong></td>
<td width="120">100% (0.252)</td>
<td width="120">17.9% (0.126)</td>
<td width="120"><strong>19.7% (0.132)</strong></td>
</tr>
<tr>
<td><strong>Mars</strong></td>
<td width="120">17.9% (0.126)</td>
<td width="120">100% (0.224)</td>
<td width="120"><strong>21% (0.111)</strong></td>
</tr>
<tr>
<td><strong>Pluto</strong></td>
<td width="120">19.7% (0.132)</td>
<td width="120"><strong>21% (0.111)</strong></td>
<td width="120">100% (0.21)</td>
</tr>
</tbody>
</table>
&nbsp;

The differences are far less pronounced, probably because the texts have very similar origins and format restrictions, and use the same highly specialized scientific vocabulary. Still, within the space of this data set, Pluto was slightly more of a planet rather than of a comet!

&nbsp;

<strong>Closing Remarks</strong>

How seriously should these results be taken?

First, they are obtained on a small data set, with a home-grown classifier, and the differences detected are relatively modest. If anybody wants to take a serious Data Science driven stance on this issue, they should use a much, much larger text corpus.

Second, people are still free to call Pluto a "planet", a "dwarf planet", or a "candelabrum" if that suites them -- regardless of these results.

But there are important points that I wanted to make. First, language use may offer another definition of what is more practical to call "a planet". Second, the language differences behind that definition are objective and measurable. And finally, those differences are representative of the physical world.

Thank you for reading,
Eugene V. Bobukh]]></content:encoded>
    </item>
    <item>
      <title>Equation of a Fuzzing Curve -- Part 2/2</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/equation-of-a-fuzzing-curve-part-22</link>
      <pubDate>Tue, 06 Jan 2015 12:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2015/01/06/equation-of-a-fuzzing-curve-part-22/</guid>
      <description><![CDATA[Follow-up notes and discussion
See Part 1 here.
&nbsp;
Can you predict how many bugs will be...]]></description>
      <content:encoded><![CDATA[<h1 class="WordSection1"><span style="font-family: verdana,geneva;">Follow-up notes and discussion</span></h1>
<p class="WordSection1"><span style="font-family: verdana,geneva;">See <a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/12/18/equation-of-a-fuzzing-curve-part-1-2.aspx">Part 1 here</a>.</span></p>
<p class="WordSection1">&nbsp;</p>
<h2 class="WordSection1"><span style="font-family: verdana,geneva;">Can you predict how many bugs will be found at infinity?</span></h2>
<p class="WordSection1">&nbsp;</p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">No.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">There seems to be a fundamental limit on fuzzing curve extrapolation.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">To see that, consider bug distribution function of the following form:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6646.TwoModeG(p).png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6646.TwoModeG_2800_p_2900_.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6646.TwoModeG(p).png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/298x47/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6646.TwoModeG_2800_p_2900_.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">where <strong>p<sub>0</sub> &gt;&gt; p<sub>1</sub></strong> but <strong>a<sub>0</sub> &asymp; a<sub>1</sub></strong> and <strong>&delta;(x)</strong> is a Dirac <a href="https://en.wikipedia.org/wiki/Dirac_delta_function">delta-function</a>.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">For such a distribution <strong>U(N)</strong> is:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4064.150.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4064.150.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4064.150.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/393x47/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4064.150.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">At early fuzzing stages, the 2<sup>nd</sup> term is so much smaller than the first one that it remains undetectable. Under that condition, only the first term would be considered, with the expected <strong>U(N)</strong> value at infinity of <strong>a<sub>0</sub></strong>. However, the true bug yield for that curve is <strong>a<sub>0</sub>&nbsp;+&nbsp;a<sub>1</sub></strong>! But we will not notice that until we've done enough fuzzing to "see" the 2<sup>nd</sup> term.&nbsp; It is possible to show that it will become detectable only after more than the following number of iterations is performed:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4380.160.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4380.160.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4380.160.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/393x66/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4380.160.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Prior to that, the extrapolation of the above fuzzing curve will produce grossly incorrect results.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Since (in general) any fuzzing curve can potentially carry terms similar to the last one in (150) it means that no matter how long we've fuzzed, predicting what the curve would look like beyond a certain horizon is impossible. If that reasoning is correct, only short or mid-term fuzzing extrapolation is possible.</span></p>
<p class="WordSection1">&nbsp;</p>
<h2 class="WordSection1"><span style="font-family: verdana,geneva;">What defines G(p)?</span></h2>
<p class="WordSection1">&nbsp;</p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">What if we obtain <strong>G(p)</strong> not from the observations but use some theory to construct it in the most generic form?</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Unfortunately, it seems that <strong>G(p)</strong> is more a function of a fuzzer rather than of a product.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Often, you run a sophisticated fuzzer for countless millions of iterations and beat seemingly all the dust out of your product. Next day, someone appears with a new fuzzer made out of 5 lines of Python code and <a href="https://fuzzinginfo.files.wordpress.com/2012/05/cmiller-csw-2010.pdf">finds new good bugs</a>. Many. That's hard to explain if bug discovery probability was mostly a property of a product, not of a fuzzer.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">[Also, fuzzing stack traces often tend to be very deep, with many dozens of frames, suggesting that the volume of functionality space being fuzzed is so large that most fuzzers probably only scratch the surface of it rather than completely exhaust it]</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">If that is correct, fuzzer diversity should work better than fuzzing duration. Twelve different fuzzers running for a month should be able to produce more good bugs than one fuzzer running for a year.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">That said, constructing <strong>G(p)</strong> from a pure theory and therefore finding a fuzzing curve approximation that works better than generic form (140) might be possible &ndash; but for <strong>a specific fuzzer</strong> only, using the knowledge of that fuzzer intrinsic properties and modes of operation.</span></p>
<p class="WordSection1">&nbsp;</p>
<h2 class="WordSection1"><span style="font-family: verdana,geneva;">How long should you fuzz?</span></h2>
<p class="WordSection1">&nbsp;</p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">The only right answer from the security standpoint is "indefinitely"!</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">However, from the business prospective the following return-on-investment (ROI) analysis may make sense and lead to a different answer.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Fuzzing has its dollar cost. That&rsquo;s because infrastructure, machines, electric power, triaging, filing and fixing bugs cost money, not to mention paying the people who do all that.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Not fuzzing has its cost as well. The most prominent part of it is the cost of patching vulnerabilities that could&rsquo;ve been prevented by fuzzing.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Let&rsquo;s introduce the cost of fuzzing per one iteration <strong>X(N)</strong>. In addition to that, there is a cost of bugs management <strong>Y</strong> per bug, and a one-time cost of enabling fuzzing infrastructure <strong>C</strong>. Thus, the cost of running <strong>N</strong> fuzzing iterations per a parser is:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7357.200.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7357.200.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7357.200.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/367x71/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7357.200.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Not running fuzzing introduces security bugs. Let&rsquo;s denote the average cost of responding to a security vulnerability via <strong>T</strong>, and the probability that the response will be required via <strong>q</strong> (assuming, for simplicity, that <strong>q</strong> is constant). If there are <strong>B</strong> total bugs in the product, and the fuzzer in question has ran for <strong>N</strong> iterations, it would&rsquo;ve found <strong>U(N)</strong> bugs. Then <strong>B - U(N)</strong> bugs would remain in the product, and some of them would be found and exploited. Therefore, the cost of <strong>not</strong> running fuzzer after the initial <strong>N</strong> iterations is <strong>q*T*(B-U(N))</strong>.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Combined together, these two figures give the cost of a decision to run fuzzing for <strong>N</strong> iterations and then stopping, as a function of <strong>N</strong>:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0245.210.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0245.210.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0245.210.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/502x70/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0245.210.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">From purely the ROI standpoint, fuzzing needs to continue while the increase of iterations decreases the total cost:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6102.Cost-Generic.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6102.Cost_2D00_Generic.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6102.Cost-Generic.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/153x70/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6102.Cost_2D00_Generic.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">which translates to:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5531.220.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5531.220.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5531.220.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/313x69/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5531.220.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Let&rsquo;s introduce <strong>&Delta;</strong><strong>N(N)</strong> &ndash; the number of fuzzing iterations required to find one bug. By the definition,</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6305.delta-N-def.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6305.delta_2D00_N_2D00_def.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6305.delta-N-def.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/149x63/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6305.delta_2D00_N_2D00_def.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Multiplying (220) by <strong>&Delta;</strong><strong>N</strong> and slightly rearranging, we arrive to:</span></p>
<p class="WordSection1"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1346.230.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1346.230.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1346.230.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/249x47/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1346.230.png" alt="" border="0" /></a></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">This is a generic stopping condition for static fuzzing.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">It says: &ldquo;fuzz until the cost of finding the next bug plus managing it exceeds the cost of addressing a vulnerability multiplied by the chance such vulnerability would be found or exploited&rdquo;.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Since fuzzing with no bugs found (yet) costs <strong>no less</strong> than fuzzing until the first bug is hit, the condition above could be generalized to include situations with or with no bugs found yet:</span></p>
<ol>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">Estimate the cost of addressing one externally known vulnerability in your product</span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">Multiply it by the chance that internally known bug becomes externally known or exploited</span></div>
</li>
<ol>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">As a security person, I should strongly push to use 100% here&hellip;</span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">&hellip;but we know that in reality it may be different</span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">My <a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-1-3.aspx">previous post</a> provides some thoughts on assessing that figure</span></div>
</li>
</ol>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">Subtract the cost of managing and fixing one bug internally</span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">Call the result <strong>V</strong></span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">Fuzz until the costs of fuzzing exceeds <strong>V </strong>or until potentially exploitable bugs are found</span></div>
</li>
<li>
<div class="WordSection1"><span style="font-family: verdana,geneva;">If such bugs are found, reset the cost to 0 and go back to step 5</span></div>
</li>
</ol>
<p><span style="font-family: verdana,geneva;">Similar logic could be applied to the attacker&rsquo;s side and similar conclusion would be derived, but with "vulnerability cost" replaced by "the benefits of exploiting one vulnerability". Comparing the attacker's and the defenders' ROIs then leads to some conclusions:</span></p>
<ol>
<li><span style="font-family: verdana,geneva;">If defenders lose less than attackers benefit from finding a vulnerability, attackers will statistically prevail in fuzzing (actually, you don't really need the math above to see that).</span></li>
<li><span style="font-family: verdana,geneva;">ROI-driven fuzzing dictates a "stopping point" that will be surpassed by attackers who:</span></li>
<ol>
<li><span style="font-family: verdana,geneva;">Are not driven by direct profits (e.g., groups with geopolitical or military interests), and</span></li>
<li><span style="font-family: verdana,geneva;">Have large cache reserves (e.g., are state-sponsored)</span></li>
<li><span style="font-family: verdana,geneva;">&hellip;meaning, such groups will (provided other things being equal) prevail over the defenders in finding fuzzing bugs and exploiting them.</span></li>
</ol>
<li><span style="font-family: verdana,geneva;">Software manufacturers still should fuzz, but mainly to increase the entry barrier (<strong>&Delta;</strong><strong>N</strong>) for new attackers seeking to join the arms race.</span></li>
</ol>
<p>&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">What if my fuzzer is not random?</span></h2>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">Some fuzzers learn from what they find to better aim their efforts. Their bug search is not completely random. However, a slight modification to the same theory permits describing their output with the same equation (140) where individual bugs are replaced by small (3-15 in size) bug clusters.</span></p>
<p>&nbsp;</p>
<h1><span style="font-family: verdana,geneva;">Summary</span></h1>
<p><span style="font-family: verdana,geneva;">1. In many practical cases, fuzzing curve could be modeled as</span></p>
<p style="text-align: center;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0724.140.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0724.140.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0724.140.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/311x79/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0724.140.png" alt="" border="0" /></a>&nbsp;&nbsp;</p>
<p><span style="font-family: verdana,geneva;">where <strong>a<sub>i</sub></strong> and <strong>p<sub>i</sub></strong> are some positive fixed parameters and <strong>D</strong> is small, on the order of <strong>0</strong> to <strong>3</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">2. Two fuzzers working for a month are better than one fuzzer working for 2 months.</span></p>
<p><span style="font-family: verdana,geneva;">3. There is a limit on how far into the future a static fuzzing curve could be extrapolated, and it seems to be fundamental (not depending on the method of extrapolation).</span></p>
<p><span style="font-family: verdana,geneva;">4. State-sponsored, non-profit driven attackers are likely to fuzz more than ROI-driven software manufacturers. That said the defenders have no other choice as to continue fuzzing and invest in fuzzing diversity and sophistication (rather than pure duration).</span></p>
<p>&nbsp;</p>
<h1><span style="font-family: verdana,geneva;">Acknowledgements</span></h1>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">To Tim Burrell, Graham Calladine, Patrice Godefroid, Matt Miller, Lars Opstad, Andy Renk, Tony Rice, Gavin Thomas for valuable feedback, support, and discussions. To Charlie Miller for his permission to reference data in his <a href="https://fuzzinginfo.files.wordpress.com/2012/05/cmiller-csw-2010.pdf">work</a>.</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Equation of a Fuzzing Curve -- Part 1/2</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/equation-of-a-fuzzing-curve-part-12</link>
      <pubDate>Thu, 18 Dec 2014 13:26:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/12/18/equation-of-a-fuzzing-curve-part-12/</guid>
      <description><![CDATA[Equation of a Fuzzing Curve
&nbsp;

Introduction
While fuzzing, you may need to extrapolate or...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<h1><span style="font-family: verdana,geneva;">Equation of a Fuzzing Curve</span></h1>
<h1>&nbsp;</h1>
</div>
<h2 class="WordSection1"><span style="font-family: verdana,geneva;">Introduction</span></h2>
<p class="WordSection1"><span style="font-family: verdana,geneva;">While fuzzing, you may need to extrapolate or describe analytically a "fuzzing curve", which is the dependency between the number of bugs found and the count of fuzzing inputs. Here </span><span style="font-family: verdana,geneva;">I will share my approach to deriving an analytical expression for that curve. The results could be applied for bug flow forecasting, return-on-investment (ROI) analysis, and general theoretical understanding of some aspects of fuzzing.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">If you are wondering about things like "what is fuzzing?" here is a little FAQ:</span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Q: So, what is fuzzing?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: (Wikipedia' </em><a href="https://en.wikipedia.org/wiki/Fuzz_testing"><em>definition</em></a><em>)</em><em>: "Fuzz testing or fuzzing is a software testing technique &lt;&hellip;&gt; that involves providing invalid, unexpected, or random data to the inputs of a computer program. The program is then monitored for exceptions such as crashes, or failing built-in code assertions or for finding potential memory leaks. Fuzzing is commonly used to test for security problems in software or computer systems."</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Basically, every time you throw intentionally corrupted or random input at the parser and hope it will crash, you do fuzzing.</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Q: What is a fuzzing curve?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: That is simply the dependency between the number of unique bugs (e.g., crashes) found and the number of inputs provided to the program, also called &ldquo;iterations&rdquo;. A fuzzing curve, being a special case of a bug discovery curve, may look like this:</em></span></p>
<p class="WordSection1" style="padding-left: 30px;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4682.TypicalFuzzingCurve-1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4682.TypicalFuzzingCurve_2D00_1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4682.TypicalFuzzingCurve-1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/519x387/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4682.TypicalFuzzingCurve_2D00_1.png" alt="" border="0" /></a></p>
<p class="WordSection1" style="padding-left: 30px;"><em><span style="font-family: verdana,geneva;">[For the record, the data is simulated and no real products have&nbsp;suffered while making this chart.]</span></em></p>
<p class="WordSection1" style="padding-left: 30px;">&nbsp;</p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Q: Why do we need to know fuzzing curve equation?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: If you don't know the true equation of a it, you can't justifiably extrapolate it. If a choice of extrapolation function is random, so is the extrapolation result, as the picture below illustrates:</em></span></p>
<p class="WordSection1" style="padding-left: 30px;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.Extrapolation-3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.Extrapolation_2D00_3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.Extrapolation-3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x445/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.Extrapolation_2D00_3.png" alt="" border="0" /></a></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Imagine you tell your VP that you need to continue fuzzing and budget $2M for that. They may reasonably ask "why?" At that point, you need a better answer than "because we assumed it was a&nbsp;power law", right?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Q: Has there been any past research of this area?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: Plenty, although I haven't seen yet a fuzzing curve equation published. To mention couple examples:</em></span></p>
<div class="WordSection1">
<ul style="padding-left: 30px;">
<ul>
<li><span style="font-family: verdana,geneva;"><em>In 2008, <strong>Jason Shirk, Lars Opstad, and Dave Weinstein </strong>presented their </em><a href="https://channel9.msdn.com/Events/Blue-Hat-Security-Briefings/BlueHat-Security-Briefings-Fall-2008-Sessions-and-Interviews/v8-17"><em>Fuzzed enough? When it&rsquo;s OK to put the shears down</em></a><em> work at Blue Hat 2008. They conducted analysis of fuzzing results for 250+ Windows Vista parsers and found empirically that 500,000 iterations appeared to be enough to detect at least one crash in most crashing parsers. They also ran one of the fuzzers for 27 million iterations but were unable to &ldquo;flatten out&rdquo; the fuzzing curve.</em></span></li>
<li><span style="font-family: verdana,geneva;"><strong><em>Charlie Miller&rsquo;s</em></strong><em> </em><a href="https://fuzzinginfo.files.wordpress.com/2012/05/cmiller-csw-2010.pdf"><em>Fuzzing "truths" discovered while fuzzing 4 products with 5 lines of Python</em></a><em> from BlueHat 2010 is closely related to the topic of this blog post. In that work, the results of &ldquo;dumb&rdquo; fuzzing against four different parsers are discussed. Classic pictures of fuzzing curves are presented and a notion of their saturation is introduced. The approach to fuzzing ROI assessment was in lines of &ldquo;when the curve flattens out, you know you&rsquo;d fuzzed enough&rdquo;. The lack of theoretical ground for such prediction is explicitly called out: &ldquo;I don&rsquo;t know how long the fuzzing needs to continue&hellip; it would be awesome to know how many iterations are required to flatten out each of these curves&rdquo;</em></span></li>
</ul>
</ul>
</div>
<p style="padding-left: 60px;"><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7776.CharlieMillerFuzzing.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7776.CharlieMillerFuzzing.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7776.CharlieMillerFuzzing.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x310/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7776.CharlieMillerFuzzing.png" alt="" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span><span style="font-family: verdana,geneva;"><em>Q: Can this work replace any of the following:</em></span></p>
<ul style="padding-left: 30px;">
<ul>
<li><span style="font-family: verdana,geneva;"><em>Targeted or systematic/deterministic fuzzing?</em></span></li>
<li><span style="font-family: verdana,geneva;"><em>Analysis of additional fuzzing data such as stack traces proximity or code coverage?</em></span></li>
</ul>
</ul>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: No.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>&nbsp;</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Q: Can it provide any of the following:</em></span></p>
<ul style="padding-left: 30px;">
<ul>
<li><span style="font-family: verdana,geneva;"><em>Prediction of the total number of bugs in a product discoverable by any combination of fuzzers?</em></span></li>
<li><span style="font-family: verdana,geneva;"><em>A method to point out specific bugs or areas of higher bug density in the product (as opposed to only counting bugs)?</em></span></li>
<li><span style="font-family: verdana,geneva;"><em>Information on new or existing zero-day vulnerabilities?</em></span></li>
</ul>
</ul>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>A: No.</em></span></p>
<p><span style="font-family: verdana,geneva;">If you are still interested &ndash; let's continue after the Standard Disclaimer:</span></p>
<p><span style="font-family: verdana,geneva;">&lt;Disclaimer&gt;</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva; font-size: x-small;"><em>Information and opinions expressed in this post do not necessarily reflect those of my employer (Microsoft). My recommendations in no way imply that Microsoft officially recommends doing the same. Any phrases where it may look as if I don't care about customer's security or recommend practices that increase the risk should be treated as purely accidental failures of expression. No figures or statements given here imply any conclusions about the actual state of security of Microsoft's or any other products. The intent of this text is to share knowledge that aims to improve security of software products in the face of limited time and resources allocated for development. The emotional content represents my personal ways of writing rather than any part of the emotional atmosphere within the company. While I did my best to verify the correctness of my calculations, they may still contain errors, so I assume no responsibility for using (or not using) them.</em></span></p>
<p><span style="font-family: verdana,geneva;">&lt;/Disclaimer&gt;</span></p>
<p>&nbsp;</p>
<p><br />&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">Derivation of the equation</span></h2>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">The following conditions, further referred to as "static fuzzing", are assumed:</span></p>
<ol>
<li><span style="font-family: verdana,geneva;">The product does not change during fuzzing.</span></li>
<li><span style="font-family: verdana,geneva;">The fuzzer does not experience major configuration changes during fuzzing.</span></li>
<li><span style="font-family: verdana,geneva;">There are no super-crashers in the product. Those are bugs that manifest with probability sufficiently close to 1 (e.g., over 10%). A product with super-crashers in it is likely so unstable that it won&rsquo;t be ready for fuzz testing anyways.</span></li>
<li><span style="font-family: verdana,geneva;">The definitions of bugs uniqueness or exploitability do not change throughout fuzzing.</span></li>
<li><span style="font-family: verdana,geneva;">Fuzzing is random or nearly random in a sense that bug discovery chances do not depend on past results, or such dependency extends only over bug sets much smaller than the total number of bugs found.</span></li>
</ol>
<p><span style="font-family: verdana,geneva;">Let's start with a hypothetical product with <strong>B</strong> bugs in it where each bug has exactly the same fixed probability <strong>p</strong> <strong>&gt; 0</strong> of being discovered upon one fuzzing iteration.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Actually, the "bug" does not have to be a crash, and "fuzzing iteration" does not have to apply exactly against a parser. A random URLs mutator looking for XSS bugs would be described by the same math.</em></span></p>
<p><span style="font-family: verdana,geneva;">Consider one of these bugs. If the probability to discover it in one iteration is <strong>p</strong>, then the probability of not discovering it is <strong>1&nbsp;&ndash;&nbsp;p</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">What are the chances that this bug will not be discovered after <strong>N</strong> iterations, then? In static fuzzing, each non-discovery is an independent event for that bug, and therefore the answer is:</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7888.10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7888.10.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7888.10.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/226x35/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7888.10.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Then, the chance that it will be eventually discovered after <strong>N</strong> iterations is:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1362.20.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1362.20.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1362.20.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/254x43/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1362.20.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Provided there are <strong>B </strong>bugs in the product, each one having the same discovery probability <strong>P(N)</strong>, the mathematical expectation of the number of unique bugs <strong>U</strong> found by the iteration number <strong>N</strong> is:</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1882.30.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1882.30.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1882.30.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/379x41/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1882.30.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">If all bugs in the product had the same discovery probability that would&rsquo;ve been the answer. However, real bugs differ by their &ldquo;reachability&rdquo; and therefore by the chance of being found. Let&rsquo;s account for that.</span></p>
<p><span style="font-family: verdana,geneva;">Consider a product with two bug populations, <strong>B<sub>1</sub></strong> and <strong>B<sub>2</sub></strong>, with discovery probabilities of <strong>p<sub>1</sub></strong> and <strong>p<sub>2</sub></strong> respectively. For each population, the same reasoning as in (10)-(30) applies, and (30) changes to:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7444.40.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7444.40.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7444.40.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x46/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7444.40.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Extending that logic onto an arbitrary number <strong>Q</strong> of populations <strong>{B<sub>q</sub>}</strong> one arrives at:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.50.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.50.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.50.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/360x78/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.50.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">(<strong>q</strong> enumerates bug populations here)</span></p>
<p><span style="font-family: verdana,geneva;">Let&rsquo;s introduce bugs distribution function over discovery probability <strong>G(p)</strong> per definition:</span></p>
<p align="center"><span style="font-family: verdana,geneva;"><strong>B<sub>q</sub>(p) = G(p<sub>q</sub>)*dp</strong></span></p>
<p><span style="font-family: verdana,geneva;">Then (50) is replaced by integration:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6471.60.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6471.60.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6471.60.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/351x66/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6471.60.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Let&rsquo;s recall that <strong>(1&nbsp;‑&nbsp;p)<sup>N</sup> = e<sup>N*Ln(1&nbsp;‑&nbsp;p)</sup></strong>. In the absence of super-crashers <strong>G(p)</strong> is significant only at very small probabilities <strong>p&nbsp;&lt;&lt;&nbsp;1</strong>. Thus, the logarithm could be expanded into first order series as <strong>Ln(1‑p)&nbsp;&asymp;&nbsp;‑p</strong>, and then:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3884.100.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3884.100.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3884.100.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/355x81/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3884.100.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">This is the generic analytical expression for a static fuzzing curve.</span></p>
<p><span style="font-family: verdana,geneva;">Can it be simplified further? I believe the answer is "no", and here is why.</span></p>
<p><span style="font-family: verdana,geneva;">Let's look at the first derivative of <strong>U(N)</strong> which is easy to obtain from (100):</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1602.110.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1602.110.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1602.110.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/311x83/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1602.110.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Since <strong>G(p)</strong> is a distribution function, it is non-negative, as well as the exponent and <strong>p</strong>. Therefore,</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1207.dUdN.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1207.dUdN.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1207.dUdN.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/136x60/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1207.dUdN.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">for any <strong>N</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">Similarly, the 2<sup>nd</sup> derivative could be analyzed to discover that it is strictly non-positive:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6813.d2UdN2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6813.d2UdN2.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6813.d2UdN2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/158x56/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6813.d2UdN2.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Repeating the differentiation, it is easy to show that</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0513.dnUdNn.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0513.dnUdNn.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0513.dnUdNn.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/213x73/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0513.dnUdNn.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">for any <strong>n&gt;=0</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">That is a very prominent property. It is called a <a href="https://en.wikipedia.org/wiki/Total_monotonicity">total (or complete) monotonicity</a> and only few functions possess it. Sergei Bernstein <a href="https://rd.springer.com/article/10.1007%2FBF02592679">has shown</a> in 1928 [see <a href="https://en.wikipedia.org/wiki/Bernstein%27s_theorem_on_monotone_functions">Wikipedia summary</a>] that <strong>any</strong> function that is totally monotonic must be a mixture of exponential functions and its most general representation is:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5466.120.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5466.120.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5466.120.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/400x67/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5466.120.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">where <strong>m(x)</strong> is non-negative.</span></p>
<p><span style="font-family: verdana,geneva;">In our case the first derivative of <strong>U(N)</strong> is totally monotonic. Therefore, <strong>U(N)</strong> with possible addition of some linear terms like <strong>a</strong> <strong>+</strong> <strong>b*N</strong> belongs to the same class of functions. Therefore, (100) is already a natural representation for <strong>U(N)</strong> and there is no simpler one. Any attempt to represent <strong>U(N)</strong> in an alternative non-identical way (e.g., via a finite polynomial) would destroy the complete monotonicity of <strong>U(N)</strong>, and that would inevitably surface as errors or peculiarities in further operations with <strong>U(N)</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">So let&rsquo;s settle upon (100) as the equation of a static fuzzing curve.</span></p>
<p>&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">G(p)?</span></h2>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">But wait! Expression (100) depends on the unknown function <strong>G(p)</strong>. So it's not really the answer. It just defines one unknown thing through another. How is that supposed to help?</span></p>
<p><span style="font-family: verdana,geneva;">In a sense, that's true. The long and official response to that is this: you start fuzzing, observe the initial part of your fuzzing curve, solve the inverse problem to restore <strong>G(p)</strong> from it, plug it back to (100) and arrive to the analytical expression of your specific curve.</span></p>
<p><span style="font-family: verdana,geneva;">But here is a little shortcut that can make things easier.</span></p>
<p><span style="font-family: verdana,geneva;">Every function could be represented (at least for integration purposes) as a collection of Dirac delta-functions, so let's do that with <strong>G(p)</strong>:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6305.130.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6305.130.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6305.130.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/288x65/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6305.130.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">where <strong>a<sub>i</sub></strong>&gt;0 and <strong>p<sub>i</sub></strong>&gt;0 are some fixed parameters.</span></p>
<p><span style="font-family: verdana,geneva;">Most of the time, only small ranges of probabilities contribute significantly to fuzzing results. That's because "easy" bugs are already weeded out, and "tough" ones are mostly unreachable. To reflect that, let's replace the infinite sum with a fixed small degree <strong>D</strong> corresponding to the most impactful probability ranges:</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3225.135.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3225.135.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3225.135.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/287x72/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3225.135.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Substituting that into (100) results in the following fuzzing curve equation:</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1172.140.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1172.140.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1172.140.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/311x79/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1172.140.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">After some experiments I found that <strong>D</strong> = <strong>1</strong> already fits and extrapolates real fuzzing curves reasonably well, while <strong>D</strong> = <strong>3</strong> produces a virtually perfect fit -- at least on the data available to me.</span></p>
<p><span style="font-family: verdana,geneva;">Finally, at the very early fuzzing stages (140) could be reduced even further to:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6038.145.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6038.145.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6038.145.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/317x49/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6038.145.png" alt="" border="0" /></a></p>
<p><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2015/01/06/equation-of-a-fuzzing-curve-part-2-2.aspx">Continue to Part 2</a>.&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Estimating Hidden Bug Count -- Part 3/3</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/estimating-hidden-bug-count-part-33</link>
      <pubDate>Fri, 03 Oct 2014 10:15:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/10/03/estimating-hidden-bug-count-part-33/</guid>
      <description><![CDATA[Part 4: Step By Step Guide
This is just a summary of the previous chapters as a flow chart (click...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<h2><span style="font-family: verdana,geneva;">Part 4: Step By Step Guide</span></h2>
<p><span style="font-family: verdana,geneva;">This is just a summary of the previous chapters as a flow chart (<a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-1-3.aspx">click here for the derivation of the method</a>):</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7271.image001.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7271.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7271.image001.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7271.image001.png" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Here variable meanings are:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;">External bugs, or <strong>E</strong> &ndash; the count of active (not fixed) bugs reported against the product externally</span></li>
<li><span style="font-family: verdana,geneva;">Internal bugs, or <strong>I</strong> &ndash; the count of active bugs reported against the product internally</span></li>
<li><span style="font-family: verdana,geneva;">Shared bugs, or <strong>S</strong> &ndash; the count of active bugs reported by both internal and external sources</span></li>
<li><span style="font-family: verdana,geneva;"><strong>E<sub>f</sub></strong>, <strong>I<sub>f</sub></strong>, and <strong>S<sub>f</sub></strong> are the same categories as above but fixed (not active)</span></li>
<li><span style="font-family: verdana,geneva;"><strong>&beta;</strong> is the ratio of how faster Shared bugs are fixed in comparison to the Internal bugs.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>f </strong>is the fix rate per (small) unit of time relative to bugs quantity. For example, if out of 20 product bugs 2 are fixed per day, the fix rate is 2/20 = 0.1/day.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>B<sub>0</sub></strong> is the count of all bugs (known and hidden) in the product when versions were split</span></li>
<li><span style="font-family: verdana,geneva;"><strong>B </strong>is the current bug count (both known and unknown bugs)</span></li>
<li><span style="font-family: verdana,geneva;"><strong>t </strong>is the observation time</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">That&rsquo;s it &ndash; I hope you find it useful for assessing the hidden part of your product's security "iceberg".</span></p>
<p>&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">Part 5: Nothing is Perfect</span></h2>
<p><span style="font-family: verdana,geneva;">Obviously, the method has its limitations, and some of them should be named:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;">It's not very precise. Statisticians can easily point out numerous reasons why. My experience suggests that its results should be treated as approximate with an error margin on the order of &plusmn;2x.</span></li>
<li><span style="font-family: verdana,geneva;">You need both internal and external bugs to be filed against the product, which is rather embarrassing. Yes, there are methods that don't require that (i.e., they can work with internal sources only), but those are more complex.</span></li>
<li><span style="font-family: verdana,geneva;">&hellip;and you need those bugs in statistically significant quantities. I'd say at least about 10 Internal and External bugs, and at least 3-4 Shared among them.</span></li>
<li><span style="font-family: verdana,geneva;">Detecting Shared bugs requires sound engineering practices and great bug tracking discipline in the product.</span></li>
<li><span style="font-family: verdana,geneva;">It is assumed that Internal and External bugs are not correlated. That means both sources work independently and don't know anything about each other. In practice, some correlation might exist, both positive ("look where the attackers are focusing") and negative ("search for known bugs before filing your own ones").</span></li>
<li><span style="font-family: verdana,geneva;">The method would have difficulty with continuously updated online service, since their time-to-fix window is so short that virtually no shared bugs have any chances to arise.</span></li>
<li><span style="font-family: verdana,geneva;">The results apply, technically, to only one of two product versions. To carry over the conclusions to both versions, codebases must remain similar.</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">That's it. Thank you for reading and have a great day.</span></p>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Estimating Hidden Bug Count -- Part 2/3</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/estimating-hidden-bug-count-part-23</link>
      <pubDate>Tue, 30 Sep 2014 13:09:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/09/30/estimating-hidden-bug-count-part-23/</guid>
      <description><![CDATA[Previous part: introduction and simpler theory



Next:&nbsp;Flowchart Summary and...]]></description>
      <content:encoded><![CDATA[<p class="WordSection1"><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-1-3.aspx">Previous part: introduction and simpler theory</a></span></p>
<div class="WordSection1">
<div class="WordSection1">
<div class="WordSection1">
<div class="WordSection1"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/10/03/estimating-hidden-bug-count-part-three-3.aspx">Next:&nbsp;Flowchart Summary and Limitations</a></div>
</div>
</div>
</div>
<p class="WordSection1">&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">Part 3: Harsh Reality</span></h2>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">That simple logic is nice, but practice makes it questionable for at least two reasons:</span></p>
<ol>
<li><span style="font-family: verdana,geneva;">Bugs found by either of the parties are fixed. After that, another party gets no chances to find them again.</span></li>
<li><span style="font-family: verdana,geneva;">Product development results in code changes and new code introductions, further obscuring the picture.</span></li>
</ol>
<p><span style="font-family: verdana,geneva;">The good news is that in &ldquo;classic&rdquo; software with a clear notion of &ldquo;version&rdquo; (such as an operating system or a browser) both effects could be accounted for.</span></p>
<p><span style="font-family: verdana,geneva;">The 2<sup>nd</sup> one is easier. By shipping version 1.0 of the product to the customer you effectively create a nearly frozen snapshot of it affected only by bug fixes. External Researchers will be testing that version. Of course, Internal Engineering will be working with version 2.0 which is different. But it is usually quite easy to filter their discoveries (for analytical purposes) down to the codebase common with 1.0. So if we apply the method to the codebase that is shared between 1.0 and 2.0, we can work around the issue #2.</span></p>
<p><span style="font-family: verdana,geneva;">#1 is trickier. Intuitively it's clear that shared bugs can still appear in a changing product if both parties hit the same bug while it's active &ndash; i.e., between the moments it was found and fixed. But knowing the quantity of such discoveries requires more precise tracking of bug counts at each intermediate moment of time.</span></p>
<p><span style="font-family: verdana,geneva;">And one of the ways to achieve that is to describe bug populations with a system of differential equations.</span></p>
<p><span style="font-family: verdana,geneva;">The next few pages treat that subject. If you are not interested, just <a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/10/03/estimating-hidden-bug-count-part-three-3.aspx">jump directly to the Step by Step Guide</a> where the results are applied.</span></p>
<h4><span style="font-family: verdana,geneva;">Bug Population Dynamics: General Case</span></h4>
<p><span style="font-family: verdana,geneva;">Let&rsquo;s introduce the following variables:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;"><strong>E</strong> is the count of externally found <strong>active</strong> (i.e., not fixed yet) bugs that are <strong>not</strong> Shared bugs (so they are reported only once and by external sources only)</span></li>
<li><span style="font-family: verdana,geneva;"><strong>I </strong>is<strong> </strong>the count of internally found active non-shared bugs</span></li>
<li><span style="font-family: verdana,geneva;"><strong>S</strong> is the count of active shared bugs, found by both internal and external entities. That does not include internal Duplicate bugs where the same bug is seen by two or more <strong>internal</strong> entities.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>B</strong> is the total bug count in the system (obviously we are talking about some fixed class of them, e.g., memory corruptions)</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">Each of them is a function of observation time <strong>t</strong> passed since version 1.0 of the product was released.</span></p>
<p><span style="font-family: verdana,geneva;">We will assume that bugs are found and fixed with rates linearly proportional to their amount. While that is obviously a simplification, it is probably somewhat close to the reality, since when bug counts increase more developer resources are poured in to fix them.</span></p>
<p><span style="font-family: verdana,geneva;">That will bring along a few more variables:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;"><strong>x</strong> is the rate of external bugs search and reporting per small unit of time (e.g., one month), relative to the total number of bugs. For instance, if x = 0.1 that mean Externals can find 10% of the bugs in a product in one month.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>y</strong> is bugs discovery rate for internal engineering, defined in the same manner as <strong>x</strong>.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>f</strong> is the rate of internal bug fixing relative to the total count of active bugs, per unit of time [i.e., <strong>f</strong> = <strong>-(dA/dt)/A</strong>, where <strong>A</strong> is the count of the active bugs].</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">Strictly speaking, none of these variables are constants. They change over time and as bug counts change. But we need to start somewhere, so let's use that model as a first order approximation.</span></p>
<p><span style="font-family: verdana,geneva;">We will also assume that <strong>f</strong> is the same for all bugs regardless of whether they are internal or external. The case of significant difference could be reduced to the solution of uniform <strong>f</strong>, as will be shown later.</span></p>
<p><span style="font-family: verdana,geneva;">In these variables, the dynamics of each bug category is described by the following system of differential equations:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1588.20.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1588.20.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1588.20.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/429x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1588.20.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">The first line says that active External bugs <strong>E</strong> are found with frequency <strong>x</strong> per year proportionally to the total bugs count <strong>B</strong> in the system, and are fixed with frequency <strong>f</strong> proportionally to their amount <strong>E</strong>, and sometimes also move away from External to Shared category when Internals find them, which happens with frequency <strong>y</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">The 2<sup>nd</sup> equation is the same but with respect to the Internal active bugs count <strong>I</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">The 3<sup>rd</sup> says that active Shared bugs are created when one party hits another's existing active bugs, and destroyed by fixing with a common fix rate <strong>f</strong>. We assume here that internal sources don't have knowledge of external bugs to check for duplicates before filing. That's not exactly the case but is likely close to reality; if needed, a modification to account for that effect is relatively easy.</span></p>
<p><span style="font-family: verdana,geneva;">Finally, the 4<sup>th</sup> equation describes the overall bug population, and states simply that the total bug count in the product is reduced when active bugs are fixed.</span></p>
<p><span style="font-family: verdana,geneva;">There is really nothing of a rocket science behind solving that system, but it takes a few pages if done by hand on paper, so I&rsquo;ll spare you from that and will just write down the answer that shows how each bug category behaves over time:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4885.30-60.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4885.30_2D00_60.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4885.30-60.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4885.30_2D00_60.png" alt="" border="0" /></a>&nbsp;</span></p>
<p><span style="font-family: verdana,geneva;">where</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4466.Lanbthas.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4466.Lanbthas.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/4466.Lanbthas.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/329x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/4466.Lanbthas.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">And <strong>B<sub>0</sub></strong> is the total initial bug count in the system.</span></p>
<p><span style="font-family: verdana,geneva;">Hmm?&hellip;</span></p>
<p><span style="font-family: verdana,geneva;">This is ugly.</span></p>
<p><span style="font-family: verdana,geneva;">Extracting <strong>B<sub>0</sub></strong> from that system is technically possible. You "just" fit those curves to the observed bug rates&hellip; in theory&hellip; and obtain miserable nonsense in practice because of the data noise, model imperfections, and unmanageable complexity.</span></p>
<p><span style="font-family: verdana,geneva;">Fortunately, we don&rsquo;t need full precision here. In real life we often deal only with special cases &ndash; such as very short or very long observation times, or very high fix frequency, and so on. And it turns out that in this case it is possible to effectively &ldquo;tile&rdquo; most practically useful situations with those simpler special cases.</span></p>
<p><span style="font-family: verdana,geneva;">So let&rsquo;s derive them from the general form.</span></p>
<h4><span style="font-family: verdana,geneva;">Case A. Short Observation Times</span></h4>
<p><span style="font-family: verdana,geneva;">At very short observation durations, such that <strong>t</strong> &lt;&lt; <strong>1</strong>/<strong>f</strong> and <strong>t</strong> &lt;&lt; <strong>1</strong>/(<strong>x</strong>+<strong>y</strong>), functions [30] &ndash; [60] could be expanded into low-order time series, and then it is possible to show that:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5226.image012.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5226.image012.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5226.image012.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/383x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5226.image012.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Since nobody prevents us from declaring t = 0 at any arbitrary moment of product lifetime, this means a simple and very powerful thing: as long as there are statistically enough <strong>active</strong> bugs of Internal, External, and Shared category <strong>opened far less than 1</strong>/<strong>f time ago</strong>, the total bug count in the system can be estimated at the current moment of time as simply as:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6862.image013.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6862.image013.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6862.image013.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/170x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6862.image013.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">This works regardless of the frequency of product fixes and (as will be shown later) even if there is an asymmetry between fix rates for internal and external bugs.</span></p>
<h4><span style="font-family: verdana,geneva;">Case B. Long Observation Times</span></h4>
<p><span style="font-family: verdana,geneva;">At very long observation times such that <strong>t</strong> &gt;&gt; <strong>1/f</strong> and <strong>t</strong> &gt;&gt; <strong>1</strong>/(<strong>x</strong>+<strong>y</strong>) most of the bugs found are fixed, so there are very few (if any at all) of them in the active state suitable for use in expression [80].</span></p>
<p><span style="font-family: verdana,geneva;">However, <strong>fixed</strong> bugs of each category will probably be numerous. Can we use their counts instead?</span></p>
<p><span style="font-family: verdana,geneva;">Let&rsquo;s call them <strong>E<sub>f</sub></strong>, <strong>I<sub>f</sub></strong>, and <strong>S<sub>f</sub></strong> -- similarly to the corresponding active categories (index &ldquo;f&rdquo; stands for &ldquo;fixed&rdquo;). Each of those is obtained by integrating the corresponding active bug fixes over time, e.g.:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5545.image014.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5545.image014.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5545.image014.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/208x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5545.image014.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Using these variables and simplifying the full solution for the case of <strong>t -&gt; &infin;</strong>, one can show that</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3264.image015.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3264.image015.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3264.image015.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/209x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3264.image015.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">where</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7183.110-120.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7183.110_2D00_120.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7183.110-120.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/202x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7183.110_2D00_120.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">[And estimates for <strong>x</strong> and <strong>y</strong> could be used to verify if indeed <strong>t</strong> &gt;&gt; <strong>1</strong>/(<strong>x</strong>+<strong>y</strong>)]</span></p>
<p><span style="font-family: verdana,geneva;">The relation between the current bug count <strong>B(t)</strong> and the initial one <strong>B<sub>0 </sub></strong>is:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0447.image018.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0447.image018.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/0447.image018.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/254x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/0447.image018.png" alt="" border="0" /></a></span></p>
<p>&nbsp;</p>
<h4><span style="font-family: verdana,geneva;">Case C. Sufficiently High Fix Rate</span></h4>
<p><span style="font-family: verdana,geneva;">When <strong>f</strong> &gt;&gt; <strong>(x+y)</strong> another useful simplification arises for times such that <strong>t</strong> &gt;&gt; <strong>1/f</strong>:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/8625.140-160.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/8625.140_2D00_160.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/8625.140-160.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/508x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/8625.140_2D00_160.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">And then</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5047.170-180.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5047.170_2D00_180.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5047.170-180.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/377x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5047.170_2D00_180.png" alt="" border="0" /></a>&nbsp;</span></p>
<p><span style="font-family: verdana,geneva;">with <strong>x</strong> and <strong>y</strong> provided by [110] and [120].</span></p>
<p><span style="font-family: verdana,geneva;">This set of simplifications actually covers (mostly) all of the problem space. Indeed:</span></p>
<p><span style="font-family: verdana,geneva;">a) If there is statistically significant number of active bugs in the product, expression [80] approximately solves the problem, since we can declare <strong>t</strong> = 0 at any moment of time.</span></p>
<p><span style="font-family: verdana,geneva;">b) If not, but there is statistically significant number of fixed/closed bugs in the product that means the observation time is far greater than typical fix time. In other words, we are in the <strong>t</strong> &gt;&gt; <strong>1/f</strong> domain. Since most bugs are in the fixed rather than active state that also means <strong>f</strong> &gt;&gt; <strong>(x+y) </strong>and expressions [170] and [180] provide the answer.</span></p>
<p><span style="font-family: verdana,geneva;">e) In other cases there are either not enough bugs to make a call, or we are in the "narrow" intermediate time window of <strong>t</strong> &asymp; <strong>1/f</strong>. We should either wait, or use the full solution [30]-[60].</span></p>
<h4><span style="font-family: verdana,geneva;">Case D. Asymmetric Fix Rate</span></h4>
<p><span style="font-family: verdana,geneva;">Situations when fix rates for internal and external bugs are significantly different are conceivable. How would our logic work for them?</span></p>
<p><span style="font-family: verdana,geneva;">Let <strong>f</strong> be the fix rate of internal bugs, <strong>&alpha;f</strong> &ndash; fix rate of external bugs, and <strong>&beta;f</strong> &ndash; of shared bugs. System [20] becomes then:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6406.190.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6406.190.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6406.190.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/409x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6406.190.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Technically, it could be solved from scratch, but we can avoid that by making variable substitution:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/2364.200.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/2364.200.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/2364.200.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/339x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/2364.200.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">and introducing an intermediate variable</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6607.image030.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6607.image030.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6607.image030.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/136x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6607.image030.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">That would result in the solutions of the same form as [30] &ndash; [60] with the following input variable replacements:</span></p>
<p><span style="font-family: verdana,geneva;">For <strong>E</strong>(t): <strong>t</strong> -&gt; <strong>&alpha;t</strong>, <strong>x</strong>-&gt; <strong>x/&alpha;</strong>, <strong>y</strong>-&gt;<strong>y/&alpha;</strong>&nbsp;&nbsp;&nbsp; [210]</span></p>
<p><span style="font-family: verdana,geneva;">For <strong>S</strong>(t): <strong>t</strong> -&gt; <strong>&beta;</strong>t, <strong>x</strong>-&gt; <strong>x/&beta;</strong>, <strong>y</strong>-&gt;<strong>y/&beta;</strong>&nbsp;&nbsp;&nbsp; [220]</span></p>
<p><span style="font-family: verdana,geneva;">The short-term estimate then is exactly the same as [80] (i.e., not sensitive to the asymmetry):</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.image013.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.image013.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3513.image013.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/170x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3513.image013.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Long-term is more difficult because sufficiently high fix rate discrepancy may cause a situation where ft &lt;&lt; 1 while &alpha;*ft &gt;&gt; 1 at the same time. Technically, the answer would need to be derived from full case [30]-[60], but there is one potential alternative here.</span></p>
<p><span style="font-family: verdana,geneva;">That is to consider <strong>internal</strong> product version as the primary target, with <strong>f</strong> being the frequency of <strong>internal</strong> fixes, and <strong>E</strong> meaning not all externally reported bugs but only those that affect the internal product version currently in development. If fix rate discrepancy is smaller for the internal version (which is probably more reasonable to expect), long-term estimates [170]-[180] may still be applicable within that interpretation, provided the following changes in variables interpretations:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;"><strong>f</strong> is the frequency of internal fixes for bugs of internal origin</span></li>
<li><span style="font-family: verdana,geneva;"><strong>E </strong>is the count of those externally reported bugs that affect the internal product version currently in development</span></li>
<li><span style="font-family: verdana,geneva;"><strong>B</strong> is the count of those bugs in internal (2.0) version that affect external (1.0) version</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">Also the expressions for <strong>x</strong> and <strong>y</strong> in this case change to</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7215.240-250.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7215.240_2D00_250.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7215.240-250.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/237x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7215.240_2D00_250.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/10/03/estimating-hidden-bug-count-part-three-3.aspx">Next: flowchart summary</a></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Estimating Hidden Bug Count -- Part 1/3</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/estimating-hidden-bug-count-part-13</link>
      <pubDate>Tue, 30 Sep 2014 12:57:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/09/30/estimating-hidden-bug-count-part-13/</guid>
      <description><![CDATA[Part 1: Introduction and Basic Theory

Part 2: Accounting for Bug Fixes


Part 3: Flowchart...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-1-3.aspx">Part 1: Introduction and Basic Theory</a></div>
<div class="WordSection1">
<div class="WordSection1"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-2-3.aspx">Part 2: Accounting for Bug Fixes</a></div>
<div class="WordSection1">
<div class="WordSection1">
<div class="WordSection1"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/10/03/estimating-hidden-bug-count-part-three-3.aspx">Part 3: Flowchart Summary and Limitations</a></div>
<div class="WordSection1">&nbsp;</div>
</div>
</div>
</div>
<h2 class="WordSection1"><span style="font-family: verdana,geneva;">Part 1: Introduction</span></h2>
<p class="WordSection1">&nbsp;</p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Probably every piece of software has some defects in it. Known defects (also called bugs) are found by manufacturers and users and fixed. Unknown ones remain there, waiting to be discovered some day.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">The question is: how big is that unknown set?</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">One approach to that problem is presented here. It is not the most precise and definitely has some limitations. But it is relatively simple and straightforward to derive, with no secret know-hows, so I decided to share it.</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">Every time I mention "counting unknown bugs" questions arise that are worth a little FAQ, so here it is:</span></p>
<p class="WordSection1"><span style="font-family: verdana,geneva;">&lt;Little FAQ&gt;</span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong><em>Q: </em></strong><em>Why would your even need to know the count of unknown bugs?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong><em>A:</em></strong><em>&nbsp; First, it sets the expectations. Knowing how many bugs are still there provides an upper cup on your support/response story. Second, it provides a quantitative assessment of the engineering effectiveness, by comparing what&rsquo;s been found to what still remains in there.</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong><em>Q</em></strong><em>: Is it even possible to count <strong>all</strong> unknown bugs?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong><em>A</em></strong><em>: Strictly speaking, no. Some "bugs" are infinite by nature (e.g., new feature suggestions). Some bug may belong to classes yet unknown -- for example, there was no way count CSRF bugs before CSRF was invented. So we are talking here about counting unknown bugs of <strong>known, specific class/nature</strong>, defined at the <strong>today's</strong> level of technology.</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>Q</strong>: Any examples of those classes?</em></span></p>
<p class="WordSection1" style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>A</strong>: It&rsquo;s got to be something of a common nature, with similar lifetime, discovery methods, and statistical properties. For instance:</em></span></p>
<div class="WordSection1" style="padding-left: 30px;">
<ul style="padding-left: 30px;">
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Memory corruption issues</em></span></li>
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>XSS bugs</em></span></li>
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Performance degradations above certain threshold</em></span></li>
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Errors in documentation</em></span></li>
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Setup failures</em></span></li>
</ul>
</div>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>Q</strong>: The idea of "total bug count" looks like an abstract theory rather than something with a practical meaning. Is it anything material?</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>A</strong>: To translate that into a practical world, think these measurable examples:</em></span></p>
<ul style="padding-left: 60px;">
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>The total number of bugs to be found with current discovery methods if testing continues indefinitely</em></span></li>
<li style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>The number of bugs to be reported by customers if the last version of the product remains on the market for infinite time</em></span></li>
</ul>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>Q</strong>: Fine, the theory may look good, but can it be validated? Does it have a predictive power?</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em><strong>A</strong>: Here are some ways to validate it that I see.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>The first is side by side products comparison. If products A and B of similar functionality and similar user base have vastly different hidden bug counts, it is reasonable to expect that their post-release defects discovery rates would be similarly different. </em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Second, this method derives an analytical description of some bug curves over time. That equals to a long-term bug discovery rate forecast. Unfortunately I had a chance to do only very rudimentary work on such forecasting so this still needs more research. Yet this approach could probably be used for validation purposes as well.</em></span></p>
<p><span style="font-family: verdana,geneva;">&lt;/Little FAQ&gt;</span></p>
<p><span style="font-family: verdana,geneva;">With that, let's go to the theory.</span></p>
<p><span style="font-family: verdana,geneva;">&lt;Disclaimer&gt;</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>While I did my best to verify the correctness of calculations, they may still contain errors, so I assume no responsibility for using (or not using) them. </em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>The method presented here is a research work and carries no guarantees.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Opinions expressed in the post do not necessarily reflect those of my employer (Microsoft). My recommendations in no way imply that Microsoft officially supports them or recommends doing the same. Any phrases where it may look as if I don't care about customer's security, or recommend practices that seemingly increase the risk should be treated as purely accidental failures of expression. No figures or statements given here imply any conclusions about the actual state of security of any Microsoft products. All numeric figures are purely fictitious examples.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>The intent of this text is to share the knowledge and improve security of software products across the industry. The emotional content represents my personal ways of writing rather than any part of the emotional atmosphere within the company.</em></span></p>
<p><span style="font-family: verdana,geneva;">&lt;/Disclaimer&gt;</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<h2><span style="font-family: verdana,geneva;">Part 2: Ideal World Theory</span></h2>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;">The approach is based on the <a href="https://en.wikipedia.org/wiki/Mark_and_recapture">Capture &amp; Recapture</a> method that has been used in biology and criminalistics since at least 1930s. Conceptually, it is very simple.</span></p>
<p><span style="font-family: verdana,geneva;">When two entities independently search for something in a shared space (e.g., for birds in a forest), they occasionally may stumble upon each other's past discoveries. The tighter the space to search is the more often bumping into each other's finds will occur. By measuring the frequency of such encounters it is possible to reason about the total size of the space being searched.</span></p>
<p><span style="font-family: verdana,geneva;">To formalize it, let's use a fictitious product example. In that product, no bug fixes or code changes ever happen at all. Two groups of people randomly and independently of each other look for bugs in it. The first group is called Internal Engineering, the 2nd -- External <span style="text-decoration: line-through;">Attackers</span> Researchers. Think of them as of two projectors randomly scooping with light rays through the product and sometimes highlighting the same spots:</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5125.image001.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5125.image001.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5125.image001.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5125.image001.jpg" alt="" border="0" /></a></p>
<p><span style="font-family: verdana,geneva;">Let&rsquo;s put that into basic math:</span></p>
<p><span style="font-family: verdana,geneva;">&lt;The Math&gt;</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">Let <strong>B</strong> be the total count of bugs of a particular class in the product, both known and unknown.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">Detection effectiveness of the Internal Engineering process is <strong>y</strong> per year. That means, they find <strong>I&nbsp;</strong>=&nbsp;<strong>y</strong>*<strong>B</strong> bugs in one year.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">Detection &amp; reporting effectiveness of External Researchers is <strong>x</strong>. They find and report <strong>E</strong>&nbsp;=&nbsp;<strong>x</strong>*<strong>B</strong> bugs through the same time.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">If these processes are independent, then&nbsp;it does not matter whether External Researchers are looking for already known or yet unknown bugs. In either case their chances of success are <strong>x</strong>. Therefore they will find <strong>x</strong> of those bugs that have already been found internally, which would be <strong>x*I&nbsp;</strong>bugs. These are the shared bugs, known to both Internal and External parties. Let&rsquo;s denote their count through <strong>S</strong> = <strong>x*I</strong>.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">Now let's substitute into that the already known value of <strong>I</strong> =&nbsp;<strong>y</strong>*<strong>B</strong>, so <strong>S&nbsp;=&nbsp;x*I&nbsp;=&nbsp;x*y*B</strong>.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">Then let's construct an expression of the form <strong>I</strong>*<strong>E</strong>/<strong>S</strong>. What does it evaluate to?</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">It&rsquo;s easy: <strong>I</strong>*<strong>E</strong>/<strong>S</strong> = (<strong>y</strong>*<strong>B</strong>)*(<strong>x</strong>*<strong>B</strong>)/(<strong>y</strong>*<strong>x</strong>*<strong>B</strong>) = (<span style="text-decoration: line-through;">y</span>*<strong>B</strong>)*(<span style="text-decoration: line-through;">x</span>*<strong>B</strong>)/(<span style="text-decoration: line-through;">x</span>*<span style="text-decoration: line-through;">y</span>*<strong>B</strong>) = <strong>B</strong>*<strong>B</strong>/<strong>B</strong> = <strong>B.</strong></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">In other words, the total number of bugs (both known and unknown) in such an ideal product is estimated simply as:</span></p>
<p style="padding-left: 30px;" align="center"><span style="font-family: verdana,geneva;"><strong>B = I*E/S&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </strong>[10]</span></p>
<p><span style="font-family: verdana,geneva;">&lt;/The Math&gt;</span></p>
<p><span style="font-family: verdana,geneva;">For example, let's say that in one year Internal Engineering has found 40 bugs in a product. External Researchers have found 20 bugs in the same time, and out of those twenty 4 bugs were the same as internally known bugs. That means, Externals were capable of detecting 4/20 = 20% of known bugs. If unknown bugs are not fundamentally different, that means Externals' detection rate is also 20% for unknown (and all) bugs. And that 20% efficiency is represented by 20 external discoveries. Therefore, the total bug count in the system is 20/0.20 = 100.</span></p>
<p><span style="font-family: verdana,geneva;">Although very simple, formula [10] already enables reasonable estimates in many cases. If a product in question is not changing much through the duration of observation, just multiplying the number of internal finds by the number of external ones, and dividing that by the number of shared bugs is sufficient to assess the remaining defects count.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>And how do you count shared bugs? Make a list of bugs that affected version 1.0. Cross out those that also affected 2.0. The remaining are those that hit 1.0 <strong>exclusively</strong>. So most likely we did something to 2.0 that has prevented those bugs. Look at the 2.0's code where those bugs originally were in version 1.0. See what's changed there. Often, that would be a fix to an internally found bug. Often something completely oblivious of the externally reported problem :)) Yet this is a shared find. Count it.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Another source is your Duplicate bugs. See if any of them are resolved to the externally reported cases.</em></span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/09/30/estimating-hidden-bug-count-part-2-3.aspx">Next part: accounting for bug fixes</a></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Practice and Theory of Security Reviews -- Part 3</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/practice-and-theory-of-security-reviews-part-3</link>
      <pubDate>Mon, 04 Aug 2014 12:39:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/08/04/practice-and-theory-of-security-reviews-part-3/</guid>
      <description><![CDATA[Problem introduction and disclaimer.
Security Review Heuristics Zoo.&nbsp;
Part 3 --...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<div class="WordSection1">
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/18/practice-and-theory-of-security-reviews.aspx">Problem introduction and disclaimer</a>.</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/23/security-reviews-the-heuristics-zoo-part-1-2.aspx">Security Review Heuristics Zoo</a>.&nbsp;</span></p>
<h2><span style="font-family: verdana,geneva;">Part 3 -- Reflections</span></h2>
<p><span style="font-family: verdana,geneva;">Or rather a few closing notes...</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Can you quantify "product security"?</strong></span></p>
<p><span style="font-family: verdana,geneva;">Usually when people start talking about "X being 23% more secure than Y" I just snort. However, with the notion of features interaction complexity, we can at least try to approach that problem from a quantitative side.</span></p>
<p><span style="font-family: verdana,geneva;">The idea is to assign a numeric security measure <strong>L</strong> to a product if all features interactions up to the complexity level <strong>L</strong> for most industry-recognized representations of the product have been analyzed and checked for security. Yes, "most representations" is not very precise, but as in practice there are not so many of them (about 10?), this still may offer a workable yardstick for a quantitative assessment. The scale would look then something like this:</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Level 0</strong>. No systematic security work has been done even with individual features. Well-recognized security industry standards are likely violated (e.g., credit card numbers are sent over WiFi in clear). A product almost certainly contains glaring security flaws easily discovered by non-professionals.</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Level 1</strong>. Security coverage has been achieved for isolated features of a product. It is probably free from the most common security failures associated with its domain of operation.&nbsp; No pairwise component interactions have been seriously considered though, so security weaknesses still arise in corner customer scenarios; those would require some basic level of security expertise to recognize them. (e.g., a customer is authenticated at the product's Web site, but critical operations do not verify that the Authentication has not expired yet, so a CSRF is considerable).</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Level 2</strong>. All elements and their pairwise interactions are covered for most of industry recognized ways to re-factor a product into elements. Theoretically, a well-done Threat Modeling can achieve that at least from the design standpoint. Finding a security hole would require pushing a product very far into an unexpected state. Most pentesing vendors would still be able to discover scattered holes in such a product with enterprise-level funding (e.g., via fuzzing).</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Level 3</strong>. Exhaustive verification of all three-way interactions is conducted for multiple re-projections. A product is solid against most flavors of nearly all industry-recognized attacks. Finding a new security hole in it costs O(<strong>N<sup>4</sup></strong>) effort with respect to the number of elements logically comprising a product. In practice this would require at least billions of attempts. State-sponsored agencies can probably still run effective security offence against products at that security level, with costs expressed in 8-9 digit figures.</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Level 4</strong>. All 4-way interactions have been verified as safe. I would say this is what a military-grade security *should* look like. Compromising such a product would take computational resources of leading world superpowers and cost significant chunks of their yearly GDPs. Security holes at this level would literally be solitary, unique in nature, and kept under high secrecy as important military assets.</span></p>
<p><span style="font-family: verdana,geneva;">And so on.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Security Career Roles</strong></span></p>
<p><span style="font-family: verdana,geneva;">Every company needs "security people", but whom exactly? While most security specialists can wear multiple hats at the same time, they also have their natural preferences. And those preferences could be classified within the definitions of this article as following:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;"><strong>Security Auditor.</strong> They work "horizontally", quickly and effectively scaling across largest products. They typically are familiar with all classes of security attacks, are up-to-date on the most recent news in that space, and easily pass an interview on that subject. They may not always be exceptionally technical, and probably won't spend too much time looking for new unusual attacks. But they are good at grasping a large picture and factoring it into a set of actionable work items, so if you need someone to manage a security compliance program for your product, an Auditor would be an ideal choice. They tend to be process-oriented and naturally plug in into existing workflows.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>Security Reviewer</strong>. They prefer to apply more computationally expensive techniques like Threat Modeling or Scenario Fuzzing to discover security holes. They like analyzing complex interactions, looking for the unknown, creatively discovering new classes of attacks. They would have sufficient technical depth to talk to developers in their language, and are good at quickly telling significant security issues from minor ones. They would do great job owning a few critical components within the overall architecture. But they would be bored to death running a "security compliance" program or pushing a team through 25 security checkboxes. They treat security as science rather than a production line and would be outraged by a request "to sign off" on anything they haven't spent at least a day scrutinizing and that has not had at least a month of security work behind the belt. Surprisingly, they may be unaware of some popular security keywords or famous recent attacks. That's because they simply don't care &ndash; they can re-invent that stuff on a daily basis as needed :)</span></li>
<li><span style="font-family: verdana,geneva;"><strong>Security SME</strong> uses exceptional knowledge of some technology to help other people navigate within it. Need to make a call on the suitability of Elliptic Curve encryption for your product? Wondering what an obscure DACL means? Ask an SME. They will answer all these questions, precisely and probably immediately. Having an SME in the team is really, really great. However, it takes years to grow (or even hire) one.</span></li>
<li><span style="font-family: verdana,geneva;"><strong>Pattern Discoverer</strong>. Through years of working with security issues, discovers patterns in them and then completely eliminates whole classes of issues with the (proposed) automatic tools. They may not even be "security experts" <em>per se</em>, as they enjoy solving any classes of challenging problems using the same approach. Alone, they may achieve what dozens of people could've not achieved in years. On the darker side, they may easily spend years without any immediately visible results while the product would be in a need for a regular security attendance at simpler complexity levels.</span></li>
</ul>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Security Reviews: The Heuristics Zoo, Part 2/2</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/security-reviews-the-heuristics-zoo-part-22</link>
      <pubDate>Mon, 28 Jul 2014 15:52:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/07/28/security-reviews-the-heuristics-zoo-part-22/</guid>
      <description><![CDATA[Introduction (Part I)&nbsp;
The Heuristics Zoo, Part 1/2
Note: standard Disclaimer expressed...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<div class="WordSection1">
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/18/practice-and-theory-of-security-reviews.aspx">Introduction (Part I)&nbsp;</a></span></p>
<p><span style="font-family: verdana,geneva;">The <a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/23/security-reviews-the-heuristics-zoo-part-1-2.aspx">Heuristics Zoo, Part 1/2</a></span></p>
<p><span style="font-family: verdana,geneva;">Note: standard Disclaimer expressed in Part I applies here as well.</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 5: "Area Expertise" and "Penetration Testing"</strong></span></p>
<p><span style="font-family: verdana,geneva;">These two seemingly different techniques share a lot in how they approach managing the complexity of security reviews, so I will consider them together.</span></p>
<p><span style="font-family: verdana,geneva;">"Area Expertise" is simply learning. Studying a technology long enough can make one a Subject Matter Expert (an "SME") who knows all subtle interactions within the area and can recognize possible security issues there quickly. This approach is orthogonal to an "audit". While "audit" spans a horizontal area across the product, an expertise would tend to occupy a deep "vertical" cut of a limited width. E.g., an expert may know how to write interfaces with static methods on them in <a href="https://en.wikipedia.org/wiki/Common_Intermediate_Language">Intermediate Language</a> -- but would have no idea about network security:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7266.Expertise.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7266.Expertise.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/7266.Expertise.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/7266.Expertise.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Penetration testing is a technique that seeks (to some degree) to re-create SME effect on a faster timescale. It starts with carefully choosing a sub-set of a product "surface" that appears to be softer. Then, with a help of tools and deep design dives it seeks to pass through the L1-L2 complexity levels and gain a control over a set of more complex interactions, typically at Level 3 and deeper. As many security heuristics would have patchy coverage at complexities above L2, chances are good that executing or examining that functionality would reveal new security holes.</span></p>
<p><span style="font-family: verdana,geneva;">Importantly, pentesting cannot be used to ensure large products security. As it focuses on higher order interactions, it would need at least something like O(<strong>N<sup>3</sup></strong>) time for that. Time and budget limitations would prevent such an algorithm from scaling across the entire product. Pentesting resolves this contradiction by reducing <strong>N</strong> &ndash; in other words, but precisely aiming at a smaller part of a product to break through.</span></p>
<p><span style="font-family: verdana,geneva;">Therefore, the value of pentesing is akin to that of a geological drilling. It measures product "security" by assessing the difficulty of breaking through its L1-L2 security "crust", and delivers samples not easily found on the surface.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 6: "Learning from Attackers"</strong></span></p>
<p><span style="font-family: verdana,geneva;">If you are an enterprise, you may have something like 10<sup>4</sup>-10<sup>16</sup> automatic security checks at your disposal to run against your product before giving it to the user. On top of that, there are 10<sup>2</sup>-10<sup>5</sup> human verification questions available to apply, too. Seems like a lot? But a product with just 100 elements can have up to 10<sup>30</sup> possible element interaction states! Even if 99.999% of them are void, the remainder still represents a space overwhelmingly huge and is beyond any technical or human capability. Within that vast space, where should we place our "tiny" 10<sup>16</sup> security checks?</span></p>
<p><span style="font-family: verdana,geneva;">Well, the good news is that we actually don&rsquo;t need to find <strong>all</strong> security holes. We need to find only those that attackers can and will (eventually) find. With that leverage, the effectiveness of the security assurance can be boosted by orders of magnitude, enabling practical and effective security defense.</span></p>
<p><span style="font-family: verdana,geneva;">What specifically could be done?</span></p>
<p><span style="font-family: verdana,geneva;">First, you can study past attacks against your product or other similar ones on the market. How are they typically attacked? What are the top 5 classes of security vulnerabilities they had faced? <a href="https://cve.mitre.org/">https://cve.mitre.org/</a> could be a good starting point for such an assessment.</span></p>
<p><span style="font-family: verdana,geneva;">Second, you can attend security conferences and talk to security researchers. Or, if travel budget is an issue, just read abstracts of their presentations and summaries of their talks. Even in such form the knowledge gain can help you <strong>predict</strong> how your product would be attacked in 1-3 years. By smartly prioritizing security assurance work then it could be shrank in volume (compared to "blind" planning) by a factor of 2-10 times.</span></p>
<p><span style="font-family: verdana,geneva;">Finally, if you are an online service, you can learn security attacking patterns from your logs! That way, attackers actually work <strong>for you</strong>. Any new creative idea they come up with becomes part of <strong>your</strong> tools set. For free. Isn't it cool? Of course, this is easier to say than to do. Putting together a proper monitoring &amp; detection solution is a daunting task. But the benefits are so great that I would definitely strive for that.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Mind the privacy though &ndash; you don't want any sensitive or private users' data to be exposed internally or externally in the process.</em></span></p>
<p><span style="font-family: verdana,geneva;">By the way, not only services can benefit from that approach. Traditional "box" products can collect crash reports and mine attacks in them as well, thus improving their security posture.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 7: "Scenario Fuzzing"</strong></span></p>
<p><span style="font-family: verdana,geneva;">A JPG file may miss up to 90% of information contained in the original bitmap image. Yet for a human viewer it represents all the essentials of the picture. Conceptually, this is achieved by abandoning a &ldquo;dot&rdquo; as a principal element of image construction and choosing a &ldquo;wave&rdquo; for that purpose.</span></p>
<p><span style="font-family: verdana,geneva;">Can we apply the same idea? Can a very large product be effectively represented with a number of elements substantially smaller than its feature count?</span></p>
<p><span style="font-family: verdana,geneva;">Personally, I'm quite positive that is possible, and possible in many ways. Here I will discuss one of them that is based on the concept of an<strong> end-user scenario</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">What is a "scenario"? A scenario is a description of some meaningful user's interaction sequence. An example would be: "launch the browser, login into your email account, read new messages, log-off and close the browser." A product could be represented then as a set of scenarios it supports.</span></p>
<p><span style="font-family: verdana,geneva;">Why would that work?</span></p>
<p><span style="font-family: verdana,geneva;">First, scenario-based description is complete. The proof is obvious: if there is a functionality that is <strong>not</strong> activated in <strong>any</strong> legitimate user interaction, it is a dead code. Throw it away. No customer would notice that, ever.</span></p>
<p><span style="font-family: verdana,geneva;">Second, scenario representation is compact. Typically, scenarios drive product requirements, which drive the specifications, which produce the API, then the code, and thus the product itself. A single use case quoted above would already hit thousands APIs and dozens of features in a contemporary OS. A few hundred scenarios often completely describe even a large product.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>In fact, for a product where scenarios "choose" features to participate randomly and independently of each other, the number of scenarios needed to execute all features grows logarithmically with respect to feature count: <strong>S</strong> = <strong>O(Log(N))</strong>. While that is probably not exactly how real products are structured, it still demonstrates the power of scenario-based product description at least for some cases.</em></span></p>
<p><span style="font-family: verdana,geneva;">Effectively, scenarios play a role similar to that of a &ldquo;coverage graph&rdquo;, providing access to a <strong>vicinity</strong> of each and every possible functionality combination in a product.</span></p>
<p><span style="font-family: verdana,geneva;">How do we use that to discover security weaknesses?</span></p>
<p><span style="font-family: verdana,geneva;">If you look carefully at security breaches you'll see that most have valid user scenarios closely associated with them. When an attacker breaks into a system, he/she mostly interacts with it in ways that are fairly close to what a "legitimate" user would do. Those are only 1-3 unexpected "twists" that throw the system into a state of abnormal that may further lead to exploitability.</span></p>
<p><span style="font-family: verdana,geneva;">In other words, an attack often is a scenario that differs from a legitimate use by few changes. If so, can it be derived from a legit scenario by some morphing, using techniques such as random tweaks, grammar-based generators, genetic algorithms, or <strong>N</strong>-wise combinations building?</span></p>
<p><span style="font-family: verdana,geneva;">After all, if we do fuzz binary inputs, why not fuzz scenarios?</span></p>
<p><span style="font-family: verdana,geneva;">Many security reviewers do exactly that. They take a valid workflow and morph it. &ldquo;Next step: FTP log-in. Well, what if I send my log-in packet twice after seeing a 100 response but right before a 200 is received? Or what if I start sending log-in packet, but will never finish it?&rdquo;</span></p>
<p><span style="font-family: verdana,geneva;">Such variations may trigger completely new sequences of calls within the system and cause interactions between components that have never been in touch before&hellip; and sometimes result in an unexpected behavior.</span></p>
<p><span style="font-family: verdana,geneva;">Personally, that&rsquo;s why I tend to start security reviews with a simple question of &ldquo;please give me the user&rsquo;s perspective&rdquo;. That&rsquo;s my minimal "fuzzing seed&rdquo; &ndash; a valid use case that I can tweak to produce new variations.</span></p>
<p><span style="font-family: verdana,geneva;">Scenario fuzzing is intuitively easy. It scales well with respect to the scenarios count involved. It naturally considers multi-component interactions and can, in theory, discover completely unknown classes of security bugs.</span></p>
<p><span style="font-family: verdana,geneva;">But of course it has its limitations as well.</span></p>
<p><span style="font-family: verdana,geneva;">First, today it is primarily a human-driven process. I&rsquo;d be very happy to see a tool that can do that job but I think our scientific understanding of natural languages is just not there yet. This tool would need to be able to take a valid English description of a scenario, tweak it, and produce a different (yet still meaningful) description as an output.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Of course, this does not have to be done in English. There are artificial formal languages for scenario description in software industry. They may offer a better starting point for this approach.</em></span></p>
<p><span style="font-family: verdana,geneva;">Second, it takes years of security training and a good bit of human creativity to come up with good scenario tweaks.</span></p>
<p><span style="font-family: verdana,geneva;">Third (and the most important) its' practical application is limited by the knowledge limits of your data sources.</span></p>
<p><span style="font-family: verdana,geneva;">It is easy to ask crazy questions like &ldquo;what if I do <strong>A</strong> before doing <strong>B</strong>, without doing <strong>C</strong>, while continuing to do <strong>D</strong> at the same time?&rdquo; However, in many cases neither the specification, nor the product team would be confident in the answer! In fact, hitting a blank look and &ldquo;we don&rsquo;t know&rdquo; is an indicator that the review is within the space that nobody has deeply thought through before.</span></p>
<p><span style="font-family: verdana,geneva;">But does that help? If most of these questions eventually lead to boring answers, people will quickly learn to ignore your requests. Because obtaining the answers might be difficult for them. It could take hours of examining the source code, or getting a response from SMEs, or painful debugging. Plus it takes great tenacity to make sure that email threads do not die, that people keep working on your questions and deliver consistent answers.</span></p>
<p><span style="font-family: verdana,geneva;">So it requires building great trust with people. While operating in this mode, you need to make sure you are asking "good" questions frequently enough so that people would not learn to dismiss them as "mostly useless" based on past observations.</span></p>
<p><span style="font-family: verdana,geneva;">What is the complexity of this algorithm? It's hard to say. It really depends on the specific mechanism chosen to generate new scenarios. And it does not even have a defined stopping point, so formally speaking this is not even an "algorithm".</span></p>
<p><span style="font-family: verdana,geneva;">Just for the sake of illustration, we can assess the complexity for one special case when:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;">Fuzzed scenarios are produced by random tweaks of legitimate scenarios, and we do <strong>t</strong> tweaks per each one</span></li>
<li><span style="font-family: verdana,geneva;">Scenarios use features randomly and independently of each other</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">For that case, the complexity of the review could be shown to be approximately O(<strong>t</strong>*Log(<strong>N</strong>)) &ndash; in other words, it's less than linear! How's that possible? The short answer is because in scenario fuzzing, one question executes many features, and the reviewer does not need to explicitly know them all. So he/she doesn't have to spend O(<strong>N</strong>) time enumerating them &ndash; that has already been done by the engineering team when they designed and built the product.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Item 8: What about Secure Design and Layers of Abstraction?</strong></span></p>
<p><span style="font-family: verdana,geneva;">I'll say it upfront: neither of them is a <strong>review</strong> heuristic. They are <strong>design</strong> techniques. But as it's simply impossible to avoid this topic while discussing software security, I'll share my view on it.</span></p>
<p><span style="font-family: verdana,geneva;">So, in a system of <strong>N</strong> elements where each can (generally) interact with each other one, there are potentially up to <strong>2<sup>N</sup></strong> various interaction combinations possible. That causes security review to be exponentially complex and generally unmanageable in practice.</span></p>
<p><span style="font-family: verdana,geneva;">But what if we avoid that "any-to-any" interaction pattern and bring some structure into the system?</span></p>
<p><span style="font-family: verdana,geneva;">One way of doing that could be logical arrangement of product elements into a two-store hierarchy where:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;">The lower level elements are bucketed into groups each "reporting" to a parent element. Lower elements can freely talk to each other within the group, but cannot converse across the group boundary</span></li>
<li><span style="font-family: verdana,geneva;">All cross-group interactions happen through the top-level elements that talk only to each other or their "child" groups.</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">Here is how it may look like:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1185.Hierarchy.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1185.Hierarchy.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1185.Hierarchy.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1185.Hierarchy.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">What would be the total number of checks needed to ensure the secure design of such a system? Obviously, it will require <strong>2<sup>k</sup></strong> checks to cover the upper level, plus <strong>k</strong> times <strong>2<sup>N/k</sup></strong> for the lower:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong>T = 2<sup>k</sup> + k*2<sup>N/k</sup></strong> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [3]</span></p>
<p><span style="font-family: verdana,geneva;">What is the choice of <strong>k</strong> that minimizes that number? That's answered by demanding that <strong>&part;T/&part;k</strong> = <strong>0</strong> and solving the resulting equation. While the precise solution is impossible to express in common functions, a good approximation for <strong>N &gt;&gt; 1</strong> is easy to obtain:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong>k &asymp; N<sup>1/2</sup> + &frac14; Ln(N)</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [4]</span></p>
<p><span style="font-family: verdana,geneva;">With the corresponding number of needed security checks being then:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/2451.TwoLayerT.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/2451.TwoLayerT.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/2451.TwoLayerT.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/164x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/2451.TwoLayerT.png" alt="" border="0" />[5]</a></span></p>
<p><span style="font-family: verdana,geneva;">While that is still a huge number, it is <strong>tremendously</strong> smaller than <strong>2<sup>N</sup></strong> checks needed for a "disorganized" system where just everything interacts with everything. So arranging a system as a 2-layered hierarchy brings in a great reduction in complexity of the security assurance.</span></p>
<p><span style="font-family: verdana,geneva;">Can we capitalize on that and introduce more layers of abstraction, somewhat similarly to the <a href="https://en.wikipedia.org/wiki/OSI_model">OSI model</a>?</span></p>
<p><span style="font-family: verdana,geneva;">The answer is positive. For a system with <strong>L</strong> such layers the total number of security checks needed (very approximately and assuming very large <strong>N</strong>) is even further reduced to:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1258.LLayers.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1258.LLayers.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1258.LLayers.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/221x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1258.LLayers.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Why not continue adding layers indefinitely? Because there is a design and implementation cost behind each next layer of abstraction. So there needs to be just enough of them to keep the system manageable &ndash; but not more. How many, exactly? If your budget of security checks is <strong>T</strong>, and there are <strong>N &gt;&gt; 1</strong> atomic elements expected to be in the system, the count is obtained by inversing [6], and it is, roughly:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong>L &asymp; Ln(N)/Ln(Ln(T))</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [7]</span></p>
<p><span style="font-family: verdana,geneva;">For a product 10 Gb in size with 1 million security checks budget that evaluates to <strong>L</strong> &asymp; <strong>10</strong>. Not surprisingly, it is quite close to the number of abstraction layers in the OSI model, because the dependency on both input parameters is rather weak.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>In fact, a logarithm of&nbsp; a&nbsp;logarithm of pretty much anything in our Universe could be considered a constant for most practical uses :))</em></span></p>
<p><span style="font-family: verdana,geneva;">Now when we are done with that, let's make a few sobering notes.</span></p>
<p><span style="font-family: verdana,geneva;">First, as a security reviewer, you rarely face a large product that you have a chance to design "properly" from scratch. More often you face systems already mostly designed, with a long history of past versions, people joining and leaving, patches, changes, and even acquisitions. Your responsibility is similar to that of a doctor, who needs to diagnose a patient and give actionable advice while fully accepting their background, past life events and known health issues.</span></p>
<p><span style="font-family: verdana,geneva;">Second, even if you get a chance to design something from the beginning, I seriously doubt that such a thing as "Secure Design" exists in practice. Sure, it would be nice to live in a world where no unexpected higher order interactions between components are possible, and all lower-order ones are documented. But I doubt that's possible. In my opinion, there is no such thing as "completely non-interacting features". There are only features with very low interaction probability. So every time you think you've eliminated a class of unwanted interference, a completely new one surfaces right behind it&hellip;</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Functions A and B may never call each other. But if each one allocates one byte of memory dynamically, A may (potentially) eat the last memory byte precisely before B would need it. So suddenly B's call becomes dependent on A's execution state. Is that an effective control mechanism? Not really. But is that a dependency? Yes. And it may need to be factored into the analysis if the cost of a potential failure is represented by a nine digit figure.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Certainly, not keeping any function variables cures this problem. But even if software is (somehow?) completely removed from the risk picture, what about hardware failures? Believe it or not, some researchers have learned how to exploit results of memory corruptions caused by (among other things) random protons from the interstellar space hitting our memory chips -- see </em><a href="https://dinaburg.org/bitsquatting.html"><em>https://dinaburg.org/bitsquatting.html</em></a><em> for details.</em></span></p>
<p><span style="font-family: verdana,geneva;">My personal take on that is that there is always<strong> some</strong> probability of an unexpected interaction. Eliminating one class of it brings up the next one, far less probable but way more elusive and difficult to eliminate. So <strong>some</strong> surprises will always keep hiding somewhere within the realms of higher complexity interferences.</span></p>
<p><span style="font-family: verdana,geneva;">That being said, a "Secure Design" as something that tries to <strong>minimize</strong> side interactions is valuable. While it may not be perfectly achievable, it may still bring the security of a product couple levels up in terms of the complexity analysis needed to successfully attack it.</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/08/04/theory-and-practice-of-security-reviews-part-3.aspx">Continue to the last part...</a></span></p>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Security Reviews: The Heuristics Zoo, Part 1/2</title>
      <link>https://review.docs.microsoft.com/archive/blogs/eugene_bobukh/security-reviews-the-heuristics-zoo-part-12</link>
      <pubDate>Wed, 23 Jul 2014 13:24:00 GMT</pubDate>
      <dc:creator><![CDATA[Eugene Bobukh]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/eugene_bobukh/2014/07/23/security-reviews-the-heuristics-zoo-part-12/</guid>
      <description><![CDATA[Initially meant to fit into&nbsp;one chapter, this text grew quickly and I had to split it into two....]]></description>
      <content:encoded><![CDATA[<p><span style="font-family: verdana,geneva;">Initially meant to fit into&nbsp;one chapter, this text grew quickly and I had to split it into two. So there will be four parts of the article in total.</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/18/practice-and-theory-of-security-reviews.aspx">Introduction (or Part I) is here.</a></span></p>
<p><span style="font-family: verdana,geneva;">&lt;Disclaimer&gt;By no means this list is "complete". I think every security person on the planet can add couple extra good tricks learned through their experience.&lt;/Disclaimer&gt;</span></p>
<div class="WordSection1">
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 1: "Audit"</strong></span></p>
<p><span style="font-family: verdana,geneva;">I already considered this method in the previous chapter, so I'll be brief here.</span></p>
<p><span style="font-family: verdana,geneva;">To run a security "audit", you logically split the system into a set of elements and ask "security" questions about each one. Something like: &ldquo;Do you use MD5?&rdquo;, &ldquo;Do you transfer user passwords over HTTP in clear?&rdquo;, &ldquo;Do you have Admin/root accounts with default password?&rdquo;, "Do you parse JPGs with a home-grown untested parser?" and so on. Answering "Yes" to a question typically indicates an actionable security risk.</span></p>
<p><span style="font-family: verdana,geneva;">This approach may look primitive, but it does have its place. It usually prevents at least the most outrageous security holes. It provides *some* basic assurance when nothing else is available under the time crunch. The complexity of this algorithm is linear O(<strong>N</strong>) so it scales well. And when you are done with it, you have a bonus list of high risk targets for later detailed inspection.</span></p>
<p><span style="font-family: verdana,geneva;">On the complexity diagram, audit occupies "the first floor":</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6685.Audit.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6685.Audit.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/6685.Audit.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/6685.Audit.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">The primary shortcoming of this method is that without special attention it would not consider complex interactions and so can miss contract violations like those I already presented:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong>Component A</strong>: &ldquo;We don&rsquo;t check any user&rsquo;s input, we only archive and pass it through&rdquo;</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><strong>Component B</strong>: &ldquo;We are the backend system and expect only good filtered inputs&rdquo;</span></p>
<p><span style="font-family: verdana,geneva;">Despite that limitation, audit may still be helpful at least as a tool for the first quick assessment or when there is no time for anything else.</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Some readers may point out that even audit may be tough to apply across really large products that can easily have 100,000 work items logged against them. Just reading the description of every item would consume weeks of time. It's impossible to imagine asking&nbsp;20 security questions about each one on top of that.</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>There are approaches to deal with that problem, both technical and organizational. The former focus on automatic items triaging and may utilize machine learning for that. The latter try to distribute this work across feature owners in some reasonable manner. Unfortunately, as this is a large topic, I'll have to defer the discussion on it to (hopefully) some better times.</em></span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 2: "Threat Modeling" (and other polynomial algorithms)</strong></span></p>
<p><span style="font-family: verdana,geneva;">Let's study this method in detail to understand some of the inherent limitations behind it.</span></p>
<p><span style="font-family: verdana,geneva;">Threat Modeling (at least in its "classical" form) primarily considers pairwise interactions between product elements. It can detect contract violations like the one just mentioned. It is a well-recognized and fairly efficient technique known broadly across the industry, it is <a href="https://blogs.msdn.com/b/ptorr/archive/2005/02/22/guerillathreatmodelling.aspx">easy to learn</a> and there are tools that support it (e.g., <a href="https://www.microsoft.com/security/sdl/adopt/threatmodeling.aspx">https://www.microsoft.com/security/sdl/adopt/threatmodeling.aspx</a><strong>)</strong>.</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1680.ThreatModeling.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1680.ThreatModeling.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/1680.ThreatModeling.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/1680.ThreatModeling.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">Downsides? It complexity is O(<strong>N<sup>2</sup></strong>). No, the problem is not the volume of needed computations <em>per se</em>. It is rather how humans tend to respond to that, causing what I call "Threat Modeling fragmentation".</span></p>
<p><span style="font-family: verdana,geneva;">If the cost to Threat Model one 10-element feature is 10*10 = 100 questions/checks, splitting it into two sets and running two independent Threat Modeling sessions would reduce that cost to 5*5 + 5*5 = 50 checks. That is a large time saving! Unfortunately, the benefit comes purely from ignoring the connections between separated subcomponents, and thus missing possible security holes associated with them. But under tight time pressure, it visibly saves time! So people across the organization may start slicing large features into smaller and smaller components, eventually producing a huge set of tiny isolated Threat Models each concerned "with my feature only" and totally ignoring most inter-component connections.</span></p>
<p><span style="font-family: verdana,geneva;">That is the mechanism that makes Thread Modeling difficult across large products.</span></p>
<p><span style="font-family: verdana,geneva;">That issue would affect <strong>any</strong> security review algorithm with a complexity significantly greater than linear O(<strong>N</strong>). Why? Because for any function f(<strong>N</strong>) with a growth faster than O(<strong>N</strong>) it is true that:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">f(<strong>N1</strong>) + f(<strong>N2</strong>) &lt; f(<strong>N1</strong> + <strong>N2</strong>)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [1]</span></p>
<p><span style="font-family: verdana,geneva;">[This says, effectively, that two smaller reviews cost less than one review of the combined features.]</span></p>
<p><span style="font-family: verdana,geneva;">So unless a carefully planned consolidation effort is applied during the release cycle, all human-performed "complex" non-linear security algorithms would naturally tend to produce fragmented results, up to a point (in the worst case) of impossibility to merge them back into any coherent single picture.</span></p>
<p><span style="font-family: verdana,geneva;">To be free from that problem, a security review heuristic must run in time not much worse than O(<strong>N</strong>). Otherwise, reviews fragmentation will kick in and it may consume significant resources to deal with it.</span></p>
<p><span style="font-family: verdana,geneva;">An algorithm that would be naturally feature-inclusive and facilitating holistic security is the one with the property opposite to that of [1]:</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">f(<strong>N1</strong>) + f(<strong>N2</strong>) &gt;= f(<strong>N1</strong> + <strong>N2</strong>)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [2]</span></p>
<p><span style="font-family: verdana,geneva;">&hellip;which is satisfied if it runs in O(<strong>N</strong>) time, or faster.</span></p>
<p><span style="font-family: verdana,geneva;">But can it really run <strong>faster</strong>? The answer is "no", because simply enumerating all features takes O(<strong>N</strong>) effort, and not enumerating any of them would mean we <strong>completely</strong> missed some, with possible associated vulnerabilities!</span></p>
<p><span style="font-family: verdana,geneva;"><strong>Therefore, a security review algorithm that scales well across a large org on large systems must run in precisely O(N) time.</strong></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>In practice small deviations from that could be permissible. E.g., if <strong>doubling</strong> the scope of the review increases a cost <strong>per feature</strong> by a small fraction only it may still be acceptable. What constitutes a &ldquo;small fraction&rdquo;? A review process should scale well from individual feature (<strong>N</strong> = 1) to a large product (<strong>N</strong> = 1 million). A cost <strong>per feature</strong> should not change more than by an order of magnitude upon such an expansion. That dictates that a practically acceptable review algorithm should run in a time no worse than something like O(<strong>N*Log(N)</strong>) or O(<strong>N<sup>1.16</sup></strong>).</em></span></p>
<p><span style="font-family: verdana,geneva;">Finally, it's worth mentioning that since Threat Modeling primarily focuses on pairwise interactions, it tends to pay less attention to vulnerabilities arising from interactions of higher orders of complexity (i.e, of triplet interactions). As a result, some classes of security holes are not easily found with Threat Modeling. For example, people with field experience know that it is very difficult to represent (yet to detect) an XSS or a Clickjacking attack by using a Threat Model. In reality, those attacks are usually found only when a reviewer is aware of their possibility and specifically hunts for them.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 3: "Continued Refactoring"</strong></span></p>
<p><span style="font-family: verdana,geneva;">So a product was represented as a set of logical elements, you ran your favorite security review algorithm over it, found some issues, got them fixed.</span></p>
<p><span style="font-family: verdana,geneva;">A question is: if you re-represent the product as a different set of elements and run a review again, will you find new security bugs?</span></p>
<p><span style="font-family: verdana,geneva;">The answer is "almost certainly yes"</span></p>
<p><span style="font-family: verdana,geneva;">A semi-formal "proof" is easy. Let's say a product has a total of <strong>M</strong> truly "atomic", indivisible elements (such as bits of code). There are up to <strong>2<sup>M</sup></strong> potential interactions between them. For review purposes, the product is represented as a set of <strong>N<sub>1</sub></strong> &lt; <strong>M</strong> groups of those bits. Within each group, the review is perfectly efficient &ndash; it finds all weaknesses. Outside the group, connections are ignored.</span></p>
<p><span style="font-family: verdana,geneva;">Assuming all groups are equal in size (just to show the point), how many connections would this grouping analyze? Within each group, there are (<strong>M/N<sub>1</sub></strong>) elements, therefore <strong>2<sup>(M/N1)</sup></strong> possible interactions between them will be considered. Since there are <strong>N<sub>1</sub></strong> groups, the total coverage would be <strong>N<sub>1</sub></strong>*<strong>2<sup>(M/N1)</sup></strong> which is significantly lesser than <strong>2<sup>M</sup> </strong>when <strong>N<sub>1</sub></strong> &lt; <strong>M</strong>.</span></p>
<p><span style="font-family: verdana,geneva;">In other words, by choosing a split of a product we greatly shrink the volume of our review but make it manageable. That also means that by abandoning a representation and switching to an essentially different one a new fresh look is almost guaranteed. And so new security bugs!</span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Of course, the catch is to choose "<strong>good</strong>" representations. Neither splitting a product into just two components, nor slicing it down to individual bits is really helpful:</em></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5417.WrongSplits.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5417.WrongSplits.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/5417.WrongSplits.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/5417.WrongSplits.png" alt="" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-family: verdana,geneva;"><em>Choosing the right representation still remains a bit of an art.</em></span></p>
<p><span style="font-family: verdana,geneva;">So keep trying. The more meaningful ways to represent a product as "a set-of-somethings" you can creatively come up with, the more successful your review will be. Some of the possible approaches are:</span></p>
<ul>
<li><span style="font-family: verdana,geneva;">By feature</span></li>
<li><span style="font-family: verdana,geneva;">By source file</span></li>
<li><span style="font-family: verdana,geneva;">By API</span></li>
<li><span style="font-family: verdana,geneva;">By binary</span></li>
<li><span style="font-family: verdana,geneva;">By functionality domain</span></li>
<li><span style="font-family: verdana,geneva;">By dev owner</span></li>
<li><span style="font-family: verdana,geneva;">By location within the network topology</span></li>
</ul>
<p><span style="font-family: verdana,geneva;">Generally, if you have <strong>k</strong> independent ways to re-represent your product, your security enhances roughly <strong>k</strong> times. So it makes sense to approach security from multiple angles. You split by feature and do Threat Modeling. Then you split by source file and do code review or scan for unwanted API. Then you split by API entry points and do fuzzing. Then by binary and scan for recognized insecure structures. And so on.</span></p>
<p><span style="font-family: verdana,geneva;">That's one of the reasons why the "many eyes" argument works. And that is, essentially, how <a href="https://www.microsoft.com/security/sdl/default.aspx">SDL</a>-like heuristics arise. By the way, they still run in linear time &ndash; in O(<strong>k*N</strong>), precisely.</span></p>
<p>&nbsp;</p>
<p><span style="font-family: verdana,geneva;"><strong>Heuristic 4: "Pattern Elimination"</strong></span></p>
<p><span style="font-family: verdana,geneva;">Some security bugs are not completely random. Studying their past history may lead to recognition of a pattern. When a pattern is known, its detection could be automated. And then a whole class of security issues could be eliminated at one push of a button. At O(<strong>1</strong>) human time cost.</span></p>
<p><span style="font-family: verdana,geneva;">But patterns do not necessarily follow simple lines on product complexity diagram &ndash; they would rather tend to have quite complex shapes and may be difficult to spot:</span></p>
<p><span style="font-family: verdana,geneva;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3833.Pattern.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3833.Pattern.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/33/93/3833.Pattern.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-33-93/3833.Pattern.png" alt="" border="0" /></a></span></p>
<p><span style="font-family: verdana,geneva;">So pattern elimination is a two-step approach. First, it takes a good human guess. Next, cheap computational power essentially replicates the results of that guess across all the product, detecting and/or eliminating security issues.</span></p>
<p><span style="font-family: verdana,geneva;">The effectiveness of this approach is limited by four factors:</span></p>
<ol>
<li><span style="font-family: verdana,geneva;">It might take years of building security expertise before patterns would emerge behind what initially looks like a flow of random bugs. Yet the product may need to ship much sooner.</span></li>
<li><span style="font-family: verdana,geneva;">Even if all patterns are somehow recognized (by a human), automating the necessary checks may not be easy. Depending on your definition of "a class" of security bugs, there are something between 20 and 3000 of them known in the industry. Each one may require a separate tool.</span></li>
<li><span style="font-family: verdana,geneva;">Even good security tools cut only though a part of the security space. They may discover wonderful bugs in one direction and completely miss trivial issues nearby.</span></li>
<li><span style="font-family: verdana,geneva;">Finally, the computational cost. Even with 10<sup>15</sup> security checks available per a product, it is unrealistic to certify all interactions of complexity order higher than 4-5 for a product with more than a thousand logical elements ("features") in it. In other words, we still need help of other methods.</span></li>
</ol>
<p><span style="font-family: verdana,geneva;"><a href="https://blogs.msdn.com/b/eugene_bobukh/archive/2014/07/28/security-reviews-the-heuristics-zoo-part-2-2.aspx">Continue to Part 2 of the Zoo...</a></span></p>
</div>]]></content:encoded>
    </item>
  </channel>
</rss>