CINXE.COM

Project Zero: Racing against the clock -- hitting a tiny kernel race window

<!DOCTYPE html> <html class='v2' dir='ltr' lang='en' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <meta content='width=1100' name='viewport'/> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='https://googleprojectzero.blogspot.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Project Zero - Atom" href="https://googleprojectzero.blogspot.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Project Zero - RSS" href="https://googleprojectzero.blogspot.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Project Zero - Atom" href="https://www.blogger.com/feeds/4838136820032157985/posts/default" /> <link rel="alternate" type="application/atom+xml" title="Project Zero - Atom" href="https://googleprojectzero.blogspot.com/feeds/2892029424446991022/comments/default" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <link href='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png' rel='image_src'/> <meta content='https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html' property='og:url'/> <meta content='Racing against the clock -- hitting a tiny kernel race window' property='og:title'/> <meta content=' TL;DR: How to make a tiny kernel race window really large even on kernels without CONFIG_PREEMPT : use 聽a cache miss to widen the race w...' property='og:description'/> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/w1200-h630-p-k-no-nu/image19.png' property='og:image'/> <title>Project Zero: Racing against the clock -- hitting a tiny kernel race window</title> <style type='text/css'>@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;font-display:swap;src:url(//fonts.gstatic.com/s/opensans/v40/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4gaVY.eot);}</style> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Simple Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#66bbdd"/> <Group description="Page Text" selector="body"> <Variable name="body.font" description="Font" type="font" default="normal normal 12px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="body.text.color" description="Text Color" type="color" default="#222222"/> </Group> <Group description="Backgrounds" selector=".body-fauxcolumns-outer"> <Variable name="body.background.color" description="Outer Background" type="color" default="#66bbdd"/> <Variable name="content.background.color" description="Main Background" type="color" default="#ffffff"/> <Variable name="header.background.color" description="Header Background" type="color" default="transparent"/> </Group> <Group description="Links" selector=".main-outer"> <Variable name="link.color" description="Link Color" type="color" default="#2288bb"/> <Variable name="link.visited.color" description="Visited Color" type="color" default="#888888"/> <Variable name="link.hover.color" description="Hover Color" type="color" default="#33aaff"/> </Group> <Group description="Blog Title" selector=".header h1"> <Variable name="header.font" description="Font" type="font" default="normal normal 60px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="header.text.color" description="Title Color" type="color" default="#3399bb" /> </Group> <Group description="Blog Description" selector=".header .description"> <Variable name="description.text.color" description="Description Color" type="color" default="#777777" /> </Group> <Group description="Tabs Text" selector=".tabs-inner .widget li a"> <Variable name="tabs.font" description="Font" type="font" default="normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="tabs.text.color" description="Text Color" type="color" default="#999999"/> <Variable name="tabs.selected.text.color" description="Selected Color" type="color" default="#000000"/> </Group> <Group description="Tabs Background" selector=".tabs-outer .PageList"> <Variable name="tabs.background.color" description="Background Color" type="color" default="#f5f5f5"/> <Variable name="tabs.selected.background.color" description="Selected Color" type="color" default="#eeeeee"/> </Group> <Group description="Post Title" selector="h3.post-title, .comments h4"> <Variable name="post.title.font" description="Font" type="font" default="normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> </Group> <Group description="Date Header" selector=".date-header"> <Variable name="date.header.color" description="Text Color" type="color" default="#000000"/> <Variable name="date.header.background.color" description="Background Color" type="color" default="transparent"/> <Variable name="date.header.font" description="Text Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="date.header.padding" description="Date Header Padding" type="string" default="inherit"/> <Variable name="date.header.letterspacing" description="Date Header Letter Spacing" type="string" default="inherit"/> <Variable name="date.header.margin" description="Date Header Margin" type="string" default="inherit"/> </Group> <Group description="Post Footer" selector=".post-footer"> <Variable name="post.footer.text.color" description="Text Color" type="color" default="#666666"/> <Variable name="post.footer.background.color" description="Background Color" type="color" default="#f9f9f9"/> <Variable name="post.footer.border.color" description="Shadow Color" type="color" default="#eeeeee"/> </Group> <Group description="Gadgets" selector="h2"> <Variable name="widget.title.font" description="Title Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="widget.title.text.color" description="Title Color" type="color" default="#000000"/> <Variable name="widget.alternate.text.color" description="Alternate Color" type="color" default="#999999"/> </Group> <Group description="Images" selector=".main-inner"> <Variable name="image.background.color" description="Background Color" type="color" default="#ffffff"/> <Variable name="image.border.color" description="Border Color" type="color" default="#eeeeee"/> <Variable name="image.text.color" description="Caption Text Color" type="color" default="#000000"/> </Group> <Group description="Accents" selector=".content-inner"> <Variable name="body.rule.color" description="Separator Line Color" type="color" default="#eeeeee"/> <Variable name="tabs.border.color" description="Tabs Border Color" type="color" default="transparent"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#eeeeee" default="$(color) none repeat scroll top left"/> <Variable name="body.background.override" description="Body Background Override" type="string" default=""/> <Variable name="body.background.gradient.cap" description="Body Gradient Cap" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="body.background.gradient.tile" description="Body Gradient Tile" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/body_gradient_tile_light.png)"/> <Variable name="content.background.color.selector" description="Content Background Color Selector" type="string" default=".content-inner"/> <Variable name="content.padding" description="Content Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.padding.horizontal" description="Content Horizontal Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.shadow.spread" description="Content Shadow Spread" type="length" default="40px" min="0" max="100px"/> <Variable name="content.shadow.spread.webkit" description="Content Shadow Spread (WebKit)" type="length" default="5px" min="0" max="100px"/> <Variable name="content.shadow.spread.ie" description="Content Shadow Spread (IE)" type="length" default="10px" min="0" max="100px"/> <Variable name="main.border.width" description="Main Border Width" type="length" default="0" min="0" max="10px"/> <Variable name="header.background.gradient" description="Header Gradient" type="url" default="none"/> <Variable name="header.shadow.offset.left" description="Header Shadow Offset Left" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.offset.top" description="Header Shadow Offset Top" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.spread" description="Header Shadow Spread" type="length" default="1px" min="0" max="100px"/> <Variable name="header.padding" description="Header Padding" type="length" default="30px" min="0" max="100px"/> <Variable name="header.border.size" description="Header Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.bottom.border.size" description="Header Bottom Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.border.horizontalsize" description="Header Horizontal Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="description.text.size" description="Description Text Size" type="string" default="140%"/> <Variable name="tabs.margin.top" description="Tabs Margin Top" type="length" default="0" min="0" max="100px"/> <Variable name="tabs.margin.side" description="Tabs Side Margin" type="length" default="30px" min="0" max="100px"/> <Variable name="tabs.background.gradient" description="Tabs Background Gradient" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="tabs.border.width" description="Tabs Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="tabs.bevel.border.width" description="Tabs Bevel Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="post.margin.bottom" description="Post Bottom Margin" type="length" default="25px" min="0" max="100px"/> <Variable name="image.border.small.size" description="Image Border Small Size" type="length" default="2px" min="0" max="10px"/> <Variable name="image.border.large.size" description="Image Border Large Size" type="length" default="5px" min="0" max="10px"/> <Variable name="page.width.selector" description="Page Width Selector" type="string" default=".region-inner"/> <Variable name="page.width" description="Page Width" type="string" default="auto"/> <Variable name="main.section.margin" description="Main Section Margin" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding" description="Main Padding" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding.top" description="Main Padding Top" type="length" default="30px" min="0" max="100px"/> <Variable name="main.padding.bottom" description="Main Padding Bottom" type="length" default="30px" min="0" max="100px"/> <Variable name="paging.background" color="#ffffff" description="Background of blog paging area" type="background" default="transparent none no-repeat scroll top center"/> <Variable name="footer.bevel" description="Bevel border length of footer" type="length" default="0" min="0" max="10px"/> <Variable name="mobile.background.overlay" description="Mobile Background Overlay" type="string" default="transparent none repeat scroll top left"/> <Variable name="mobile.background.size" description="Mobile Background Size" type="string" default="auto"/> <Variable name="mobile.button.color" description="Mobile Button Color" type="color" default="#ffffff" /> <Variable name="startSide" description="Side where text starts in blog language" type="automatic" default="left"/> <Variable name="endSide" description="Side where text ends in blog language" type="automatic" default="right"/> */ /* Content ----------------------------------------------- */ body { font: normal normal 12px Open Sans; color: #000000; background: #eeeeee none repeat scroll top left; padding: 0 0 0 0; } html body .region-inner { min-width: 0; max-width: 100%; width: auto; } h2 { font-size: 22px; } a:link { text-decoration:none; color: #2288bb; } a:visited { text-decoration:none; color: #888888; } a:hover { text-decoration:underline; color: #33aaff; } .body-fauxcolumn-outer .fauxcolumn-inner { background: transparent none repeat scroll top left; _background-image: none; } .body-fauxcolumn-outer .cap-top { position: absolute; z-index: 1; height: 400px; width: 100%; } .body-fauxcolumn-outer .cap-top .cap-left { width: 100%; background: transparent none repeat-x scroll top left; _background-image: none; } .content-outer { -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -goog-ms-box-shadow: 0 0 0 #333333; box-shadow: 0 0 0 rgba(0, 0, 0, .15); margin-bottom: 1px; } .content-inner { padding: 10px 40px; } .content-inner { background-color: #ffffff; } /* Header ----------------------------------------------- */ .header-outer { background: transparent none repeat-x scroll 0 -400px; _background-image: none; } .Header h1 { font: normal normal 40px Open Sans; color: #000000; text-shadow: 0 0 0 rgba(0, 0, 0, .2); } .Header h1 a { color: #000000; } .Header .description { font-size: 18px; color: #000000; } .header-inner .Header .titlewrapper { padding: 22px 0; } .header-inner .Header .descriptionwrapper { padding: 0 0; } /* Tabs ----------------------------------------------- */ .tabs-inner .section:first-child { border-top: 0 solid #dddddd; } .tabs-inner .section:first-child ul { margin-top: -1px; border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; } .tabs-inner .widget ul { background: transparent none repeat-x scroll 0 -800px; _background-image: none; border-bottom: 1px solid #dddddd; margin-top: 0; margin-left: -30px; margin-right: -30px; } .tabs-inner .widget li a { display: inline-block; padding: .6em 1em; font: normal normal 12px Open Sans; color: #000000; border-left: 1px solid #ffffff; border-right: 1px solid #dddddd; } .tabs-inner .widget li:first-child a { border-left: none; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; background-color: #eeeeee; text-decoration: none; } /* Columns ----------------------------------------------- */ .main-outer { border-top: 0 solid transparent; } .fauxcolumn-left-outer .fauxcolumn-inner { border-right: 1px solid transparent; } .fauxcolumn-right-outer .fauxcolumn-inner { border-left: 1px solid transparent; } /* Headings ----------------------------------------------- */ div.widget > h2, div.widget h2.title { margin: 0 0 1em 0; font: normal bold 11px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; color: #000000; } /* Widgets ----------------------------------------------- */ .widget .zippy { color: #999999; text-shadow: 2px 2px 1px rgba(0, 0, 0, .1); } .widget .popular-posts ul { list-style: none; } /* Posts ----------------------------------------------- */ h2.date-header { font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } .date-header span { background-color: #bbbbbb; color: #ffffff; padding: 0.4em; letter-spacing: 3px; margin: inherit; } .main-inner { padding-top: 35px; padding-bottom: 65px; } .main-inner .column-center-inner { padding: 0 0; } .main-inner .column-center-inner .section { margin: 0 1em; } .post { margin: 0 0 45px 0; } h3.post-title, .comments h4 { font: normal normal 22px Open Sans; margin: .75em 0 0; } .post-body { font-size: 110%; line-height: 1.4; position: relative; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 2px; background: #ffffff; border: 1px solid #eeeeee; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body .tr-caption-container { color: #666666; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } .post-header { margin: 0 0 1.5em; line-height: 1.6; font-size: 90%; } .post-footer { margin: 20px -2px 0; padding: 5px 10px; color: #666666; background-color: #eeeeee; border-bottom: 1px solid #eeeeee; line-height: 1.6; font-size: 90%; } #comments .comment-author { padding-top: 1.5em; border-top: 1px solid transparent; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } #comments .avatar-image-container img { border: 1px solid #eeeeee; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #999999; border-bottom: 1px solid #999999; } .comments .comment-thread.inline-thread { background-color: #eeeeee; } .comments .continue { border-top: 2px solid #999999; } /* Accents ---------------------------------------------- */ .section-columns td.columns-cell { border-left: 1px solid transparent; } .blog-pager { background: transparent url(//www.blogblog.com/1kt/simple/paging_dot.png) repeat-x scroll top center; } .blog-pager-older-link, .home-link, .blog-pager-newer-link { background-color: #ffffff; padding: 5px; } .footer-outer { border-top: 1px dashed #bbbbbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } .mobile .body-fauxcolumn-outer .cap-top { background-size: 100% auto; } .mobile .content-outer { -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); box-shadow: 0 0 3px rgba(0, 0, 0, .15); } .mobile .tabs-inner .widget ul { margin-left: 0; margin-right: 0; } .mobile .post { margin: 0; } .mobile .main-inner .column-center-inner .section { margin: 0; } .mobile .date-header span { padding: 0.1em 10px; margin: 0 -10px; } .mobile h3.post-title { margin: 0; } .mobile .blog-pager { background: transparent none no-repeat scroll top center; } .mobile .footer-outer { border-top: none; } .mobile .main-inner, .mobile .footer-inner { background-color: #ffffff; } .mobile-index-contents { color: #000000; } .mobile-link-button { background-color: #2288bb; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #ffffff; } .mobile .tabs-inner .section:first-child { border-top: none; } .mobile .tabs-inner .PageList .widget-content { background-color: #eeeeee; color: #000000; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #dddddd; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 1120px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 1120px; max-width: 1120px; _width: 1120px; } .main-inner .columns { padding-left: 0; padding-right: 310px; } .main-inner .fauxcolumn-center-outer { left: 0; right: 310px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0") - parseInt("310px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0; } .main-inner .fauxcolumn-right-outer { width: 310px; } .main-inner .column-left-outer { width: 0; right: 100%; margin-left: -0; } .main-inner .column-right-outer { width: 310px; margin-right: -310px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } body#layout div.add_widget { padding: 8px; } body#layout div.add_widget a { margin-left: 32px; } --></style> <script type='text/javascript'> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-240546891-1', 'auto', 'blogger'); ga('blogger.send', 'pageview'); </script> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&amp;zx=dd12d16f-47b1-45ec-9a52-9d6602fe5782' media='none' onload='if(media!=&#39;all&#39;)media=&#39;all&#39;' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&amp;zx=dd12d16f-47b1-45ec-9a52-9d6602fe5782' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body class='loading'> <div class='navbar section' id='navbar' name='Navbar'><div class='widget Navbar' data-version='1' id='Navbar1'><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar/4838136820032157985?po\x3d2892029424446991022\x26origin\x3dhttps://googleprojectzero.blogspot.com', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script><script type="text/javascript"> (function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '//pagead2.googlesyndication.com/pagead/js/google_top_exp.js'; var head = document.getElementsByTagName('head')[0]; if (head) { head.appendChild(script); }})(); </script> </div></div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header' name='Header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner'> <div class='titlewrapper'> <h1 class='title'> <a href='https://googleprojectzero.blogspot.com/'> Project Zero </a> </h1> </div> <div class='descriptionwrapper'> <p class='description'><span>News and updates from the Project Zero team at Google</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol' name='Cross-Column'></div> <div class='tabs no-items section' id='crosscol-overflow' name='Cross-Column 2'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main' name='Main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class="date-outer"> <h2 class='date-header'><span>Thursday, March 24, 2022</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png' itemprop='image_url'/> <meta content='4838136820032157985' itemprop='blogId'/> <meta content='2892029424446991022' itemprop='postId'/> <a name='2892029424446991022'></a> <h3 class='post-title entry-title' itemprop='name'> Racing against the clock -- hitting a tiny kernel race window </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-2892029424446991022' itemprop='description articleBody'> <style type="text/css">.lst-kix_oh8g77k3z2ge-8>li:before{content:"\0025a0 "}ol.lst-kix_94mysn7p9quk-2.start{counter-reset:lst-ctn-kix_94mysn7p9quk-2 0}.lst-kix_94mysn7p9quk-5>li{counter-increment:lst-ctn-kix_94mysn7p9quk-5}.lst-kix_oh8g77k3z2ge-6>li:before{content:"\0025cf "}ol.lst-kix_94mysn7p9quk-7{list-style-type:none}ol.lst-kix_94mysn7p9quk-6{list-style-type:none}.lst-kix_oh8g77k3z2ge-7>li:before{content:"\0025cb "}ol.lst-kix_94mysn7p9quk-8{list-style-type:none}.lst-kix_oh8g77k3z2ge-4>li:before{content:"\0025cb "}ol.lst-kix_94mysn7p9quk-3{list-style-type:none}ol.lst-kix_94mysn7p9quk-2{list-style-type:none}ol.lst-kix_94mysn7p9quk-5{list-style-type:none}ol.lst-kix_94mysn7p9quk-4{list-style-type:none}.lst-kix_oh8g77k3z2ge-5>li:before{content:"\0025a0 "}ol.lst-kix_94mysn7p9quk-1{list-style-type:none}ol.lst-kix_94mysn7p9quk-0{list-style-type:none}.lst-kix_oh8g77k3z2ge-0>li:before{content:"\0025cf "}.lst-kix_94mysn7p9quk-0>li{counter-increment:lst-ctn-kix_94mysn7p9quk-0}.lst-kix_oh8g77k3z2ge-2>li:before{content:"\0025a0 "}.lst-kix_oh8g77k3z2ge-3>li:before{content:"\0025cf "}ol.lst-kix_94mysn7p9quk-5.start{counter-reset:lst-ctn-kix_94mysn7p9quk-5 0}.lst-kix_94mysn7p9quk-4>li{counter-increment:lst-ctn-kix_94mysn7p9quk-4}.lst-kix_oh8g77k3z2ge-1>li:before{content:"\0025cb "}ol.lst-kix_94mysn7p9quk-8.start{counter-reset:lst-ctn-kix_94mysn7p9quk-8 0}.lst-kix_u4yb8nbjtsh3-5>li:before{content:"\0025a0 "}.lst-kix_u4yb8nbjtsh3-6>li:before{content:"\0025cf "}ol.lst-kix_94mysn7p9quk-1.start{counter-reset:lst-ctn-kix_94mysn7p9quk-1 0}.lst-kix_u4yb8nbjtsh3-4>li:before{content:"\0025cb "}.lst-kix_u4yb8nbjtsh3-8>li:before{content:"\0025a0 "}.lst-kix_94mysn7p9quk-0>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-0,decimal) ". "}.lst-kix_u4yb8nbjtsh3-7>li:before{content:"\0025cb "}.lst-kix_94mysn7p9quk-3>li{counter-increment:lst-ctn-kix_94mysn7p9quk-3}.lst-kix_94mysn7p9quk-6>li{counter-increment:lst-ctn-kix_94mysn7p9quk-6}ol.lst-kix_94mysn7p9quk-4.start{counter-reset:lst-ctn-kix_94mysn7p9quk-4 0}.lst-kix_94mysn7p9quk-2>li{counter-increment:lst-ctn-kix_94mysn7p9quk-2}ol.lst-kix_94mysn7p9quk-7.start{counter-reset:lst-ctn-kix_94mysn7p9quk-7 0}.lst-kix_94mysn7p9quk-6>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-6,decimal) ". "}.lst-kix_94mysn7p9quk-8>li{counter-increment:lst-ctn-kix_94mysn7p9quk-8}ul.lst-kix_oh8g77k3z2ge-7{list-style-type:none}ul.lst-kix_oh8g77k3z2ge-8{list-style-type:none}.lst-kix_94mysn7p9quk-5>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-5,lower-roman) ". "}ul.lst-kix_oh8g77k3z2ge-3{list-style-type:none}ul.lst-kix_oh8g77k3z2ge-4{list-style-type:none}.lst-kix_u4yb8nbjtsh3-0>li:before{content:"\0025cf "}ul.lst-kix_oh8g77k3z2ge-5{list-style-type:none}ul.lst-kix_oh8g77k3z2ge-6{list-style-type:none}.lst-kix_94mysn7p9quk-4>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-4,lower-latin) ". "}ul.lst-kix_oh8g77k3z2ge-0{list-style-type:none}.lst-kix_u4yb8nbjtsh3-1>li:before{content:"\0025cb "}.lst-kix_u4yb8nbjtsh3-2>li:before{content:"\0025a0 "}ul.lst-kix_oh8g77k3z2ge-1{list-style-type:none}.lst-kix_94mysn7p9quk-1>li{counter-increment:lst-ctn-kix_94mysn7p9quk-1}ul.lst-kix_oh8g77k3z2ge-2{list-style-type:none}ol.lst-kix_94mysn7p9quk-0.start{counter-reset:lst-ctn-kix_94mysn7p9quk-0 0}.lst-kix_94mysn7p9quk-1>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-1,lower-latin) ". "}.lst-kix_94mysn7p9quk-3>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-3,decimal) ". "}.lst-kix_94mysn7p9quk-7>li{counter-increment:lst-ctn-kix_94mysn7p9quk-7}.lst-kix_u4yb8nbjtsh3-3>li:before{content:"\0025cf "}.lst-kix_94mysn7p9quk-2>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-2,lower-roman) ". "}ul.lst-kix_u4yb8nbjtsh3-6{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-5{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-8{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-7{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-2{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-1{list-style-type:none}ol.lst-kix_94mysn7p9quk-3.start{counter-reset:lst-ctn-kix_94mysn7p9quk-3 0}ul.lst-kix_u4yb8nbjtsh3-4{list-style-type:none}ul.lst-kix_u4yb8nbjtsh3-3{list-style-type:none}li.li-bullet-0:before{margin-left:-18pt;white-space:nowrap;display:inline-block;min-width:18pt}ol.lst-kix_94mysn7p9quk-6.start{counter-reset:lst-ctn-kix_94mysn7p9quk-6 0}.lst-kix_94mysn7p9quk-7>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-7,lower-latin) ". "}.lst-kix_94mysn7p9quk-8>li:before{content:"" counter(lst-ctn-kix_94mysn7p9quk-8,lower-roman) ". "}ul.lst-kix_u4yb8nbjtsh3-0{list-style-type:none}ol{margin:0;padding:0}table td,table th{padding:0}.c15{padding-top:20pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.c0{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left;height:11pt}.c1{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c8{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:20pt;font-family:"Arial";font-style:normal}.c5{color:#cc0000;font-weight:700;text-decoration:none;vertical-align:baseline;font-size:9pt;font-family:"Courier New";font-style:normal}.c26{padding-top:18pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.c13{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:italic}.c2{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:9pt;font-family:"Courier New";font-style:normal}.c3{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left}.c10{color:#000000;text-decoration:none;vertical-align:baseline;font-size:10pt;font-style:normal}.c7{color:#000000;text-decoration:none;vertical-align:baseline;font-size:11pt;font-style:normal}.c11{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.c22{color:#000000;text-decoration:none;vertical-align:baseline;font-style:normal}.c23{font-weight:400;font-size:16pt;font-family:"Arial"}.c19{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c4{font-weight:400;font-family:"Courier New"}.c20{padding:0;margin:0}.c12{color:inherit;text-decoration:inherit}.c17{margin-left:108pt;padding-left:0pt}.c14{margin-left:36pt;padding-left:0pt}.c21{margin-left:72pt;padding-left:0pt}.c16{font-style:italic}.c25{background-color:#f4cccc}.c6{font-weight:700}.c18{font-family:"Courier New"}.c9{font-size:9pt}.c24{font-size:10pt}.title{padding-top:0pt;color:#000000;font-size:26pt;padding-bottom:3pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:0pt;color:#666666;font-size:15pt;padding-bottom:16pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Arial"}p{margin:0;color:#000000;font-size:11pt;font-family:"Arial"}h1{padding-top:20pt;color:#000000;font-size:20pt;padding-bottom:6pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-size:16pt;padding-bottom:6pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:16pt;color:#434343;font-size:14pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:14pt;color:#666666;font-size:12pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}</style></head><body class="c19"> <p class="c3"><span class="c6">TL;DR:</span></p> <p class="c3"><span>How to make a tiny kernel race window really large even on kernels without </span><span class="c4">CONFIG_PREEMPT</span><span class="c1">:</span></p><ul style="padding: 0;" class="c20 lst-kix_u4yb8nbjtsh3-0 start"><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span>use</span><span class="c1">&nbsp;a cache miss to widen the race window a little bit</span></li><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span class="c1">make a timerfd expire in that window (which will run in an interrupt handler - in other words, in hardirq context)</span></li><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span class="c1">make sure that the wakeup triggered by the timerfd has to churn through 50000 waitqueue items created by epoll</span></li></ul> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">Racing one thread against a timer also avoids accumulating timing variations from two threads in each race attempt - hence the title. On the other hand, it also means you now have to deal with how hardware timers actually work, which introduces its own flavors of weird timing variations.</span></p><h1 class="c15" id="h.au2flcod0vb"><span class="c8">Introduction</span></h1> <p class="c3"><span>I recently discovered a race condition (</span><span class="c11"><a class="c121" href="https://crbug.com/project-zero/2247">https://crbug.com/project-zero/2247</a></span><span>) in the Linux kernel.</span><span>&nbsp;(While trying to explain to someone how </span><span class="c11"><a class="c121" href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cbcf01128d0a92e131bd09f1688fe032480b65ca">the fix for CVE-2021-0920</a></span><span>&nbsp;worked - I was explaining why the Unix GC is now safe, and then got confused because I couldn&#39;t actually figure out why it&#39;s safe after that fix, eventually realizing that it actually isn&#39;t safe.) It&#39;s a fairly narrow race window, so I was wondering whether it could be hit with a small number of attempts - especially on kernels that aren&#39;t built with </span><span class="c4">CONFIG_PREEMPT</span><span>, which would make it possible to preempt a thread with another thread, </span><span class="c11"><a class="c121" href="https://static.sched.com/hosted_files/lsseu2019/04/LSSEU2019%20-%20Exploiting%20race%20conditions%20on%20Linux.pdf#page=12">as I described at LSSEU2019</a></span><span class="c1">.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>This is a writeup of how I managed to hit the race on a normal Linux desktop kernel, with a hit rate somewhere around 30% if the proof of concept has been tuned for the specific machine. I didn&#39;t do a full exploit though, I stopped at getting evidence of use-after-free (UAF) accesses (with the help of a very large file descriptor table and </span><span class="c11"><a class="c121" href="https://lwn.net/Articles/819834/">userfaultfd</a></span><span class="c1">, which might not be available to normal users depending on system configuration) because that&#39;s the part I was curious about.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c6">This also demonstrates that even very small race conditions can still be exploitable if someone sinks enough time into writing an exploit, so be careful if you dismiss very small race windows as unexploitable or don&#39;t treat such issues as security bugs.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>The UAF reproducer is </span><span class="c11"><a class="c121" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2247#c6">in our bugtracker</a></span><span class="c1">.</span></p><h1 class="c15" id="h.qtcj62oowcyh"><span class="c8">The bug</span></h1> <p class="c3"><span>In the UNIX domain socket garbage collection code (which is needed to deal with reference loops formed by UNIX</span><span>&nbsp;domain sockets</span><span>&nbsp;that use </span><span class="c4">SCM_RIGHTS</span><span class="c1">&nbsp;file descriptor passing), the kernel tries to figure out whether it can account for all references to some file by comparing the file&#39;s refcount with the number of references from inflight SKBs (socket buffers). If they are equal, it assumes that the UNIX domain sockets subsystem effectively has exclusive access to the file because it owns all references.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>(The same pattern also appears for files as an optimization in </span><span class="c4">__fdget_pos()</span><span>, see </span><span class="c11"><a class="c121" href="https://lore.kernel.org/lkml/CAG48ez1pnatAB095dnbrn9LbuQe4+ENwh-WEW36pM40ozhpruw@mail.gmail.com/">this LKML thread</a></span><span class="c1">.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>The problem is that </span><span class="c4">struct file</span><span>&nbsp;can also be referenced from an RCU read-side critical section (which you can&#39;t detect by looking at the refcount), and such an RCU reference can be upgraded into a refcounted reference using </span><span class="c4">get_file_rcu()</span><span>&nbsp;/ </span><span class="c4">get_file_rcu_many()</span><span>&nbsp;by </span><span class="c4">__fget_files()</span><span>&nbsp;as long as the refcount is non-zero. For example, when this happens in the </span><span class="c4">dup()</span><span class="c1">&nbsp;syscall, the resulting reference will then be installed in the FD table and be available for subsequent syscalls.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>When the garbage collector (GC) believes that it has exclusive access to a file, it will perform operations on that file that violate the locking rules used in normal socket-related syscalls such as </span><span class="c4">recvmsg()</span><span>&nbsp;- </span><span class="c4">unix_stream_read_generic()</span><span>&nbsp;assumes that queued SKBs can only be removed under the </span><span class="c4">-&gt;iolock</span><span>&nbsp;mutex, but the GC removes queued SKBs without using that mutex. (Thanks to Xingyu Jin for explaining that to me.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>One way of looking at this bug is that the GC is working correctly - here&#39;s a state diagram showing some of the possible states of a </span><span class="c4">struct file</span><span class="c1">, with more specific states nested under less specific ones and with the state transition in the GC marked:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="All relevant states are RCU-accessible. An RCU-accessible object can have either a zero refcount or a positive refcount. Objects with a positive refcount can be either live or owned by the garbage collector. When the GC attempts to grab a file, it transitions from the state &quot;live&quot; to the state &quot;owned by GC&quot; by getting exclusive ownership of all references to the file." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png" style="max-height: 750; max-width: 600px;" title="All relevant states are RCU-accessible. An RCU-accessible object can have either a zero refcount or a positive refcount. Objects with a positive refcount can be either live or owned by the garbage collector. When the GC attempts to grab a file, it transitions from the state &quot;live&quot; to the state &quot;owned by GC&quot; by getting exclusive ownership of all references to the file." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>While </span><span class="c4">__fget_files()</span><span>&nbsp;is making an incorrect assumption about the state of the </span><span class="c4">struct file</span><span>&nbsp;while it is trying to narrow down its possible states - it checks whether </span><span class="c4">get_file_rcu()</span><span>&nbsp;/ </span><span class="c4">get_file_rcu_many()</span><span class="c1">&nbsp;succeeds, which narrows the file&#39;s state down a bit but not far enough:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgab4PkrLpvTF9E3D-nw5dSk35gvYRa--cQy0Oi60WjpbOjZbKE5zKNWa5ZEsuI-q7-UVZJrJD4Au-gTBF0p1VJSQ9gY75yIYL72zENk2Nt9RSQXBeJrIjtueSKK3xi3QN3a7dSAKXf-6JAL0RZXrnpCh3CdcC_QAyZK-hbbqXesvQQWlys0V-N3PE7/s638/image10%281%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="__fget_files() first uses get_file_rcu() to conditionally narrow the state of a file from &quot;any RCU-accessible state&quot; to &quot;any refcounted state&quot;. Then it has to narrow the state from &quot;any refcounted state&quot; to &quot;live&quot;, but instead it just assumes that they are equivalent." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgab4PkrLpvTF9E3D-nw5dSk35gvYRa--cQy0Oi60WjpbOjZbKE5zKNWa5ZEsuI-q7-UVZJrJD4Au-gTBF0p1VJSQ9gY75yIYL72zENk2Nt9RSQXBeJrIjtueSKK3xi3QN3a7dSAKXf-6JAL0RZXrnpCh3CdcC_QAyZK-hbbqXesvQQWlys0V-N3PE7/s638/image10%281%29.png" style="max-height: 750; max-width: 600px;" title="__fget_files() first uses get_file_rcu() to conditionally narrow the state of a file from &quot;any RCU-accessible state&quot; to &quot;any refcounted state&quot;. Then it has to narrow the state from &quot;any refcounted state&quot; to &quot;live&quot;, but instead it just assumes that they are equivalent." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>And this directly leads to how </span><span class="c11"><a class="c121" href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=054aa8d439b9185d4f5eb9a90282d1ce74772969">the bug was fixed</a></span><span>&nbsp;(there&#39;s </span><span class="c11"><a class="c121" href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e386dfc56f837da66d00a078e5314bc8382fab83">another follow-up patch</a></span><span>, but that one just tries to clarify the code and recoup some of the resulting performance loss) - the fix adds another check in </span><span class="c4">__fget_files()</span><span>&nbsp;to properly narrow down the state of the file such that the file is guaranteed to be live</span><span class="c1">:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit6FDn3u3QdHshSHrZY09kFv_HtKUA7KlZZNKLc9t8Tx3uGJsC14qcx_IU691a14IUOH-OgfDjGpRLGh4Cwx4nlBfap1fCVOzT5PuOQXVv6e1-t79J46UO-uuUL_s6QzABu8JkrnEtyKV68GovV_o8Yem6E8Y9sl23usVNoXnoJ5xOhPZFVsconARG/s638/image16.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="The fix is to properly narrow the state from &quot;any refcounted state&quot; to &quot;live&quot; by checking whether the file is still referenced by a file descriptor table entry." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit6FDn3u3QdHshSHrZY09kFv_HtKUA7KlZZNKLc9t8Tx3uGJsC14qcx_IU691a14IUOH-OgfDjGpRLGh4Cwx4nlBfap1fCVOzT5PuOQXVv6e1-t79J46UO-uuUL_s6QzABu8JkrnEtyKV68GovV_o8Yem6E8Y9sl23usVNoXnoJ5xOhPZFVsconARG/s638/image16.png" style="max-height: 750; max-width: 600px;" title="The fix is to properly narrow the state from &quot;any refcounted state&quot; to &quot;live&quot; by checking whether the file is still referenced by a file descriptor table entry." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">The fix ensures that a live reference can only be derived from another live reference by comparing with an FD table entry, which is guaranteed to point to a live object.</span></p> <p class="c3"><span class="c16">[Sidenote: This scheme is similar to the one used for </span><span class="c4 c16">struct page</span><span class="c16">&nbsp;- </span><span class="c4 c16">gup_pte_range()</span><span class="c16">&nbsp;also uses the &quot;grab pointer, increment refcount, recheck pointer&quot; pattern for locklessly looking up a </span><span class="c4 c16">struct page</span><span class="c16">&nbsp;from a page table entry while ensuring that new refcounted references can&#39;t be created without holding an existing reference. This is really important for </span><span class="c4 c16">struct page</span><span class="c16">&nbsp;because a page can be given back to the page allocator and reused while </span><span class="c4 c16">gup_pte_range()</span><span class="c16">&nbsp;holds an uncounted reference to it - freed pages still have their </span><span class="c4 c16">struct page</span><span class="c13">, so there&#39;s no need to delay freeing of the page - so if this went wrong, you&#39;d get a page UAF.]</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>My initial suggestion was to instead fix the issue by changing how </span><span class="c4">unix_gc()</span><span>&nbsp;ensures that it has exclusive access, letting it set the file&#39;s refcount to zero to prevent turning RCU references into refcounted ones; this would have avoided adding any code in the hot </span><span class="c4">__fget_files()</span><span>&nbsp;path, but it would have only fixed </span><span class="c4">unix_gc()</span><span>, not the </span><span class="c4">__fdget_pos()</span><span class="c1">&nbsp;case I discovered later, so it&#39;s probably a good thing this isn&#39;t how it was fixed:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1Eob19v9RoctxBGUR4nmMSrJQJXvw-WjVihOxiudjG3ssu5WfL-YRXj_Gd99CifW1GplS3Yhwlc2WIc0-nPSOecYylZouk0zco24SgxUyyaFDps2wck4jVhWCcSbU7l3cFKB-hk4q3EOwwFJIgFKFSNnW9DgprGIMo5EKTMCt7SFnNdofQ9LnCjQu/s641/image5%283%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1Eob19v9RoctxBGUR4nmMSrJQJXvw-WjVihOxiudjG3ssu5WfL-YRXj_Gd99CifW1GplS3Yhwlc2WIc0-nPSOecYylZouk0zco24SgxUyyaFDps2wck4jVhWCcSbU7l3cFKB-hk4q3EOwwFJIgFKFSNnW9DgprGIMo5EKTMCt7SFnNdofQ9LnCjQu/s641/image5%283%29.png" style="max-height: 750; max-width: 600px;" title="" /></a></span></p> <p class="c3"><span class="c16">[Sidenote: In my original bug report I wrote that you&#39;d have to wait an RCU grace period in the GC for this, but that wouldn&#39;t be necessary as long as the GC ensures that a reaped socket&#39;s refcount never becomes non-zero again.]</span></p><h1 class="c15" id="h.60irov9pzlh"><span class="c8">The race</span></h1> <p class="c3"><span>There are multiple race conditions involved in exploiting this bug, but by far the trickiest to hit is that we have to race an operation into the tiny race window in the middle of </span><span class="c4">__fget_files()</span><span>&nbsp;(which can e.g. be reached via </span><span class="c4">dup()</span><span class="c1">), between the file descriptor table lookup and the refcount increment:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c2">static struct file *__fget_files(struct files_struct *files, unsigned int fd,</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fmode_t mask, unsigned int refs)</span></p> <p class="c3"><span class="c2">{</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; struct file *file;</span></p> <p class="c0"><span class="c2"></span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; rcu_read_lock();</span></p> <p class="c3"><span class="c2">loop:</span></p> <p class="c3"><span class="c4 c9">&nbsp; &nbsp; &nbsp; &nbsp; file = </span><span class="c6 c18 c9">files_lookup_fd_rcu(files, fd)</span><span class="c4 c9">; </span><span class="c5">// race window start</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; if (file) {</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* File object ref couldn&#39;t be taken.</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* dup2() atomicity guarantee is the reason</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* we loop to catch the new file (or NULL pointer)</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (file-&gt;f_mode &amp; mask)</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file = NULL;</span></p> <p class="c3"><span class="c4 c9">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else if (!</span><span class="c6 c9 c18">get_file_rcu_many(file, refs)</span><span class="c4 c9">) </span><span class="c5">// race window end</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto loop;</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; }</span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; rcu_read_unlock();</span></p> <p class="c0"><span class="c2"></span></p> <p class="c3"><span class="c2">&nbsp; &nbsp; &nbsp; &nbsp; return file;</span></p> <p class="c3"><span class="c2">}</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>In this race window</span><span>, th</span><span>e file descriptor must be closed (to drop the FD&#39;s reference to the file) and a </span><span class="c4">unix_gc()</span><span>&nbsp;run must get past the point where it checks the file&#39;s refcount (&quot;</span><span class="c4 c24">total_refs = file_count(u-&gt;sk.sk_socket-&gt;file)</span><span class="c1">&quot;).</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">In the Debian 5.10.0-9-amd64 kernel at version 5.10.70-1, that race window looks as follows:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x1e&gt; cmp &nbsp; &nbsp;r10,rax</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x21&gt; sbb &nbsp; &nbsp;rax,rax</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x24&gt; mov &nbsp; &nbsp;rdx,QWORD PTR [r11+0x8]</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x28&gt; and &nbsp; &nbsp;eax,r8d</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x2b&gt; lea &nbsp; &nbsp;rax,[rdx+rax*8]</span></p> <p class="c3"><span class="c4 c9">&lt;__fget_files+0x2f&gt; mov &nbsp; &nbsp;r12,QWORD PTR [rax] </span><span class="c22 c6 c18 c9">; RACE WINDOW START</span></p> <p class="c3"><span class="c22 c6 c18 c9">; r12 now contains file*</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x32&gt; test &nbsp; r12,r12</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x35&gt; je &nbsp; &nbsp; ffffffff812e3df7 &lt;__fget_files+0x77&gt;</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x37&gt; mov &nbsp; &nbsp;eax,r9d</span></p> <p class="c3"><span class="c4 c9">&lt;__fget_files+0x3a&gt; and &nbsp; &nbsp;eax,DWORD PTR [r12+0x44] </span><span class="c6 c18 c9 c22">; LOAD (for -&gt;f_mode)</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x3f&gt; jne &nbsp; &nbsp;ffffffff812e3df7 &lt;__fget_files+0x77&gt;</span></p> <p class="c3"><span class="c4 c9">&lt;__fget_files+0x41&gt; mov &nbsp; &nbsp;rax,QWORD PTR [r12+0x38] </span><span class="c22 c6 c18 c9">; LOAD (for -&gt;f_count)</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x46&gt; lea &nbsp; &nbsp;rdx,[r12+0x38]</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x4b&gt; test &nbsp; rax,rax</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x4e&gt; je &nbsp; &nbsp; ffffffff812e3def &lt;__fget_files+0x6f&gt;</span></p> <p class="c3"><span class="c2">&lt;__fget_files+0x50&gt; lea &nbsp; &nbsp;rcx,[rsi+rax*1]</span></p> <p class="c3"><span class="c4 c9">&lt;__fget_files+0x54&gt; lock cmpxchg QWORD PTR [rdx],rcx </span><span class="c6 c18 c9">; RACE WINDOW END (on cmpxchg success)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>As you can see, the race window is fairly small - around 12 instructions, assuming that the </span><span class="c4">cmpxchg</span><span class="c1">&nbsp;succeeds.</span></p><h1 class="c15" id="h.dhdzjamcop53"><span class="c8">Missing some cache</span></h1> <p class="c3"><span>Luckily for us, the race window contains the first few memory accesses to the </span><span class="c4">struct file</span><span>; therefore, by making sure that the </span><span class="c4">struct file</span><span>&nbsp;is not present in the fastest CPU caches, we can widen the race window by as much time as the memory accesses take. The standard way to do this is to use </span><span>an eviction pattern</span><span>&nbsp;/ </span><span class="c11"><a class="c121" href="https://www.cs.columbia.edu/~simha/spyjs.ccs15.pdf">eviction set</a></span><span>; but instead we can also make the cache line dirty on another core (see </span><span class="c11"><a class="c121" href="https://dreamsofastone.blogspot.com/2016/02/row-hammer-java-script-and-mesi.html">Anders Fogh&#39;s blogpost</a></span><span>&nbsp;for more detail). (I&#39;m not actually sure about the intricacies of how much latency this adds on different manufacturers&#39; CPU cores, or on different CPU generations - I&#39;ve only tested different versions of my proof-of-concept on Intel Skylake and Tiger Lake. Differences in cache </span><span>coherency</span><span class="c1">&nbsp;protocols or snooping might make a big difference.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>For the cache line containing the flags and refcount of a </span><span class="c4">struct file</span><span>, this can be done by, on another CPU, temporarily bumping its refcount up and then changing it back down, e.g. with </span><span class="c4">close(dup(fd))</span><span class="c1">&nbsp;(or just by accessing the FD in pretty much any way from a multithreaded process).</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>However, when we&#39;re trying to hit the race in </span><span class="c4">__fget_files()</span><span>&nbsp;via </span><span class="c4">dup()</span><span>, we don&#39;t want any cache misses to occur before we hit the race window - that would slow us down and probably make us miss the race. To prevent that from happening, we can call </span><span class="c4">dup()</span><span class="c1">&nbsp;with a different FD number for a warm-up run shortly before attempting the race. Because we also want the relevant cache line in the FD table to be hot, we should choose the FD number for the warm-up run such that it uses the same cache line of the file descriptor table.</span></p><h1 class="c15" id="h.pfod85irgmms"><span class="c8">An interruption</span></h1> <p class="c3"><span>Okay, a cache miss might be something like a few dozen or maybe hundred nanoseconds or so - that&#39;s better, but it&#39;s not </span><span class="c16">great</span><span>. What </span><span>else can we do to make this tiny</span><span class="c1">&nbsp;piece of code much slower to execute?</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>On Android, kernels normally set </span><span class="c4">CONFIG_PREEMPT</span><span>, which would&#39;ve allowed abusing the scheduler to somehow interrupt the execution of this code. The way I&#39;ve done this in the past was to give the victim thread a low scheduler priority and pin it to a specific CPU core together with another high-priority thread that is blocked on a </span><span class="c4">read()</span><span class="c1">&nbsp;syscall on an empty pipe (or eventfd); when data is written to the pipe from another CPU core, the pipe becomes readable, so the high-priority thread (which is registered on the pipe&#39;s waitqueue) becomes schedulable, and an inter-processor interrupt (IPI) is sent to the victim&#39;s CPU core to force it to enter the scheduler immediately.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>One problem with that approach, aside from its reliance on </span><span class="c4">CONFIG_PREEMPT</span><span class="c1">, is that any timing variability in the kernel code involved in sending the IPI makes it harder to actually preempt the victim thread in the right spot.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>(Thanks to the </span><span class="c11"><a class="c121" href="https://xenproject.org/users/security/">Xen security team</a></span><span class="c1">&nbsp;- I think the first time I heard the idea of using an interrupt to widen a race window might have been from them.)</span></p><h1 class="c15" id="h.u9iu9sq2l8r"><span>Setting an alarm</span></h1> <p class="c3"><span>A better way to do this on an Android phone would be to trigger the scheduler not from an IPI, but from an expiring high-resolution timer on the same core</span><span>, although I didn&#39;t get it to work (probably because my code was broken in unrelated ways)</span><span class="c1">.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>High-resolution timers (hrtimers) are exposed through many userspace APIs. Even the timeout of </span><span class="c4">select()</span><span>/</span><span class="c4">pselect()</span><span>&nbsp;uses an hrtimer, although this is an hrtimer that normally has some slack applied to it to allow batching it with timers that are scheduled to expire a bit later. An example of a non-hrtimer-based API is the timeout used for reading from a UNIX domain socket (and probably also other types of sockets?), which can be set via </span><span class="c4">SO_RCVTIMEO</span><span class="c1">.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>The thing that makes hrtimers &quot;high-resolution&quot; is that they don&#39;t just wait for the next periodic clock tick to arrive; instead, the expiration time of the next hrtimer on the CPU core is programmed into a hardware timer. So we could set an absolute hrtimer for some time in the future via something like </span><span class="c4">timer_settime()</span><span>&nbsp;or </span><span class="c4">timerfd_settime()</span><span class="c1">, and then at exactly the programmed time, the hardware will raise an interrupt! We&#39;ve made the timing behavior of the OS irrelevant for the second side of the race, the only thing that matters is the hardware! Or... well, almost...</span></p><h1 class="c15" id="h.v6rvu85hdhx3"><span class="c8">[Sidenote] Absolute timers: Not quite absolute</span></h1> <p class="c3"><span>So we pick some absolute time at which we want to be interrupted, and tell the kernel using a syscall that accepts an absolute time, in nanoseconds. And then when that timer is the next one scheduled, the OS converts the absolute time to whatever clock base/scale the hardware timer is based on, and programs it into hardware. And the hardware usually supports programming timers with absolute time - e.g. on modern X86 (with </span><span class="c4">X86_FEATURE_TSC_DEADLINE_TIMER</span><span>), you can simply write an absolute Time Stamp Counter(TSC) deadline into </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span>, and when that deadline is reached, you get an interrupt. The situation on arm64 is similar, using the timer&#39;s comparator register (</span><span class="c4">CVAL</span><span class="c1">).</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>However, on both X86 and arm64, even though the clockevent subsystem is theoretically able to give absolute timestamps to clockevent drivers (via </span><span class="c4">-&gt;set_next_ktime()</span><span>), the drivers instead only implement </span><span class="c4">-&gt;set_next_event()</span><span class="c1">, which takes a relative time as argument. This means that the absolute timestamp has to be converted into a relative one, only to be converted back to absolute a short moment later. The delay between those two operations is essentially added to the timer&#39;s expiration time.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>Luckily this didn&#39;t really seem to be a problem for me; if it was, I would have tried to repeatedly call </span><span class="c4">timerfd_settime()</span><span>&nbsp;shortly before the planned expiry time to ensure that the last time the hardware timer is programmed, the relevant code path is hot in the caches. (I did do some experimentation on arm64, where this seemed to </span><span class="c16">maybe</span><span>&nbsp;help a tiny bit, but I didn&#39;t really analyze it properly.)</span></p><h1 class="c15" id="h.dtkpi9hjsrrs"><span class="c8">A really big list of things to do</span></h1> <p class="c3"><span>Okay, so all the stuff I said above would be helpful on an Android phone with </span><span class="c4">CONFIG_PREEMPT</span><span class="c1">, but what if we&#39;re trying to target a normal desktop/server kernel that doesn&#39;t have that turned on?</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">Well, we can still trigger hrtimer interrupts the same way - we just can&#39;t use them to immediately enter the scheduler and preempt the thread anymore. But instead of using the interrupt for preemption, we could just try to make the interrupt handler run for a really long time.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>Linux has the concept of a &quot;timerfd&quot;, which is a file descriptor that refers to a timer. You can e.g. call </span><span class="c4">read()</span><span class="c1">&nbsp;on a timerfd, and that operation will block until the timer has expired. Or you can monitor the timerfd using epoll, and it will show up as readable when the timer expires.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>When a timerfd becomes ready, all the timerfd&#39;s waiters (including epoll watches), which are queued up in a linked list, are woken up via the </span><span class="c4">wake_up()</span><span class="c1">&nbsp;path - just like when e.g. a pipe becomes readable. Therefore, if we can make the list of waiters really long, the interrupt handler will have to spend a lot of time iterating over that list.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>And for any waitqueue that is wired up to a file descriptor, it is fairly easy to add a ton of entries thanks to epoll. Epoll ties its watches to specific FD numbers, so if you duplicate an FD with hundreds of </span><span class="c4">dup()</span><span class="c1">&nbsp;calls, you can then use a single epoll instance to install hundreds of waiters on the file. Additionally, a single process can have lots of epoll instances. I used 500 epoll instances and 100 duplicate FDs, resulting in 50 000 waitqueue items.</span></p><h1 class="c15" id="h.gyrfobw0uoov"><span>Measuring race outcomes</span></h1> <p class="c3"><span>A nice aspect of this race condition is that if you only hit the difficult race (</span><span class="c4">close()</span><span>&nbsp;the FD and run </span><span class="c4">unix_gc()</span><span>&nbsp;while </span><span class="c4">dup()</span><span class="c1">&nbsp;is preempted between FD table lookup and refcount increment), no memory corruption happens yet, but you can observe that the GC has incorrectly removed a socket buffer (SKB) from the victim socket. Even better, if the race fails, you can also see in which direction it failed, as long as no FDs below the victim FD are unused:</span></p> <p class="c0"><span class="c1"></span></p><ul style="padding: 0;" class="c20 lst-kix_oh8g77k3z2ge-0 start"><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span>If </span><span class="c4">dup()</span><span>&nbsp;returns -1, it was called too late / the interrupt happened too soon: The </span><span class="c4">file*</span><span>&nbsp;was already gone from the FD table when </span><span class="c4">__fget_files()</span><span class="c1">&nbsp;tried to load it.</span></li><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span>If </span><span class="c4">dup()</span><span class="c1">&nbsp;returns a file descriptor:</span></li></ul><ul style="padding: 0;" class="c20 lst-kix_oh8g77k3z2ge-1 start"><li style="margin-left: 46pt;" class="c3 c21 li-bullet-0"><span>If it returns an FD higher than the victim FD, this implies that the victim FD was only closed after </span><span class="c4">dup()</span><span>&nbsp;had already elevated the refcount and allocated a new FD. This means </span><span class="c4">dup()</span><span class="c1">&nbsp;was called too soon / the interrupt happened too late.</span></li><li style="margin-left: 46pt;" class="c3 c21 li-bullet-0"><span class="c1">If it returns the old victim FD number:</span></li></ul><ul style="padding: 0;" class="c20 lst-kix_oh8g77k3z2ge-2 start"><li style="margin-left: 46pt;" class="c3 c17 li-bullet-0"><span>If </span><span class="c4">recvmsg()</span><span>&nbsp;on the FD returned by </span><span class="c4">dup()</span><span class="c1">&nbsp;returns no data, it means the race succeeded: The GC wrongly removed the queued SKB.</span></li><li style="margin-left: 46pt;" class="c3 c17 li-bullet-0"><span>If </span><span class="c4">recvmsg()</span><span>&nbsp;returns data, the interrupt happened between the refcount increment and the allocation of a new FD. </span><span class="c4">dup()</span><span class="c1">&nbsp;was called a little bit too soon / the interrupt happened a little bit too late.</span></li></ul> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">Based on this, I repeatedly tested different timing offsets, using a spinloop with a variable number of iterations to skew the timing, and plotted what outcomes the race attempts had depending on the timing skew.</span></p><h2 class="c26" id="h.8e0qhvge46uf"><span class="c22 c23">Results: Debian kernel, on Tiger Lake</span></h2> <p class="c3"><span class="c1">I tested this on a Tiger Lake laptop, with the same kernel as shown in the disassembly. Note that &quot;0&quot; on the X axis is offset -300 ns relative to the timer&#39;s programmed expiry.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxGtaXjmnSP7rJxi3RT9dLvnD5tGCVK-JFz8h3JVDjIDQ_2j-MpgyVqVJjh4zsldFBzadavB4_9nLx9KQ_fMSRHksfZM807yXykEgetDNCmeOzM21njTe8H9qH79Kz2MgqNL-5XWbxMy5fj9NamgjI15-5QZwJyn0NvHQwFuAw2z99_0lKwfrcWm4a/s580/image12.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="This graph shows histograms of race attempt outcomes (too early, success, or too late), with the timing offset at which the outcome occurred on the X axis. The graph shows that depending on the timing offset, up to around 1/3 of race attempts succeeded." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxGtaXjmnSP7rJxi3RT9dLvnD5tGCVK-JFz8h3JVDjIDQ_2j-MpgyVqVJjh4zsldFBzadavB4_9nLx9KQ_fMSRHksfZM807yXykEgetDNCmeOzM21njTe8H9qH79Kz2MgqNL-5XWbxMy5fj9NamgjI15-5QZwJyn0NvHQwFuAw2z99_0lKwfrcWm4a/s580/image12.png" style="max-height: 750; max-width: 600px;" title="This graph shows histograms of race attempt outcomes (too early, success, or too late), with the timing offset at which the outcome occurred on the X axis. The graph shows that depending on the timing offset, up to around 1/3 of race attempts succeeded." /></a></span></p><h2 class="c26" id="h.9b7pdk4c8xov"><span>Results: Other kernel, on Skylake</span></h2> <p class="c0"><span class="c1"></span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjer2duEFeMBnPTJc3X7kT-FPCR1yx9UFEABwrTUxFKOs-gFkM_N-XnkWxPbhg31yka-2b5CFBBnOeAmrVAr41WfzKKPUBoF1dDBRpaX0tPGpu4w8p5aBZrxewYuGl1A_CDt0CojQqK44ue86r9PI8HnBtD2IR3NJDAI8O4TByvvx5fFe0sMdmHYZ6b/s652/image8%283%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="This graph shows similar histograms for a Skylake processor. The exact distribution is different, but again, depending on the timing offset, around 1/3 of race attempts succeeded." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjer2duEFeMBnPTJc3X7kT-FPCR1yx9UFEABwrTUxFKOs-gFkM_N-XnkWxPbhg31yka-2b5CFBBnOeAmrVAr41WfzKKPUBoF1dDBRpaX0tPGpu4w8p5aBZrxewYuGl1A_CDt0CojQqK44ue86r9PI8HnBtD2IR3NJDAI8O4TByvvx5fFe0sMdmHYZ6b/s652/image8%283%29.png" style="max-height: 750; max-width: 600px;" title="This graph shows similar histograms for a Skylake processor. The exact distribution is different, but again, depending on the timing offset, around 1/3 of race attempts succeeded." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>These measurements are from an older laptop with a Skylake CPU, running a different kernel. Here &quot;0&quot; on the X axis is offset -1 us relative to the timer. (T</span><span>hese timings are from a system that&#39;s running a different kernel from </span><span class="c1">the one shown above, but I don&#39;t think that makes a difference.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">The exact timings of course look different between CPUs, and they probably also change based on CPU frequency scaling? But still, if you know what the right timing is (or measure the machine&#39;s timing before attempting to actually exploit the bug), you could hit this narrow race with a success rate of about 30%!</span></p> <p class="c0"><span class="c1"></span></p><h1 class="c15" id="h.5qhkbks84bg2"><span class="c8">How important is the cache miss?</span></h1> <p class="c3"><span>The previous section showed that with the right timing, the race succeeds with a probability around 30% - but it doesn&#39;t show whether the cache miss is actually important for that, or whether the race would still work fine without it. To verify that, I patched my test code to try to make the file&#39;s </span><span>cache line hot (present in the cache) instead of cold (not present in the cache):</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c10 c4">@@ -312,8 +312,10 @@</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;}</span></p> <p class="c3"><span class="c10 c4">&nbsp;</span></p> <p class="c3"><span class="c10 c4">+#if 0</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;// bounce socket&#39;s file refcount over to other cpu</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;pin_to(2);</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;close(SYSCHK(dup(RESURRECT_FD+1-1)));</span></p> <p class="c3"><span class="c4 c10">&nbsp; &nbsp; &nbsp;pin_to(1);</span></p> <p class="c3"><span class="c10 c4">+#endif</span></p> <p class="c3"><span class="c10 c4">&nbsp;</span></p> <p class="c3"><span class="c4 c24">&nbsp; &nbsp; &nbsp;//printf(&quot;setting timer\n&quot;);</span></p> <p class="c3"><span class="c10 c4">@@ -352,5 +354,5 @@</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;close(loop_root);</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;while (ts_is_in_future(spin_stop))</span></p> <p class="c3"><span class="c10 c4">- &nbsp; &nbsp; &nbsp;close(SYSCHK(dup(FAKE_RESURRECT_FD)));</span></p> <p class="c3"><span class="c10 c4">+ &nbsp; &nbsp; &nbsp;close(SYSCHK(dup(RESURRECT_FD)));</span></p> <p class="c3"><span class="c10 c4">&nbsp; &nbsp; &nbsp;while (ts_is_in_future(my_launch_ts)) /*spin*/;</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">With that patch, the race outcomes look like this on the Tiger Lake laptop:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7ABFgzZ7f8Mx5-JqYX4cjg23oXIbYr90vK32VO2evlWS2ADk3IM_Oh7AqguoJOEkF2A20aBhazVB5YqC7w5HgdmW73YqBhepK02B_EpBU0MSmbSIanUrFQwZizZAyF1cp2UNMMfG2xd_Z5byu2kqFejEuzWJ26piIR-0qY_uSGlfSfYmfq9SvLp9Z/s659/image2%284%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="This graph is a histogram of race outcomes depending on timing offset; it looks similar to the previous graphs, except that almost no race attempts succeed anymore." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7ABFgzZ7f8Mx5-JqYX4cjg23oXIbYr90vK32VO2evlWS2ADk3IM_Oh7AqguoJOEkF2A20aBhazVB5YqC7w5HgdmW73YqBhepK02B_EpBU0MSmbSIanUrFQwZizZAyF1cp2UNMMfG2xd_Z5byu2kqFejEuzWJ26piIR-0qY_uSGlfSfYmfq9SvLp9Z/s659/image2%284%29.png" style="max-height: 750; max-width: 600px;" title="This graph is a histogram of race outcomes depending on timing offset; it looks similar to the previous graphs, except that almost no race attempts succeed anymore." /></a></span></p><h1 class="c15" id="h.7bhmtfdoxeqe"><span class="c8">But wait, those graphs make no sense!</span></h1> <p class="c3"><span class="c1">If you&#39;ve been paying attention, you may have noticed that the timing graphs I&#39;ve been showing are really weird. If we were deterministically hitting the race in exactly the same way every time, the timing graph should look like this (looking just at the &quot;too-early&quot; and &quot;too-late&quot; cases for simplicity):</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6T1QCwcnTS3XMWs-pRes47xC-6ruTCFYUU9ZxoO-OYZs3BX3Je5ZxXllb-obFMJsGo9ueKu6y5e5CorRtrVbIHf4YIWAGrLV7Gz3wRS-QzF1UG3AxK__fRQ-PC6MkCnxH3dlKDG5ueJR8bITiorTkicLIhYJg910YC3fY5_m4sf64GYWtOnIPbKDE/s359/image6%283%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome suddenly drops from 100% probability to 0% probability, and a bit afterwards, the &quot;too late&quot; outcome jumps from 0% probability to 100%" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6T1QCwcnTS3XMWs-pRes47xC-6ruTCFYUU9ZxoO-OYZs3BX3Je5ZxXllb-obFMJsGo9ueKu6y5e5CorRtrVbIHf4YIWAGrLV7Gz3wRS-QzF1UG3AxK__fRQ-PC6MkCnxH3dlKDG5ueJR8bITiorTkicLIhYJg910YC3fY5_m4sf64GYWtOnIPbKDE/s359/image6%283%29.png" style="max-height: 750; max-width: 600px;" title="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome suddenly drops from 100% probability to 0% probability, and a bit afterwards, the &quot;too late&quot; outcome jumps from 0% probability to 100%" /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">Sure, maybe there is some microarchitectural state that is different between runs, causing timing variations - cache state, branch predictor state, frequency scaling, or something along those lines -, but a small number of discrete events that haven&#39;t been accounted for should be adding steps to the graph. (If you&#39;re mathematically inclined, you can model that as the result of a convolution of the ideal timing graph with the timing delay distributions of individual discrete events.) For two unaccounted events, that might look like this:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Y9DapfGb7NRtbz3j0aEQlA4SLZgk1ttbc7rS-0ZPbgerGJlqgk-N_Bna8AfcY8J2HBaHo2VJ0x-AR5n9lvivwdjmzKkITRIcypqYSBkOXB0DpavJ9dURyIfbA0u0D0KUOU1JD2pIH8KW2f8AQffaWluuQayXFq_8bleRLPfLZHmAkeLjFbJXxDVA/s359/image18.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome drops from 100% probability to 0% probability in multiple discrete steps, and overlapping that, the &quot;too late&quot; outcome goes up from 0% probability to 100% in multiple discrete steps" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Y9DapfGb7NRtbz3j0aEQlA4SLZgk1ttbc7rS-0ZPbgerGJlqgk-N_Bna8AfcY8J2HBaHo2VJ0x-AR5n9lvivwdjmzKkITRIcypqYSBkOXB0DpavJ9dURyIfbA0u0D0KUOU1JD2pIH8KW2f8AQffaWluuQayXFq_8bleRLPfLZHmAkeLjFbJXxDVA/s359/image18.png" style="max-height: 750; max-width: 600px;" title="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome drops from 100% probability to 0% probability in multiple discrete steps, and overlapping that, the &quot;too late&quot; outcome goes up from 0% probability to 100% in multiple discrete steps" /></a></span></p> <p class="c3"><span class="c1">But what the graphs are showing is more of a smooth, linear transition, like this:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV2n4po9tRvp9j-aHeINv6S3q7YVcSdHocobXnfLZQnkg8kQqh2wx-Q8-gBX6MNFLgAibZwVoUQwOAX-HDCK3aGCRt8rroQtcmw4Kyl9JNoVJb_ldqhj5sVW7CtlA-HFMyI45HPDYfWJD-ck2NpU2ZFFh35gVzmekcAdtlUv3fGVZ8p_Muec3rMbDJ/s359/image13.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome&#39;s share linearly drops while the &quot;too late&quot; outcome&#39;s share linearly rises" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV2n4po9tRvp9j-aHeINv6S3q7YVcSdHocobXnfLZQnkg8kQqh2wx-Q8-gBX6MNFLgAibZwVoUQwOAX-HDCK3aGCRt8rroQtcmw4Kyl9JNoVJb_ldqhj5sVW7CtlA-HFMyI45HPDYfWJD-ck2NpU2ZFFh35gVzmekcAdtlUv3fGVZ8p_Muec3rMbDJ/s359/image13.png" style="max-height: 750; max-width: 600px;" title="A sketch of a histogram of race outcomes where the &quot;too early&quot; outcome&#39;s share linearly drops while the &quot;too late&quot; outcome&#39;s share linearly rises" /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">And that seems to me like there&#39;s still something fundamentally wrong. Sure, if there was a sufficiently large number of discrete events mixed together, the curve would eventually just look like a smooth smear - but it seems unlikely to me that there is such a large number of somewhat-evenly distributed random discrete events. And sure, we do get a small amount of timing inaccuracy from sampling the clock in a spinloop, but that should be bounded to the execution time of that spinloop, and the timing smear is far too big for that.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>So it looks like there is a source of randomness that isn&#39;t a discrete event, but something that introduces a random amount of timing delay within some window. So I became suspicious of the hardware timer. The kernel is using </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span>, and the Intel SDM tells us that that thing is programmed with a TSC value, which makes it look as if the timer has very high granularity. But </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span>&nbsp;is a newer mode of the LAPIC timer, and the older LAPIC timer modes were instead programmed in units of the APIC timer frequency. According to the </span><span class="c11"><a class="c121" href="https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf#page=378">Intel SDM, Volume 3A</a></span><span>, section 10.5.4 &quot;APIC Timer&quot;, that is &quot;</span><span class="c16">the processor&rsquo;s bus clock or core crystal clock frequency (when TSC/core crystal clock ratio is enumerated in CPUID leaf 0x15) divided by the value specified in the divide configuration register</span><span>&quot;. This frequency is significantly lower than the TSC frequency. So perhaps </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span class="c1">&nbsp;is actually just a front-end to the same old APIC timer?</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">I tried to measure the difference between the programmed TSC value and when execution was actually interrupted (not when the interrupt handler starts running, but when the old execution context is interrupted - you can measure that if the interrupted execution context is just running RDTSC in a loop); that looks as follows:</span></p> <p class="c3"></p> <p class="c3"><span class="c1"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHjsv0uh_9jjLz2XaZ-i_TkybjySAwI8b6eQwNIwbayoTxJjdOu2hfPK9pBY4itBx_SY444RMWU9V2zf608IUUC8mO40y2hXVuK1EZwNRhWcntOwjHaHiel0ufzva2Ft9RGq86nDFohpKbamTnuz79vZ4WWJ1cMS0gbA8HUsygjshxfHmsXgIN_19y/s756/image11.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A graph showing noise. Delays from deadline TSC to last successful TSC read before interrupt look essentially random, in the range from around -130 to around 10." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHjsv0uh_9jjLz2XaZ-i_TkybjySAwI8b6eQwNIwbayoTxJjdOu2hfPK9pBY4itBx_SY444RMWU9V2zf608IUUC8mO40y2hXVuK1EZwNRhWcntOwjHaHiel0ufzva2Ft9RGq86nDFohpKbamTnuz79vZ4WWJ1cMS0gbA8HUsygjshxfHmsXgIN_19y/s756/image11.png" style="max-height: 750; max-width: 600px;" title="A graph showing noise. Delays from deadline TSC to last successful TSC read before interrupt look essentially random, in the range from around -130 to around 10." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>As you can see, the expiry of the hardware timer indeed adds a bunch of noise. The size of the timing difference is also very close to the crystal clock frequency - the TSC/core crystal clock ratio on this machine is 117. So I tried plotting the </span><span class="c6">absolute</span><span>&nbsp;TSC values at which execution was interrupted, </span><span class="c6">modulo the TSC / core crystal clock ratio</span><span class="c1">, and got this:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFD8Ox0LairxcMkyQuJrocYf7Cg33_jrE4AuhAPvbfMLm8jY8b_NFN9evyjlLXeLHhkfsji2bFNwE_--csZOKd_daBQJdJwKGbeKzLuRiXhGCze_zFELkzxl3dC69I7i4R9SQWgQG2Gv7QlLwdpAs7HRXNQJfCNB6nHh-_8LFvP03N71uMlw7nWoQB/s543/image9%282%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A graph showing a clear grouping around 0, roughly in the range -20 to 10, with some noise scattered over the rest of the graph." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFD8Ox0LairxcMkyQuJrocYf7Cg33_jrE4AuhAPvbfMLm8jY8b_NFN9evyjlLXeLHhkfsji2bFNwE_--csZOKd_daBQJdJwKGbeKzLuRiXhGCze_zFELkzxl3dC69I7i4R9SQWgQG2Gv7QlLwdpAs7HRXNQJfCNB6nHh-_8LFvP03N71uMlw7nWoQB/s543/image9%282%29.png" style="max-height: 750; max-width: 600px;" title="A graph showing a clear grouping around 0, roughly in the range -20 to 10, with some noise scattered over the rest of the graph." /></a></span></p> <p class="c3"><span>This confirms that </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span>&nbsp;is (apparently) an interface that </span><span class="c6">internally converts the specified TSC value into less granular bus clock / core crystal clock time</span><span class="c1">, at least on some Intel CPUs.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>But there&#39;s still something really weird here: The TSC values at which execution seems to be interrupted were at </span><span class="c6">negative</span><span>&nbsp;offsets relative to the programmed expiry time, as if the timeouts were rounded </span><span class="c6">down</span><span class="c1">&nbsp;to the less granular clock, or something along those lines. To get a better idea of how timer interrupts work, I measured on yet another system (an old Haswell CPU) with a patched kernel when execution is interrupted and when the interrupt handler starts executing relative to the programmed expiry time (and also plotted the difference between the two):</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxdpX4IgQCQuV9hIXWbVTSfVhyZ1LaNDfV6NUW0HRri_uI-OrUf0wuUK-SPm1gGO0NsqGPxN5WewnPIJ0FBaF7NSPfL0Nh7MQoGXk3YGgVPP2LwuTUz05Yy7DwKgUkxbQ0vJ5nWB0mlS2rPonc1p3wq11SKtzcpYzP_tnnWb65qW5Q4EGtrhz_7mwC/s928/image17.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A graph showing that the skid from programmed interrupt time to execution interruption is around -100 to -30 cycles, the skid to interrupt entry is around 360 to 420 cycles, and the time from execution interruption to interrupt entry has much less timing variance and is at around 440 cycles." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxdpX4IgQCQuV9hIXWbVTSfVhyZ1LaNDfV6NUW0HRri_uI-OrUf0wuUK-SPm1gGO0NsqGPxN5WewnPIJ0FBaF7NSPfL0Nh7MQoGXk3YGgVPP2LwuTUz05Yy7DwKgUkxbQ0vJ5nWB0mlS2rPonc1p3wq11SKtzcpYzP_tnnWb65qW5Q4EGtrhz_7mwC/s928/image17.png" style="max-height: 750px; max-width: 600px;" title="A graph showing that the skid from programmed interrupt time to execution interruption is around -100 to -30 cycles, the skid to interrupt entry is around 360 to 420 cycles, and the time from execution interruption to interrupt entry has much less timing variance and is at around 440 cycles." /></a></span></p> <p class="c3"><span class="c1">So it looks like the CPU starts handling timer interrupts a little bit before the programmed expiry time, but interrupt handler entry takes so long (~450 TSC clock cycles?) that by the time the CPU starts executing the interrupt handler, the timer expiry time has long passed.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>Anyway, the important bit for us is that when the CPU interrupts execution due to timer expiry, it&#39;s always at a LAPIC timer edge; and LAPIC timer edges happen when the TSC value is a multiple of the TSC/LAPIC clock ratio. An exploit that doesn&#39;t take that into account and wrongly assumes that </span><span class="c4">MSR_IA32_TSC_DEADLINE</span><span class="c1">&nbsp;has TSC granularity will have its timing smeared by one LAPIC clock period, which can be something like 40ns.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">The ~30% accuracy we could achieve with the existing PoC with the right timing is already not terrible; but if we control for the timer&#39;s weirdness, can we do better?</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>The problem is that we are effectively launching the race with two timers that behave differently: One timer based on calling </span><span class="c4">clock_gettime()</span><span class="c1">&nbsp;in a loop (which uses the high-resolution TSC to compute a time), the other a hardware timer based on the lower-resolution LAPIC clock. I see two options to fix this:</span></p> <p class="c0"><span class="c1"></span></p><ol class="c20 lst-kix_94mysn7p9quk-0 start" start="1"><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span class="c1">Try to ensure that the second timer is set at the start of a LAPIC clock period - that way, the second timer should hopefully behave exactly like the first (or have an additional fixed offset, but we can compensate for that).</span></li><li style="margin-left: 46pt;" class="c3 c14 li-bullet-0"><span class="c1">Shift the first timer&#39;s expiry time down according to the distance from the second timer to the previous LAPIC clock period.</span></li></ol> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>(One annoyance with this is that while we can grab information on how wall/monotonic time is calculated from TSC from the vvar mapping used by the vDSO, the clock is subject to minuscule additional corrections at every clock tick, which occur every 4ms on standard distro kernels (with </span><span class="c4">CONFIG_HZ=250</span><span class="c1">) as long as any core is running.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">I tried to see whether the timing graph would look nicer if I accounted for this LAPIC clock rounding and also used a custom kernel to cheat and control for possible skid introduced by the absolute-to-relative-and-back conversion of the expiry time (see further up), but that still didn&#39;t help all that much.</span></p><h1 class="c15" id="h.na1zwzioj0fl"><span class="c8">(No) surprise: clock speed matters</span></h1> <p class="c3"><span class="c1">Something I should&#39;ve thought about way earlier is that of course, clock speed matters. On newer Intel CPUs with P-states, the CPU is normally in control of its own frequency, and dynamically adjusts it as it sees fit; the OS just provides some hints.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>Linux has an interface that claims to tell you the &quot;current frequency&quot; of each CPU core in </span><span class="c4">/sys/devices/system/cpu/cpufreq/policy&lt;n&gt;/scaling_cur_freq</span><span class="c1">, but when I tried using that, I got a different &quot;frequency&quot; every time I read that file, which seemed suspicious.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>Looking at the implementation, it turns out that the value shown there is calculated in </span><span class="c4">arch_freq_get_on_cpu()</span><span>&nbsp;and </span><span>its</span><span>&nbsp;</span><span>callees</span><span>&nbsp;- the value is calculated on demand when the file is read, with results cached for around 10 milliseconds. The value is determined as the ratio between the deltas of </span><span class="c4">MSR_IA32_APERF</span><span>&nbsp;and </span><span class="c4">MSR_IA32_MPERF</span><span class="c1">&nbsp;between the last read and the current one. So if you have some tool that is polling these values every few seconds and wants to show average clock frequency over that time, it&#39;s probably a good way of doing things; but if you actually want the current clock frequency, it&#39;s not a good fit.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">I hacked a helper into my kernel that samples both MSRs twice in quick succession, and that gives much cleaner results. When I measure the clock speeds and timing offsets at which the race succeeds, the result looks like this (showing just two clock speeds; the Y axis is the number of race successes at the clock offset specified on the X axis and the frequency scaling specified by the color):</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgteZAepQr9rN7W0YShZIs3QBtqs-9FAJ9uW2wTTH9PSXSXxRMnCwOr9Ystgmi4EfD8ikHkCF0gHIV6s0A1rnnEAHEkl-7cBgLOgmTeDgep0PDO6ukZa6bzFqxCi48yQ46s5iZqSNvx2Dn05XCyVgYnLtqsuUvgyCeYxSRF_eOg4-xFDa7bWKzUD676/s666/image1%20%281%29%281%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A graph showing that the timing of successful race attempts depends on the CPU&#39;s performance setting - at 11/28 performance, most successful race attempts occur around clock offset -1200 (in TSC units), while at 14/28 performance, most successful race attempts occur around clock offset -1000." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgteZAepQr9rN7W0YShZIs3QBtqs-9FAJ9uW2wTTH9PSXSXxRMnCwOr9Ystgmi4EfD8ikHkCF0gHIV6s0A1rnnEAHEkl-7cBgLOgmTeDgep0PDO6ukZa6bzFqxCi48yQ46s5iZqSNvx2Dn05XCyVgYnLtqsuUvgyCeYxSRF_eOg4-xFDa7bWKzUD676/s666/image1%20%281%29%281%29.png" style="max-height: 750; max-width: 600px;" title="A graph showing that the timing of successful race attempts depends on the CPU&#39;s performance setting - at 11/28 performance, most successful race attempts occur around clock offset -1200 (in TSC units), while at 14/28 performance, most successful race attempts occur around clock offset -1000." /></a></span></p> <p class="c3"><span class="c1">So clearly, dynamic frequency scaling has a huge impact on the timing of the race - I guess that&#39;s to be expected, really.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>But even accounting for all this, the graph still looks kind of smooth, so clearly there is still something more that I&#39;m missing - oh well. I decided to stop experimenting with the race&#39;s timing at this point, since I didn&#39;t want to sink too much time into it. (Or perhaps I actually just stopped because I got distracted by newer and shinier things?)</span></p><h1 class="c15" id="h.ktiklcw2yj2x"><span>Causing a UAF</span></h1> <p class="c3"><span>Anyway, I could probably spend much more time trying to investigate the timing variations (and probably mostly bang my head against a wall because details of execution timing are really difficult to understand in detail, and to understand it completely, it might be necessary to use something like </span><span class="c11"><a class="c121" href="https://gamozolabs.github.io/metrology/2019/08/19/sushi_roll.html">Gamozo Labs&#39; &quot;Sushi Roll&quot;</a></span><span class="c1">&nbsp;and then go through every single instruction in detail and compare the observations to the internal architecture of the CPU). Let&#39;s not do that, and get back to how to actually exploit this bug!</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>To turn this bug into memory corruption, we have to abuse that the </span><span class="c4">recvmsg()</span><span>&nbsp;path assumes that SKBs on the receive queue are protected from deletion by the socket mutex while the GC actually deletes SKBs from the receive queue without touching the socket mutex. For that purpose, while the unix GC is running, we have to start a </span><span class="c4">recvmsg()</span><span>&nbsp;call that looks up the victim SKB, block until the unix GC has freed the SKB, and then let </span><span class="c4">recvmsg()</span><span>&nbsp;continue operating on the freed SKB. This is fairly straightforward - while it is a race, we can easily slow down </span><span class="c4">unix_gc()</span><span class="c1">&nbsp;for multiple milliseconds by creating lots of sockets that are not directly referenced from the FD table and have many tiny SKBs queued up - here&#39;s a graph showing the unix GC execution time on my laptop, depending on the number of queued SKBs that the GC has to scan through:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBiMkb5gF1P8Epsw3zNsmjcesB53u-1TWswpWkW-YPl9vBXPF116eQG2v-wx7Kv7Wvb8fcUCoxU5O6VJ5854OdJWXKoSzPxBvBp0atr1HLiSyB1SqvqBTxba28bREm610sKCoFN7kA2ABwWNcq_vbmkPnH7oKGIwRV8gYcxHQ2JoWPf0eIYmO6y1Rq/s833/image15.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A graph showing the time spent per GC run depending on the number of queued SKBs. The relationship is roughly linear." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBiMkb5gF1P8Epsw3zNsmjcesB53u-1TWswpWkW-YPl9vBXPF116eQG2v-wx7Kv7Wvb8fcUCoxU5O6VJ5854OdJWXKoSzPxBvBp0atr1HLiSyB1SqvqBTxba28bREm610sKCoFN7kA2ABwWNcq_vbmkPnH7oKGIwRV8gYcxHQ2JoWPf0eIYmO6y1Rq/s833/image15.png" style="max-height: 750; max-width: 600px;" title="A graph showing the time spent per GC run depending on the number of queued SKBs. The relationship is roughly linear." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>To turn this into a UAF, it&#39;s also necessary to get past the following check near the end of </span><span class="c4">unix_gc()</span><span class="c1">:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; &nbsp; &nbsp;/* All candidates should have been detached by now. */</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; &nbsp; &nbsp; BUG_ON(!list_empty(&amp;gc_candidates));</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c4">gc_candidates</span><span>&nbsp;is a list that previously contained all sockets that were deemed to be unreachable by the GC. Then, the GC attempted to free all those sockets by eliminating their mutual references. If we manage to keep a reference to one of the sockets that the GC thought was going away, the GC detects that with the </span><span class="c4">BUG_ON()</span><span class="c1">.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>But we don&#39;t actually need the victim SKB to reference a socket that the GC thinks is going away; in </span><span class="c4">scan_inflight()</span><span>, the GC targets any SKB with a socket that is marked </span><span class="c4">UNIX_GC_CANDIDATE</span><span>, meaning it just had to be </span><span class="c6">a candidate</span><span>&nbsp;for being scanned by the GC. So by making the victim SKB hold a reference to a socket that is not directly referenced from a file descriptor table, but is indirectly referenced by a file descriptor table through another socket, we can ensure that the </span><span class="c4">BUG_ON()</span><span class="c1">&nbsp;won&#39;t trigger.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>I extended </span><span class="c11"><a class="c121" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2247#c6">my reproducer</a></span><span>&nbsp;with this trick and some userfaultfd trickery to make </span><span class="c4">recv()</span><span>&nbsp;run with the right timing. Nowadays you don&#39;t necessarily get full access to userfaultfd as a normal user, but since I&#39;m just trying to show the concept, and there are alternatives to userfaultfd</span><span>&nbsp;(using FUSE or just slow disk access</span><span>)</span><span>,</span><span class="c1">&nbsp;that&#39;s good enough for this blogpost.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>When a normal distro kernel is running normally, the UAF reproducer&#39;s UAF accesses won&#39;t actually be noticeable; but if you add the kernel command line flag </span><span class="c4">slub_debug=FP</span><span class="c1">&nbsp;(to enable SLUB&#39;s poisoning and sanity checks), the reproducer quickly crashes twice, first with a poison dereference and then a poison overwrite detection, showing that one byte of the poison was incremented:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c2">general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b6b: 0000 [#1] SMP NOPTI</span></p> <p class="c3"><span class="c2">CPU: 1 PID: 2655 Comm: hardirq_loop Not tainted 5.10.0-9-amd64 #1 Debian 5.10.70-1</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">RIP: 0010:unix_stream_read_generic+0x72b/0x870</span></p> <p class="c3"><span class="c2">Code: fe ff ff 31 ff e8 85 87 91 ff e9 a5 fe ff ff 45 01 77 44 8b 83 80 01 00 00 85 c0 0f 89 10 01 00 00 49 8b 47 38 48 85 c0 74 23 &lt;0f&gt; bf 00 66 85 c0 0f 85 20 01 00 00 4c 89 fe 48 8d 7c 24 58 44 89</span></p> <p class="c3"><span class="c2">RSP: 0018:ffffb789027f7cf0 EFLAGS: 00010202</span></p> <p class="c3"><span class="c2">RAX: 6b6b6b6b6b6b6b6b RBX: ffff982d1d897b40 RCX: 0000000000000000</span></p> <p class="c3"><span class="c2">RDX: 6a0fe1820359dce8 RSI: ffffffffa81f9ba0 RDI: 0000000000000246</span></p> <p class="c3"><span class="c2">RBP: ffff982d1d897ea8 R08: 0000000000000000 R09: 0000000000000000</span></p> <p class="c3"><span class="c2">R10: 0000000000000000 R11: ffff982d2645c900 R12: ffffb789027f7dd0</span></p> <p class="c3"><span class="c2">R13: ffff982d1d897c10 R14: 0000000000000001 R15: ffff982d3390e000</span></p> <p class="c3"><span class="c2">FS: &nbsp;00007f547209d740(0000) GS:ffff98309fa40000(0000) knlGS:0000000000000000</span></p> <p class="c3"><span class="c2">CS: &nbsp;0010 DS: 0000 ES: 0000 CR0: 0000000080050033</span></p> <p class="c3"><span class="c2">CR2: 00007f54722cd000 CR3: 00000001b61f4002 CR4: 0000000000770ee0</span></p> <p class="c3"><span class="c2">PKRU: 55555554</span></p> <p class="c3"><span class="c2">Call Trace:</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;unix_stream_recvmsg+0x53/0x70</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;__sys_recvfrom+0x166/0x180</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;__x64_sys_recvfrom+0x25/0x30</span></p> <p class="c3"><span class="c2">&nbsp;do_syscall_64+0x33/0x80</span></p> <p class="c3"><span class="c2">&nbsp;entry_SYSCALL_64_after_hwframe+0x44/0xa9</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">---[ end trace 39a81eb3a52e239c ]---</span></p> <p class="c3"><span class="c2">=============================================================================</span></p> <p class="c3"><span class="c2">BUG skbuff_head_cache (Tainted: G &nbsp; &nbsp; &nbsp;D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;): Poison overwritten</span></p> <p class="c3"><span class="c2">-----------------------------------------------------------------------------</span></p> <p class="c0"><span class="c2"></span></p> <p class="c3"><span class="c2">INFO: 0x00000000d7142451-0x00000000d7142451 @offset=68. First byte 0x6c instead of 0x6b</span></p> <p class="c3"><span class="c2">INFO: Slab 0x000000002f95c13c objects=32 used=32 fp=0x0000000000000000 flags=0x17ffffc0010200</span></p> <p class="c3"><span class="c2">INFO: Object 0x00000000ef9c59c8 @offset=0 fp=0x00000000100a3918</span></p> <p class="c0"><span class="c2"></span></p> <p class="c3"><span class="c2">Object &nbsp; 00000000ef9c59c8: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 0000000097454be8: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 0000000035f1d791: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 00000000af71b907: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c4 c9">Object &nbsp; 000000000d2d371e: 6b 6b 6b 6b </span><span class="c6 c18 c9 c25">6c</span><span class="c2">&nbsp;6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkklkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 0000000000744b35: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 00000000794f2935: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 000000006dc06746: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 000000005fb18682: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 0000000072eb8dd2: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 00000000b5b572a9: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 0000000085d6850b: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 000000006346150b: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b &nbsp;kkkkkkkkkkkkkkkk</span></p> <p class="c3"><span class="c2">Object &nbsp; 000000000ddd1ced: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 &nbsp;kkkkkkkkkkkkkkk.</span></p> <p class="c3"><span class="c2">Padding &nbsp;00000000e00889a7: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a &nbsp;ZZZZZZZZZZZZZZZZ</span></p> <p class="c3"><span class="c2">Padding &nbsp;00000000d190015f: 5a 5a 5a 5a 5a 5a 5a 5a &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ZZZZZZZZ</span></p> <p class="c3"><span class="c2">CPU: 7 PID: 1641 Comm: gnome-shell Tainted: G &nbsp; &nbsp;B D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 5.10.0-9-amd64 #1 Debian 5.10.70-1</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">Call Trace:</span></p> <p class="c3"><span class="c2">&nbsp;dump_stack+0x6b/0x83</span></p> <p class="c3"><span class="c2">&nbsp;check_bytes_and_report.cold+0x79/0x9a</span></p> <p class="c3"><span class="c2">&nbsp;check_object+0x217/0x260</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;alloc_debug_processing+0xd5/0x130</span></p> <p class="c3"><span class="c2">&nbsp;___slab_alloc+0x511/0x570</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;__slab_alloc+0x1c/0x30</span></p> <p class="c3"><span class="c2">&nbsp;kmem_cache_alloc_node+0x1f3/0x210</span></p> <p class="c3"><span class="c2">&nbsp;__alloc_skb+0x46/0x1f0</span></p> <p class="c3"><span class="c2">&nbsp;alloc_skb_with_frags+0x4d/0x1b0</span></p> <p class="c3"><span class="c2">&nbsp;sock_alloc_send_pskb+0x1f3/0x220</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;unix_stream_sendmsg+0x268/0x4d0</span></p> <p class="c3"><span class="c2">&nbsp;sock_sendmsg+0x5e/0x60</span></p> <p class="c3"><span class="c2">&nbsp;____sys_sendmsg+0x22e/0x270</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;___sys_sendmsg+0x75/0xb0</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">&nbsp;__sys_sendmsg+0x59/0xa0</span></p> <p class="c3"><span class="c2">&nbsp;do_syscall_64+0x33/0x80</span></p> <p class="c3"><span class="c2">&nbsp;entry_SYSCALL_64_after_hwframe+0x44/0xa9</span></p> <p class="c3"><span class="c2">[...]</span></p> <p class="c3"><span class="c2">FIX skbuff_head_cache: Restoring 0x00000000d7142451-0x00000000d7142451=0x6b</span></p> <p class="c0"><span class="c2"></span></p> <p class="c3"><span class="c2">FIX skbuff_head_cache: Marking all objects used</span></p> <p class="c3"><span class="c2">RIP: 0010:unix_stream_read_generic+0x72b/0x870</span></p> <p class="c3"><span class="c2">Code: fe ff ff 31 ff e8 85 87 91 ff e9 a5 fe ff ff 45 01 77 44 8b 83 80 01 00 00 85 c0 0f 89 10 01 00 00 49 8b 47 38 48 85 c0 74 23 &lt;0f&gt; bf 00 66 85 c0 0f 85 20 01 00 00 4c 89 fe 48 8d 7c 24 58 44 89</span></p> <p class="c3"><span class="c2">RSP: 0018:ffffb789027f7cf0 EFLAGS: 00010202</span></p> <p class="c3"><span class="c2">RAX: 6b6b6b6b6b6b6b6b RBX: ffff982d1d897b40 RCX: 0000000000000000</span></p> <p class="c3"><span class="c2">RDX: 6a0fe1820359dce8 RSI: ffffffffa81f9ba0 RDI: 0000000000000246</span></p> <p class="c3"><span class="c2">RBP: ffff982d1d897ea8 R08: 0000000000000000 R09: 0000000000000000</span></p> <p class="c3"><span class="c2">R10: 0000000000000000 R11: ffff982d2645c900 R12: ffffb789027f7dd0</span></p> <p class="c3"><span class="c2">R13: ffff982d1d897c10 R14: 0000000000000001 R15: ffff982d3390e000</span></p> <p class="c3"><span class="c2">FS: &nbsp;00007f547209d740(0000) GS:ffff98309fa40000(0000) knlGS:0000000000000000</span></p> <p class="c3"><span class="c2">CS: &nbsp;0010 DS: 0000 ES: 0000 CR0: 0000000080050033</span></p> <p class="c3"><span class="c2">CR2: 00007f54722cd000 CR3: 00000001b61f4002 CR4: 0000000000770ee0</span></p> <p class="c3"><span class="c4 c9">PKRU: 55555554</span></p><h1 class="c15" id="h.pjkt5u3x5bd1"><span>Conclusion(s)</span></h1> <p class="c3"><span class="c1">Hitting a race can become easier if, instead of racing two threads against each other, you race one thread against a hardware timer to create a gigantic timing window for the other thread. Hence the title! On the other hand, it introduces extra complexity because now you have to think about how timers actually work, and turns out, time is a complicated concept...</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>This shows how at least some really tight races can still be hit and </span><span>we should treat them</span><span>&nbsp;as security bugs, even if it seems like they&#39;d be very hard to hit at first glance.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">Also, precisely timing races is hard, and the details of how long it actually takes the CPU to get from one point to another are mysterious. (As not only exploit writers know, but also anyone who&#39;s ever wanted to benchmark a performance-relevant change...)</span></p><h1 class="c15" id="h.adfta2d6xkv8"><span>Appendix: How impatient are interrupts?</span></h1> <p class="c3"><span class="c1">I did also play around with this stuff on arm64 a bit, and I was wondering: At what points do interrupts actually get delivered? Does an incoming interrupt force the CPU to drop everything immediately, or do inflight operations finish first? This gets particularly interesting on phones that contain two or three different types of CPUs mixed together.</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>On a Pixel 4 (which has 4 slow in-order cores, 3 fast cores, and 1 faster core), I tried firing an interval timer at 100Hz (using </span><span class="c4">timer_create()</span><span>), with a signal handler that logs the </span><span class="c4">PC</span><span class="c1">&nbsp;register, while running this loop:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c7 c4">&nbsp; 400680:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 400684:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 400688:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9ac20820 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;udiv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x1, x2</span></p> <p class="c3"><span class="c7 c4">&nbsp; 40068c:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91006800 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1a</span></p> <p class="c3"><span class="c7 c4">&nbsp; 400690:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000400 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 400694:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 400698:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 40069c:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9ac20820 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;udiv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x1, x2</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91006800 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1a</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006ac:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000400 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006bc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006c0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;17fffff0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;400680 &lt;main+0xe0&gt;</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">The logged interrupt PCs had the following distribution on a slow in-order core:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh79bjjrDomCArX8YgAAX47nW6vfXNUUi0_WFbvS4ivPBjbOugqTtH7OZ5ZdRU9RYiHBfKiyUTEJRZCBn_mFN_MP9-v0krZKVaEoVq6mGyqkqNjx0H-2YULQe5ONPIyVrjJ7XBXY0y2UM7IkZfUHUJiVR0XYGYcZrLmDGeAecRkHStfXAmSzGmsT20D/s1252/image3%286%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A histogram of PC register values, where most instructions in the loop have roughly equal frequency, the instructions after udiv instructions have twice the frequency, and two other instructions have zero frequency." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh79bjjrDomCArX8YgAAX47nW6vfXNUUi0_WFbvS4ivPBjbOugqTtH7OZ5ZdRU9RYiHBfKiyUTEJRZCBn_mFN_MP9-v0krZKVaEoVq6mGyqkqNjx0H-2YULQe5ONPIyVrjJ7XBXY0y2UM7IkZfUHUJiVR0XYGYcZrLmDGeAecRkHStfXAmSzGmsT20D/s1252/image3%286%29.png" style="max-height: 750; max-width: 600px;" title="A histogram of PC register values, where most instructions in the loop have roughly equal frequency, the instructions after udiv instructions have twice the frequency, and two other instructions have zero frequency." /></a></span></p> <p class="c3"><span class="c1">and this distribution on a fast out-of-order core:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD-BHvjdNFWdHXsha29a8w8Yo62kCoCH7QlHdXcQQHNECyyTM2BdG2Yxy2DL5UBT-XRMVSWeQbYaUBgpbKeBeQPU7AXTyS9J9twZ7eC5TyvBSNtU1p6J5ulb8yaWVAH359IfKzzsI9g8z1hgfcEwhiP3k1LFo_G-dsXpXFLF4A0cyiRTs7yrPpa8ac/s1252/image7%283%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A histogram of PC register values, where the first instruction of the loop has very high frequency, the following 4 instructions have near-zero frequency, and the following instructions have low frequencies" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD-BHvjdNFWdHXsha29a8w8Yo62kCoCH7QlHdXcQQHNECyyTM2BdG2Yxy2DL5UBT-XRMVSWeQbYaUBgpbKeBeQPU7AXTyS9J9twZ7eC5TyvBSNtU1p6J5ulb8yaWVAH359IfKzzsI9g8z1hgfcEwhiP3k1LFo_G-dsXpXFLF4A0cyiRTs7yrPpa8ac/s1252/image7%283%29.png" style="max-height: 750; max-width: 600px;" title="A histogram of PC register values, where the first instruction of the loop has very high frequency, the following 4 instructions have near-zero frequency, and the following instructions have low frequencies" /></a></span></p> <p class="c3"><span>As always, out-of-order (OOO) cores make everything weird, and the start of the loop seems to somehow &quot;provide cover&quot; for the following instructions; but on the in-order core, we can see that more interrupts arrive </span><span class="c6">after</span><span class="c1">&nbsp;the slow udiv instructions. So apparently, when one of those is executing while an interrupt arrives, it continues executing and doesn&#39;t get aborted somehow?</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>With the following loop, which has a </span><span class="c4">LDR</span><span class="c1">&nbsp;instruction mixed in that accesses a memory location that is constantly being modified by another thread:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006a8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9ac20820 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;udiv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x1, x2</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006ac:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91006800 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1a</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000400 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006b8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006bc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006c0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006c4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9ac20820 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;udiv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x1, x2</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006c8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91006800 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1a</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006cc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000400 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0, x0, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006d0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006d4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f9400061 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, [x3]</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006d8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006dc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000442 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x2, x2, #0x1</span></p> <p class="c3"><span class="c4 c7">&nbsp; 4006e0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;91000421 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x1, x1, #0x1</span></p> <p class="c3"><span class="c7 c4">&nbsp; 4006e4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;17ffffef &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4006a0 &lt;main+0x100&gt;</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">the cache-missing loads obviously have a large influence on the timing. On the in-order core:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd2RdxeFWLW-EaNH0BmFh0bR7cTAxUQWltgd1v4192uhqNBPjlZOfcAjPF11Bh4bFpMN0Qu2iMTSiYwz8nzNwvAEPfopJDb9ZP4PG-3FMTULgznbXTsX9y5PMie65S3L4EGX1tQ_7Be53ettw-tmsP8lhP8G_sDGIeq5DwC9dqN8Y7oAc3SNBz6Hrn/s1252/image4%285%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A histogram of interrupt instruction pointers, showing that most interrupts are delivered with PC pointing to the instruction after the high-latency load instruction." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd2RdxeFWLW-EaNH0BmFh0bR7cTAxUQWltgd1v4192uhqNBPjlZOfcAjPF11Bh4bFpMN0Qu2iMTSiYwz8nzNwvAEPfopJDb9ZP4PG-3FMTULgznbXTsX9y5PMie65S3L4EGX1tQ_7Be53ettw-tmsP8lhP8G_sDGIeq5DwC9dqN8Y7oAc3SNBz6Hrn/s1252/image4%285%29.png" style="max-height: 750; max-width: 600px;" title="A histogram of interrupt instruction pointers, showing that most interrupts are delivered with PC pointing to the instruction after the high-latency load instruction." /></a></span></p> <p class="c3"><span class="c1">On the OOO core:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihTJNVw4C-vyBUdzEIFRX4tkgjk9rcnZMnn6JEAUQ3LP-NZexmmNFvL1T8LyHtIPmAbMso2arHvB4eBjB81HHxWZgO-wfQpN_Afs48fmZvEVzysO2ov7kawLQFfjq1dJvD4t8oi_5ZnYklJx5MP3jbaDrMrtXeaQlIW6tjsRoYcywfbHt_3o-pd8Yc/s1297/image20.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A similar histogram as the previous one, except that an even larger fraction of interrupt PCs are after the high-latency load instruction." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihTJNVw4C-vyBUdzEIFRX4tkgjk9rcnZMnn6JEAUQ3LP-NZexmmNFvL1T8LyHtIPmAbMso2arHvB4eBjB81HHxWZgO-wfQpN_Afs48fmZvEVzysO2ov7kawLQFfjq1dJvD4t8oi_5ZnYklJx5MP3jbaDrMrtXeaQlIW6tjsRoYcywfbHt_3o-pd8Yc/s1297/image20.png" style="max-height: 750; max-width: 600px;" title="A similar histogram as the previous one, except that an even larger fraction of interrupt PCs are after the high-latency load instruction." /></a></span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span>What is interesting to me here is that the timer interrupts seem to again arrive </span><span class="c6">after</span><span class="c1">&nbsp;the slow load - implying that if an interrupt arrives while a slow memory access is in progress, the interrupt handler may not get to execute until the memory access has finished? (Unless maybe on the OOO core the interrupt handler can start speculating already? I wouldn&#39;t really expect that, but could imagine it.)</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">On an X86 Skylake CPU, we can do a similar test:</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11b8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c3 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rbx</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11bc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c0 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11c0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 01 d8 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;%rbx,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11c3:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c3 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rbx</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11c7:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c0 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11cb:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 01 d8 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;%rbx,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11ce:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 03 02 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;(%rdx),%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11d1:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c0 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11d5:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c3 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rbx</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11d9:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 01 d8 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;%rbx,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11dc:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c3 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rbx</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11e0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 83 c0 01 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;$0x1,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11e4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48 01 d8 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add &nbsp; &nbsp;%rbx,%rax</span></p> <p class="c3"><span class="c7 c4">&nbsp; &nbsp; 11e7:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eb cf &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp &nbsp; &nbsp;11b8 &lt;main+0xf8&gt;</span></p> <p class="c0"><span class="c1"></span></p> <p class="c3"><span class="c1">with a similar result:</span></p> <p class="c3"></p> <p class="c3"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifbDV2Uu_gXT_0s8hOyj3wImUB1O17c9SHpIEkkmAEES8GbgyHlSaPjbESG6SBIbdPPp3sTDao1A_SAcs4CfmgljAY3wa8KKeIQ4Lsg_L0nXFQY-qWL_-vDGyQnSN3vvt2YUmWf2-5WS81VqprDiz5YMq94_k9r1iVRuArhgWcfNBHBU-HtzJrWmX8/s540/image14.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="A histogram of interrupt instruction pointers, showing that almost all interrupts were delivered with RIP pointing to the instruction after the high-latency load." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifbDV2Uu_gXT_0s8hOyj3wImUB1O17c9SHpIEkkmAEES8GbgyHlSaPjbESG6SBIbdPPp3sTDao1A_SAcs4CfmgljAY3wa8KKeIQ4Lsg_L0nXFQY-qWL_-vDGyQnSN3vvt2YUmWf2-5WS81VqprDiz5YMq94_k9r1iVRuArhgWcfNBHBU-HtzJrWmX8/s540/image14.png" style="max-height: 750; max-width: 600px;" title="A histogram of interrupt instruction pointers, showing that almost all interrupts were delivered with RIP pointing to the instruction after the high-latency load." /></a></span></p> <p class="c3"><span>This means that </span><span class="c6">if</span><span>&nbsp;the first access to the file terminated our race window (which is not the case), we probably wouldn&#39;t be able to win the race by making the access to the file slow - instead we&#39;d have to slow down one of the operations before that. (But note that I have only tested simple loads, not stores or read-modify-write operations here.)</span></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <span itemprop='name'>Anonymous</span> </span> </span> <span class='post-timestamp'> at <meta content='https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2022-03-24T13:51:00-07:00'>1:51&#8239;PM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-145400864'> <a href='https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=2892029424446991022&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> <a class='goog-inline-block share-button sb-email' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=2892029424446991022&target=email' target='_blank' title='Email This'><span class='share-button-link-text'>Email This</span></a><a class='goog-inline-block share-button sb-blog' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=2892029424446991022&target=blog' onclick='window.open(this.href, "_blank", "height=270,width=475"); return false;' target='_blank' title='BlogThis!'><span class='share-button-link-text'>BlogThis!</span></a><a class='goog-inline-block share-button sb-twitter' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=2892029424446991022&target=twitter' target='_blank' title='Share to X'><span class='share-button-link-text'>Share to X</span></a><a class='goog-inline-block share-button sb-facebook' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=2892029424446991022&target=facebook' onclick='window.open(this.href, "_blank", "height=430,width=640"); return false;' target='_blank' title='Share to Facebook'><span class='share-button-link-text'>Share to Facebook</span></a><a class='goog-inline-block share-button sb-pinterest' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=2892029424446991022&target=pinterest' target='_blank' title='Share to Pinterest'><span class='share-button-link-text'>Share to Pinterest</span></a> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> </span> </div> </div> </div> <div class='comments' id='comments'> <a name='comments'></a> <h4>No comments:</h4> <div id='Blog1_comments-block-wrapper'> <dl class='avatar-comment-indent' id='comments-block'> </dl> </div> <p class='comment-footer'> <div class='comment-form'> <a name='comment-form'></a> <h4 id='comment-post-message'>Post a Comment</h4> <p> </p> <a href='https://www.blogger.com/comment/frame/4838136820032157985?po=2892029424446991022&hl=en' id='comment-editor-src'></a> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410px' id='comment-editor' name='comment-editor' src='' width='100%'></iframe> <script src='https://www.blogger.com/static/v1/jsbin/3061944402-comment_from_post_iframe.js' type='text/javascript'></script> <script type='text/java '> BLOG_CMT_createIframe('https://www.blogger.com/rpc_relay.html'); </script> </div> </p> </div> </div> </div></div> </div> <div class='blog-pager' id='blog-pager'> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' href='https://googleprojectzero.blogspot.com/2022/03/forcedentry-sandbox-escape.html' id='Blog1_blog-pager-newer-link' title='Newer Post'>Newer Post</a> </span> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='https://googleprojectzero.blogspot.com/2022/02/a-walk-through-project-zero-metrics.html' id='Blog1_blog-pager-older-link' title='Older Post'>Older Post</a> </span> <a class='home-link' href='https://googleprojectzero.blogspot.com/'>Home</a> </div> <div class='clear'></div> <div class='post-feeds'> <div class='feed-links'> Subscribe to: <a class='feed-link' href='https://googleprojectzero.blogspot.com/feeds/2892029424446991022/comments/default' target='_blank' type='application/atom+xml'>Post Comments (Atom)</a> </div> </div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget BlogSearch' data-version='1' id='BlogSearch1'> <h2 class='title'>Search This Blog</h2> <div class='widget-content'> <div id='BlogSearch1_form'> <form action='https://googleprojectzero.blogspot.com/search' class='gsc-search-box' target='_top'> <table cellpadding='0' cellspacing='0' class='gsc-search-box'> <tbody> <tr> <td class='gsc-input'> <input autocomplete='off' class='gsc-input' name='q' size='10' title='search' type='text' value=''/> </td> <td class='gsc-search-button'> <input class='gsc-search-button' title='search' type='submit' value='Search'/> </td> </tr> </tbody> </table> </form> </div> </div> <div class='clear'></div> </div><div class='widget PageList' data-version='1' id='PageList1'> <h2>Pages</h2> <div class='widget-content'> <ul> <li> <a href='https://googleprojectzero.blogspot.com/p/about-project-zero.html'>About Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/working-at-project-zero.html'>Working at Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/0day.html'>0day "In the Wild"</a> </li> <li> <a href='https://googleprojectzero.github.io/0days-in-the-wild/rca.html'>0day Exploit Root Cause Analyses</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html'>Vulnerability Disclosure FAQ</a> </li> </ul> <div class='clear'></div> </div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Archives</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2025/'> 2025 </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2025/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(12)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/12/'> December </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> &#9660;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/'> 2022 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> &#9660;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='posts'> <li><a href='https://googleprojectzero.blogspot.com/2022/03/forcedentry-sandbox-escape.html'>FORCEDENTRY: Sandbox Escape</a></li> <li><a href='https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html'>Racing against the clock -- hitting a tiny kernel ...</a></li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(24)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/01/'> January </a> <span class='post-count' dir='ltr'>(10)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/'> 2020 </a> <span class='post-count' dir='ltr'>(36)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/08/'> August </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/07/'> July </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/01/'> January </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/'> 2019 </a> <span class='post-count' dir='ltr'>(27)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/08/'> August </a> <span class='post-count' dir='ltr'>(11)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(22)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/12/'> December </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/10/'> October </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(19)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/04/'> April </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/'> 2016 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(33)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/08/'> August </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/07/'> July </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/06/'> June </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> &#9658;&#160; </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> </div> </div> <script type='text/javascript'> //<![CDATA[ (function(){ let archive_list = document.getElementById('ArchiveList'); if (archive_list == null) return; let cur_year = archive_list.querySelector('.post-count-link').innerText.trim() - 0; let last_year = 2014; let elements = []; const MONTHS = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','); let parent = document.getElementById('ArchiveList'); while (parent.childNodes.length) parent.removeChild(parent.childNodes[0]); function fetch_next_year() { let url = 'https://googleprojectzero.blogspot.com/?action=getTitles&widgetId=BlogArchive1&widgetType=BlogArchive&responseType=js&path=https%3A%2F%2Fgoogleprojectzero.blogspot.com%2F'+cur_year; fetch(url).then(resp => { if (!resp.ok) { console.log('http error'); return; } resp.text().then(text => { let scope = { _WidgetManager: { _HandleControllerResult: (name, method, results) => { elements.push(document.createElement('hr')); let year_header = document.createElement('div'); year_header.appendChild(document.createTextNode(cur_year)); year_header.style.fontSize = 'large'; elements.push(year_header); let list = document.createElement('ul'); elements.push(list); for (let obj of results.posts) { let link_parts = obj.url.split('/'); let year = link_parts[3]; let month = link_parts[4]; let el = document.createElement(/*'div'*/'li'); el.style.listStyleType = 'square'; el.style.listStylePosition = 'inside'; let link = document.createElement('a'); el.appendChild(link); link.appendChild(document.createTextNode(obj.title)); link.href = obj.url; let date_trailer = document.createElement('span'); el.appendChild(date_trailer); //date_trailer.appendChild(document.createTextNode(' ('+year+'-'+month+')')); date_trailer.appendChild(document.createTextNode(' ('+MONTHS[parseInt(month, 10)]+')')); //date_trailer.style.textAlign = 'right'; //elements.push(el); list.appendChild(el); } } } }; with (scope) { eval(text); } if (cur_year == last_year) { finish(); } else { cur_year--; fetch_next_year(); } }); }); } fetch_next_year(); function finish() { for (let obj of elements) { parent.appendChild(obj); } console.log(elements); } })(); //]]> </script> <div class='clear'></div> </div> </div></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-1'></div> </td> <td class='columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-2'></div> </td> </tr> </tbody> </table> <div class='sidebar no-items section' id='sidebar-right-3'></div> </aside> </div> </div> </div> <div style='clear: both'></div> <!-- columns --> </div> <!-- main --> </div> </div> <div class='main-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <footer> <div class='footer-outer'> <div class='footer-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left footer-fauxborder-left'> <div class='fauxborder-right footer-fauxborder-right'></div> <div class='region-inner footer-inner'> <div class='foot no-items section' id='footer-1'></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='foot no-items section' id='footer-2-1'></div> </td> <td class='columns-cell'> <div class='foot no-items section' id='footer-2-2'></div> </td> </tr> </tbody> </table> <!-- outside of the include in order to lock Attribution widget --> <div class='foot section' id='footer-3' name='Footer'><div class='widget Attribution' data-version='1' id='Attribution1'> <div class='widget-content' style='text-align: center;'> Powered by <a href='https://www.blogger.com' target='_blank'>Blogger</a>. </div> <div class='clear'></div> </div></div> </div> </div> <div class='footer-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </footer> <!-- content --> </div> </div> <div class='content-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <script type='text/javascript'> window.setTimeout(function() { document.body.className = document.body.className.replace('loading', ''); }, 10); </script> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/688949419-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY7aZ6BHFdQVIkHrEFaJhW-50nyy8Q:1739851407691';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d4838136820032157985','//googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html','4838136820032157985'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '4838136820032157985', 'title': 'Project Zero', 'url': 'https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html', 'canonicalUrl': 'https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html', 'homepageUrl': 'https://googleprojectzero.blogspot.com/', 'searchUrl': 'https://googleprojectzero.blogspot.com/search', 'canonicalHomepageUrl': 'https://googleprojectzero.blogspot.com/', 'blogspotFaviconUrl': 'https://googleprojectzero.blogspot.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': false, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-240546891-1', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Project Zero - RSS\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/4838136820032157985/posts/default\x22 /\x3e\n\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/2892029424446991022/comments/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/f6e0cc369f0f1a05', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'item', 'postId': '2892029424446991022', 'postImageThumbnailUrl': 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s72-c/image19.png', 'postImageUrl': 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png', 'pageName': 'Racing against the clock -- hitting a tiny kernel race window', 'pageTitle': 'Project Zero: Racing against the clock -- hitting a tiny kernel race window'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Racing against the clock -- hitting a tiny kernel race window', 'description': ' TL;DR: How to make a tiny kernel race window really large even on kernels without CONFIG_PREEMPT : use \xa0a cache miss to widen the race w...', 'featuredImage': 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKajtxfGAZhXanWS7kcvJozqQU2jozVQGIZdgex6_IRhp9jWHiJiL2KQddxH7Ma02d5sqhnXROh1PacqksZu3sA7-zjwh3ES8E8fQ7Jza8lu2claui1T9xYN3wrgv4J9yMFxbrUIAGiouvy4GGq-J69qROIjIITzvNpMQtJpljanHvTgd4xgrhZfRt/s641/image19.png', 'url': 'https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html', 'type': 'item', 'isSingleItem': true, 'isMultipleItems': false, 'isError': false, 'isPage': false, 'isPost': true, 'isHomepage': false, 'isArchive': false, 'isLabelSearch': false, 'postId': 2892029424446991022}}]); _WidgetManager._RegisterWidget('_NavbarView', new _WidgetInfo('Navbar1', 'navbar', document.getElementById('Navbar1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/1360229384-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch1', 'sidebar-right-1', document.getElementById('BlogSearch1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_PageListView', new _WidgetInfo('PageList1', 'sidebar-right-1', document.getElementById('PageList1'), {'title': 'Pages', 'links': [{'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/about-project-zero.html', 'id': '4384467920505278144', 'title': 'About Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/working-at-project-zero.html', 'id': '2459334498880008057', 'title': 'Working at Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/0day.html', 'id': '3414239791814532209', 'title': '0day \x22In the Wild\x22'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.github.io/0days-in-the-wild/rca.html', 'title': '0day Exploit Root Cause Analyses'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html', 'id': '2935252455704572784', 'title': 'Vulnerability Disclosure FAQ'}], 'mobile': false, 'showPlaceholder': true, 'hasCurrentPage': false}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10