{"id":3369,"date":"2014-12-05T18:45:56","date_gmt":"2014-12-05T17:45:56","guid":{"rendered":"http:\/\/glandium.org\/blog\/?p=3369"},"modified":"2014-12-05T18:45:56","modified_gmt":"2014-12-05T17:45:56","slug":"using-c-templates-to-prevent-some-classes-of-buffer-overflows","status":"publish","type":"post","link":"https:\/\/glandium.org\/blog\/?p=3369","title":{"rendered":"Using C++ templates to prevent some classes of buffer overflows"},"content":{"rendered":"<p>I recently found a <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1102022\">small buffer overflow in Firefox's SOCKS support<\/a>, and came up with a nice-ish way to make it a C++ compilation error when it may happen with some template magic.<\/p>\n<p>A simplfied form of the problem looks like this:<\/p>\n<blockquote>\n<pre>class nsSOCKSSocketInfo {\r\npublic:\r\n    nsSOCKSSocketInfo() : mData(new uint8_t[BUFFER_SIZE]) , mDataLength(0) {}\r\n    ~nsSOCKSSocketInfo() { delete[] mData; }\r\n\r\n    void WriteUint8(uint8_t aValue) { mData[mDataLength++] = aValue; }\r\n\r\n    void WriteV5AuthRequest() {\r\n        mDataLength = 0;\r\n        WriteUint8(0x05);\r\n        WriteUint8(0x01);\r\n        WriteUint8(0x00);\r\n    }\r\nprivate:\r\n    uint8_t* mData;\r\n    uint32_t mDataLength;\r\n    static const size_t BUFFER_SIZE = 2;\r\n};\r\n<\/pre>\n<\/blockquote>\n<p>Here, the problem is more or less obvious: the third WriteUint8() call in WriteV5AuthRequest() will write beyond the allocated buffer size. (The real buffer size was much larger than that, and it was a different method overflowing, but you get the point)<\/p>\n<p>While growing the buffer size fixes the overflow, that doesn't do much to prevent a similar overflow from happening again if the code changes. That got me thinking that there has to be a way to do some compile-time checking of this. The resulting solution, at its core, looks like this:<\/p>\n<blockquote>\n<pre>template &lt;size_t Size&gt; class Buffer {\r\npublic:\r\n    Buffer() : mBuf(nullptr) , mLength(0) {}\r\n    Buffer(uint8_t* aBuf, size_t aLength=0) : mBuf(aBuf), mLength(aLength) {}\r\n\r\n    Buffer&lt;Size - 1&gt; WriteUint8(uint8_t aValue) {\r\n        static_assert(Size >= 1, \"Cannot write that much\");\r\n        *mBuf = aValue;\r\n        Buffer<size - 1> result(mBuf + 1, mLength + 1);\r\n        mBuf = nullptr;\r\n        mLength = 0;\r\n        return result;\r\n    }\r\n\r\n    size_t Written() { return mLength; }\r\nprivate:\r\n    uint8_t* mBuf;\r\n    size_t mLength;\r\n};\r\n<\/size><\/pre>\n<\/blockquote>\n<p>Then replacing WriteV5AuthRequest() with the following:<\/p>\n<blockquote>\n<pre>void WriteV5AuthRequest() {\r\n    mDataLength = Buffer&lt;BUFFER_SIZE&gt;(mData)\r\n        .WriteUint8(0x05)\r\n        .WriteUint8(0x01)\r\n        .WriteUint8(0x00)\r\n        .Written();\r\n}\r\n<\/pre>\n<\/blockquote>\n<p>So, how does this work? The Buffer class is templated by size. The first thing we do is to create an instance for the complete size of the buffer:<\/p>\n<blockquote><p><code>Buffer&lt;BUFFER_SIZE&gt;(mData)<\/code><\/p><\/blockquote>\n<p>Then call the WriteUint8 method on that instance, to write the first byte:<\/p>\n<blockquote><p><code>.WriteUint8(0x05)<\/code><\/p><\/blockquote>\n<p>The result of that call is a Buffer&lt;BUFFER_SIZE - 1&gt; (in our case, Buffer&lt;1&gt;) instance pointing to &mData[1] and recording that 1 byte has been written.<br \/>\nThen we call the WriteUint8 method on that result, to write the second byte:<\/p>\n<blockquote><p><code>.WriteUint8(0x01)<\/code><\/p><\/blockquote>\n<p>The result of that call is a Buffer&lt;BUFFER_SIZE - 2&gt; (in our case, Buffer&lt;0&gt;) instance pointing to &mData[2] and recording that 2 bytes have been written so far.<br \/>\nThen we call the WriteUint8 method on that new result, to write the third byte:<\/p>\n<blockquote><p><code>.WriteUint8(0x00)<\/code><\/p><\/blockquote>\n<p>But this time, the Size template parameter being 0, it doesn't match the <code>Size >= 1<\/code> static assertion, and the build fails.<br \/>\nIf we modify <code>BUFFER_SIZE<\/code> to 3, then the instance we run that last WriteUint8 call on is a Buffer&lt;1> and we don't hit the static assertion.<\/p>\n<p>Interestingly, this also makes the compiler emit more efficient code than the original version.<\/p>\n<p>Check the <a href=\"https:\/\/hg.mozilla.org\/mozilla-central\/rev\/9bc03c937fae\">full patch<\/a> for more about the complete solution.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently found a small buffer overflow in Firefox&#8217;s SOCKS support, and came up with a nice-ish way to make it a C++ compilation error when it may happen with some template magic. A simplfied form of the problem looks like this: class nsSOCKSSocketInfo { public: nsSOCKSSocketInfo() : mData(new uint8_t[BUFFER_SIZE]) , mDataLength(0) {} ~nsSOCKSSocketInfo() { [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25],"tags":[23],"class_list":["post-3369","post","type-post","status-publish","format-standard","hentry","category-planet-mozilla","tag-en"],"_links":{"self":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3369","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3369"}],"version-history":[{"count":6,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3369\/revisions"}],"predecessor-version":[{"id":3381,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=\/wp\/v2\/posts\/3369\/revisions\/3381"}],"wp:attachment":[{"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/glandium.org\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}