{"id":50,"date":"2014-02-07T21:40:27","date_gmt":"2014-02-07T21:40:27","guid":{"rendered":"http:\/\/blogs.gentoo.org\/blueness\/?p=50"},"modified":"2014-02-07T22:10:16","modified_gmt":"2014-02-07T22:10:16","slug":"the-gentoo-profile-stacking-problem","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/blueness\/2014\/02\/07\/the-gentoo-profile-stacking-problem\/","title":{"rendered":"The Gentoo Profile Stacking Problem"},"content":{"rendered":"<p>I thought I&#8217;d write a bit about a long standing problem that the hardened team has been facing with Gentoo&#8217;s profile system.\u00a0 Ever since I joined the team around 2009, we&#8217;ve had to deal with the &#8220;profile stacking problem&#8221;.\u00a0 Most users and devs just merily go along using `eselect profile` to pick the profile closest to the type of system they want and then tweak the various files under \/etc\/portage, adding a USE flag here, and keywording or unmasking a package there, until they get the &#8220;perfect&#8221; system.\u00a0 What I want to do in this post is expose just what goes into designing the profiles that we publicly export.<\/p>\n<p>I was inpsired to write this because of <a href=\"https:\/\/bugs.gentoo.org\/show_bug.cgi?id=492312\" target=\"_blank\">bug #492312<\/a>.\u00a0 There we want to re-introduce the hardened desktop profile for amd64, x86 and arm.\u00a0 I say &#8220;re-introduce&#8221; because we had to remove it and its sibling profiles \/server and \/developer.\u00a0 So what was going on there?<\/p>\n<p>To start, let me give you a nice pice of python code:<\/p>\n<pre>import portage\r\nfor p in portage.settings.profiles:\r\n    print(\"%s\" % p)<\/pre>\n<p>What this little snippet does is print out the profile stack as the directories inherited from one another via the parent file.\u00a0 Its a useful tool because profile stacking can get very hard to follow.\u00a0 When the parent file has something simple like just &#8220;..&#8221; then the inheritance is easy and that directory just inherits all the package.mask, package.unmask etc of the parent directory, as you would expect from the shell meaning of &#8220;..&#8221;\u00a0 But what happens when the parent file looks like this:<\/p>\n<pre>..\/..\/..\/base\r\n..\/..\/..\/default\/linux\r\n..\/..\/..\/arch\/amd64\r\n..<\/pre>\n<p>as it does for hardened\/linux\/amd64?\u00a0 Well then we get some interesting behavior. The first line says, inherit from base. Easy enough since base inherits from nothing else you get all of base&#8217;s settings. The second line says inherit from default\/linux, which aslo doesn&#8217;t inherit from anything.\u00a0 These setting just add and override those from base.\u00a0 Easy enough. Ah! But now we come to arch\/amd64, where the parent file says<\/p>\n<pre>..\/base\r\n..\/..\/features\/multilib\/lib32<\/pre>\n<p>and the inheritance continues to those directories in order. Finally &#8220;..&#8221; in hardened\/linux\/amd64 means inherit hardened\/linux which sets most of hardened&#8217;s needs via make.defaults, package.mask, use.mask and friends. But alas, hardened\/linux has its own parent file which reads<\/p>\n<pre>..\/..\/releases\/13.0<\/pre>\n<p>and the trip down the rabbit hole continues!\u00a0 If you are starting to get a little lost, don&#8217;t feel bad. It is hard to wrap your brain around stacking, which is why that little script above is so useful.\u00a0 But the difficulty in following profiles stacking is not the real problem. If you&#8217;re like me, you&#8217;re too proud to admit you can&#8217;t get your head around any complexity \ud83d\ude09 \u00a0 No, the real problem is that you can&#8217;t control the stacking order.<\/p>\n<p>To demonstrate, let me refer again to <a href=\"https:\/\/bugs.gentoo.org\/show_bug.cgi?id=492312\" target=\"_blank\">bug #492312<\/a>.\u00a0 There we&#8217;d like to have a profile which reads<\/p>\n<pre id=\"comment_text_1\" class=\"bz_comment_text\">hardened\/linux\/amd64\/desktop<\/pre>\n<p>Okay, but what should we put for its parent file?\u00a0 We&#8217;ll need &#8220;..&#8221; in there to inherit all of hardened\/amd64 settings, but we also would like <span class=\"quote\">targets\/desktop.\u00a0 So let&#8217;s try a parent file that looks like this<br \/>\n<\/span><\/p>\n<pre id=\"comment_text_7\" class=\"bz_comment_text\"><span class=\"quote\">..\r\n..\/..\/..\/..\/targets\/desktop<\/span><\/pre>\n<p>In that case, our little script tells us that our profile stack as follows:<\/p>\n<pre>\/usr\/portage\/profiles\/base\r\n\/usr\/portage\/profiles\/default\/linux\r\n\/usr\/portage\/profiles\/arch\/base\r\n\/usr\/portage\/profiles\/features\/multilib\r\n\/usr\/portage\/profiles\/features\/multilib\/lib32\r\n\/usr\/portage\/profiles\/arch\/amd64\r\n\/usr\/portage\/profiles\/releases\r\n\/usr\/portage\/profiles\/eapi-5-files\r\n\/usr\/portage\/profiles\/releases\/13.0\r\n\/usr\/portage\/profiles\/hardened\/linux\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\r\n\/usr\/portage\/profiles\/targets\/desktop\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\/destkop<\/pre>\n<p>And if you switch the order of .. and targets\/desktop, you get<\/p>\n<pre>\/usr\/portage\/profiles\/targets\/desktop\r\n\/usr\/portage\/profiles\/base\r\n\/usr\/portage\/profiles\/default\/linux\r\n\/usr\/portage\/profiles\/arch\/base\r\n\/usr\/portage\/profiles\/features\/multilib\r\n\/usr\/portage\/profiles\/features\/multilib\/lib32\r\n\/usr\/portage\/profiles\/arch\/amd64\r\n\/usr\/portage\/profiles\/releases\r\n\/usr\/portage\/profiles\/eapi-5-files\r\n\/usr\/portage\/profiles\/releases\/13.0\r\n\/usr\/portage\/profiles\/hardened\/linux\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\/destkop<\/pre>\n<p>The problem with the first ordering is that targets\/desktop overrides hardened\/linux\/amd64 and so any USE flags that we may turn off or on in hardened can get reverse in desktop.\u00a0 The example here is the jit flag &#8212; Just-In-Time compilers write executable code on the fly in areas of memory which must be both writeable and executable.\u00a0 But a PaX hardened kernel will not allow WX mmap-ings because this is an obvious exploit vector.\u00a0 Rather, in hardened, we prefer slower and safer methods for compiling\/interpreting code on the fly than JIT.<\/p>\n<p>Okay, so what about the second ordering.\u00a0 It may look strange to have target\/desktop before base, but that in itself is not an issue.\u00a0 Here we have the same problem as above but in an even more subtle way!\u00a0 (See my<a href=\"https:\/\/bugs.gentoo.org\/show_bug.cgi?id=492312#c9\" target=\"_blank\"> comment #9 of bug #492312<\/a>.)\u00a0 Consider a fairly important package like dev-libs\/libxml2.\u00a0 In the current state of the tree, `emerge -vp dev-libs\/libxml2` would give<\/p>\n<pre id=\"comment_text_9\" class=\"bz_comment_text\"> [ebuild   R    ] dev-libs\/libxml2-2.9.1-r1:2  USE=\"ipv6 python* ...<\/pre>\n<p>for both stacking choices. But if at some point in the future, someone added the following to profiles\/default\/linux\/package.use<\/p>\n<pre>#Python support causes problems on xyz\r\n#Don't pull it in if we don't neeed it\r\ndev-libs\/libxml2  -python<\/pre>\n<p>The vanilla profile default\/linux\/amd64\/13.0\/desktop and our hardened profile with targets\/desktop last would not change since they have &#8220;dev-libs\/libxml2 python&#8221; in package.use near the bottom of the stack, but our proposed hardened profile with targets\/desktop on top would give<\/p>\n<pre id=\"comment_text_9\" class=\"bz_comment_text\"> dev-libs\/libxml2-2.9.1-r1:2  USE=\"ipv6 readline ... -python ...<\/pre>\n<p>So, both choices for orderings of &#8220;..&#8221; and &#8220;targets\/desktop&#8221; in our parent file for hardened\/linux\/amd64\/desktop lead to situations where we can&#8217;t control what packages get what use flags. What we would like is a stacking that looks something like this<\/p>\n<pre>...\r\n\/usr\/portage\/profiles\/targets\/desktop\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\r\n\/usr\/portage\/profiles\/hardened\/linux\/amd64\/destkop<\/pre>\n<p>but how do we get that with our current inheritance mechanism? One idea that Magnus (Zorry) had was to gutt out this portion of portage and replace the parsing of the parent file with something along the lines of openrc&#8217;s depend() { &#8230; } clause. Then we just locally say what has to come before\/after what and we let the algorithm figure it out. It sounds like an interesting problem if there were two of me and if there were a good chance that it would actually get implemented. In the mean time, we limp along with what we have and do ad hoc fixes as changes in one part of the profiles means we have to adjust other things. Since we are all responsible for different areas of the tree&#8217;s profiles, inevitably we cause one another breakage even with the best of intentions. For example, a few days ago, Mike (vapier) removed a masking on the uclibc USE flag in the base profile.\u00a0 Doing so makes perfect sense. He didn&#8217;t tell me, and why should he have to?, but this lead to a small breakage in hardened\/linux\/uclibc\/amd64 and friends where I had to relax that masking.\u00a0 I only discovered this upon a catalyst run which is a bit annoying.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I thought I&#8217;d write a bit about a long standing problem that the hardened team has been facing with Gentoo&#8217;s profile system.\u00a0 Ever since I joined the team around 2009, we&#8217;ve had to deal with the &#8220;profile stacking problem&#8221;.\u00a0 Most users and devs just merily go along using `eselect profile` to pick the profile closest &hellip; <a href=\"https:\/\/blogs.gentoo.org\/blueness\/2014\/02\/07\/the-gentoo-profile-stacking-problem\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;The Gentoo Profile Stacking Problem&#8221;<\/span><\/a><\/p>\n","protected":false},"author":141,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[1],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/50"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/users\/141"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/comments?post=50"}],"version-history":[{"count":14,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/50\/revisions"}],"predecessor-version":[{"id":64,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/50\/revisions\/64"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/media?parent=50"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/categories?post=50"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/tags?post=50"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}