defensive-coding-guide/_preview/fedora/master/en-US/programming-languages/CXX.html

641 lines
29 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Defensive Coding Guide | Defensive Coding Guide | Programming Languages | The C++ Programming&nbsp;Language</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<!-- Overpass Font -->
<link rel="stylesheet" href="https://overpass-30e2.kxcdn.com/overpass.css">
<link href="../../../master/_stylesheets/asciibinder.css" rel="stylesheet" />
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<link href="../../../master/_images/favicon32x32.png" rel="shortcut icon" type="text/css">
<!--[if IE]><link rel="shortcut icon" href="../../../master/_images/favicon.ico"><![endif]-->
<meta content="AsciiBinder" name="application-name">
</head>
<body>
<div class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="https://docs.fedoraproject.org/"><img alt="Fedora Documentation" src="../../../master/_images/fedora.svg"></a>
</div>
</div>
</div>
<div class="container">
<p class="toggle-nav visible-xs pull-left">
<button class="btn btn-default btn-sm" type="button" data-toggle="offcanvas">Toggle nav</button>
</p>
<ol class="breadcrumb">
<li class="sitename">
<a href="../../../index.html">Home</a>
</li>
<li class="hidden-xs active">
<a href="../../en-US/index.html">Defensive Coding Guide </a>
</li>
<li class="hidden-xs active">
<a href="../../en-US/index.html">Defensive Coding Guide</a>
</li>
<li class="hidden-xs active"><a href="../../en-US/programming-languages/C.html">Programming Languages</a></li>
<li class="hidden-xs active">
The C++ Programming&nbsp;Language
</li>
</ol>
<div class="row row-offcanvas row-offcanvas-left">
<div class="col-xs-8 col-sm-3 col-md-3 sidebar sidebar-offcanvas">
<ul class="nav nav-sidebar">
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicGroup0">
<span id="tgSpan0" class="fa fa-angle-down"></span>Defensive Coding Guide
</a>
<ul id="topicGroup0" class="collapse in list-unstyled">
<li><a class="" href="../../en-US/index.html">Book Information</a></li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-1">
<span id="sgSpan-0-1" class="fa fa-caret-down"></span>&nbsp;Programming Languages
</a>
<ul id="topicSubGroup-0-1" class="nav-tertiary list-unstyled collapse in">
<li><a class="" href="../../en-US/programming-languages/C.html">The C Programming Language</a></li>
<li><a class=" active" href="../../en-US/programming-languages/CXX.html">The C++ Programming&nbsp;Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Java.html">The Java Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Python.html">The Python Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Shell.html">Shell Programming and bash</a></li>
<li><a class="" href="../../en-US/programming-languages/Go.html">The Go Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Vala.html">The Vala Programming Language</a></li>
</ul>
</li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-2">
<span id="sgSpan-0-2" class="fa fa-caret-right"></span>&nbsp;Specific Programming Tasks
</a>
<ul id="topicSubGroup-0-2" class="nav-tertiary list-unstyled collapse">
<li><a class="" href="../../en-US/tasks/Tasks-Library_Design.html">Library Design</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Descriptors.html">File Descriptor Management</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-File_System.html">File System Manipulation</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Temporary_Files.html">Temporary Files</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Processes.html">Processes</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Serialization.html">Serialization and Deserialization</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Cryptography.html">Cryptography</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Packaging.html">RPM Packaging</a></li>
</ul>
</li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-3">
<span id="sgSpan-0-3" class="fa fa-caret-right"></span>&nbsp;Implementing Security Features
</a>
<ul id="topicSubGroup-0-3" class="nav-tertiary list-unstyled collapse">
<li><a class="" href="../../en-US/features/Features-Authentication.html">Authentication and Authorization</a></li>
<li><a class="" href="../../en-US/features/Features-TLS.html">Transport Layer Security (TLS)</a></li>
<li><a class="" href="../../en-US/features/Features-HSM.html">Hardware Security Modules and Smart Cards</a></li>
</ul>
</li>
<li><a class="" href="../../en-US/Revision_History.html">Revision History</a></li>
</ul>
</li>
</ul>
</div>
<div class="col-xs-12 col-sm-9 col-md-9 main">
<div class="page-header">
<h2>The C++ Programming Language</h2>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-CXX-Language"><a class="anchor" href="#sect-Defensive_Coding-CXX-Language"></a>The Core Language</h2>
<div class="sectionbody">
<div class="paragraph">
<p>C++ includes a large subset of the C language. As far as the C
subset is used, the recommendations in <a href="#chap-Defensive_Coding-C">[chap-Defensive_Coding-C]</a> apply.</p>
</div>
<div class="sect2">
<h3 id="array-allocation-with-code-operator-new-code"><a class="anchor" href="#array-allocation-with-code-operator-new-code"></a>Array Allocation with <code>operator new[]</code></h3>
<div class="paragraph">
<p>For very large values of <code>n</code>, an expression
like <code>new T[n]</code> can return a pointer to a heap
region which is too small. In other words, not all array
elements are actually backed with heap memory reserved to the
array. Current GCC versions generate code that performs a
computation of the form <code>sizeof(T) * size_t(n)<br>
cookie_size</code>, where <code>cookie_size</code> is
currently at most 8. This computation can overflow, and GCC
versions prior to 4.8 generated code which did not detect this.
(Fedora 18 was the first release which fixed this in GCC.)</p>
</div>
<div class="paragraph">
<p>The <code>std::vector</code> template can be used instead
an explicit array allocation. (The GCC implementation detects
overflow internally.)</p>
</div>
<div class="paragraph">
<p>If there is no alternative to <code>operator new[]</code>
and the sources will be compiled with older GCC versions, code
which allocates arrays with a variable length must check for
overflow manually. For the <code>new T[n]</code> example,
the size check could be <code>n || (n &gt; 0 &amp;&amp; n &gt;
(size_t(-1) - 8) / sizeof(T))</code>. (See <a href="#sect-Defensive_Coding-C-Arithmetic">[sect-Defensive_Coding-C-Arithmetic]</a>.) If there are
additional dimensions (which must be constants according to the
C++ standard), these should be included as factors in the
divisor.</p>
</div>
<div class="paragraph">
<p>These countermeasures prevent out-of-bounds writes and potential
code execution. Very large memory allocations can still lead to
a denial of service. <a href="#sect-Defensive_Coding-Tasks-Serialization-Decoders">[sect-Defensive_Coding-Tasks-Serialization-Decoders]</a>
contains suggestions for mitigating this problem when processing
untrusted data.</p>
</div>
<div class="paragraph">
<p>See <a href="#sect-Defensive_Coding-C-Allocators-Arrays">[sect-Defensive_Coding-C-Allocators-Arrays]</a>
for array allocation advice for C-style memory allocation.</p>
</div>
</div>
<div class="sect2">
<h3 id="overloading"><a class="anchor" href="#overloading"></a>Overloading</h3>
<div class="paragraph">
<p>Do not overload functions with versions that have different
security characteristics. For instance, do not implement a
function <code>strcat</code> which works on
<code>std::string</code> arguments. Similarly, do not name
methods after such functions.</p>
</div>
</div>
<div class="sect2">
<h3 id="abi-compatibility-and-preparing-for-security-updates"><a class="anchor" href="#abi-compatibility-and-preparing-for-security-updates"></a>ABI compatibility and preparing for security updates</h3>
<div class="paragraph">
<p>A stable binary interface (ABI) is vastly preferred for security
updates. Without a stable ABI, all reverse dependencies need
recompiling, which can be a lot of work and could even be
impossible in some cases. Ideally, a security update only
updates a single dynamic shared object, and is picked up
automatically after restarting affected processes.</p>
</div>
<div class="paragraph">
<p>Outside of extremely performance-critical code, you should
ensure that a wide range of changes is possible without breaking
ABI. Some very basic guidelines are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Avoid inline functions.</p>
</li>
<li>
<p>Use the pointer-to-implementation idiom.</p>
</li>
<li>
<p>Try to avoid templates. Use them if the increased type
safety provides a benefit to the programmer.</p>
</li>
<li>
<p>Move security-critical code out of templated code, so that
it can be patched in a central place if necessary.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The KDE project publishes a document with more extensive
guidelines on ABI-preserving changes to C++ code, <a href="https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B">Policies/Binary
Compatibility Issues With C++</a>
(<strong>d-pointer</strong> refers to the
pointer-to-implementation idiom).</p>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-CXX-Language-CXX11"><a class="anchor" href="#sect-Defensive_Coding-CXX-Language-CXX11"></a>C++0X and C++11 Support</h3>
<div class="paragraph">
<p>GCC offers different language compatibility modes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code class="option">-std=c++98</code> for the original 1998 C++
standard</p>
</li>
<li>
<p><code class="option">-std=c++03</code> for the 1998 standard with the
changes from the TR1 technical report</p>
</li>
<li>
<p><code class="option">-std=c++11</code> for the 2011 C++ standard. This
option should not be used.</p>
</li>
<li>
<p><code class="option">-std=c++0x</code> for several different versions
of C++11 support in development, depending on the GCC
version. This option should not be used.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>For each of these flags, there are variants which also enable
GNU extensions (mostly language features also found in C99 or
C11):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code class="option">-std=gnu++98</code></p>
</li>
<li>
<p><code class="option">-std=gnu++03</code></p>
</li>
<li>
<p><code class="option">-std=gnu++11</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Again, <code class="option">-std=gnu++11</code> should not be used.</p>
</div>
<div class="paragraph">
<p>If you enable C++11 support, the ABI of the standard C++ library
<code>libstdc++</code> will change in subtle ways.
Currently, no C++ libraries are compiled in C++11 mode, so if
you compile your code in C++11 mode, it will be incompatible
with the rest of the system. Unfortunately, this is also the
case if you do not use any C++11 features. Currently, there is
no safe way to enable C++11 mode (except for freestanding
applications).</p>
</div>
<div class="paragraph">
<p>The meaning of C++0X mode changed from GCC release to GCC
release. Earlier versions were still ABI-compatible with C++98
mode, but in the most recent versions, switching to C++0X mode
activates C++11 support, with its compatibility problems.</p>
</div>
<div class="paragraph">
<p>Some C++11 features (or approximations thereof) are available
with TR1 support, that is, with <code class="option">-std=c03` or
[option]`-std=gnu03</code> and in the
<code>&lt;tr1/*&gt;</code> header files. This includes
<code>std::tr1::shared_ptr</code> (from
<code>&lt;tr1/memory&gt;</code>) and
<code>std::tr1::function</code> (from
<code>&lt;tr1/functional&gt;</code>). For other C++11
features, the Boost C++ library contains replacements.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-CXX-Std"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std"></a>The C++ Standard Library</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The C++ standard library includes most of its C counterpart
by reference, see <a href="#sect-Defensive_Coding-C-Libc">[sect-Defensive_Coding-C-Libc]</a>.</p>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-CXX-Std-Functions"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std-Functions"></a>Functions That Are Difficult to Use</h3>
<div class="paragraph">
<p>This section collects functions and function templates which are
part of the standard library and are difficult to use.</p>
</div>
<div class="sect3">
<h4 id="sect-Defensive_Coding-CXX-Std-Functions-Unpaired_Iterators"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std-Functions-Unpaired_Iterators"></a>Unpaired Iterators</h4>
<div class="paragraph">
<p>Functions which use output operators or iterators which do not
come in pairs (denoting ranges) cannot perform iterator range
checking.
(See <a href="#sect-Defensive_Coding-CXX-Std-Iterators">Iterators</a>)
Function templates which involve output iterators are
particularly dangerous:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>std::copy</code></p>
</li>
<li>
<p><code>std::copy_backward</code></p>
</li>
<li>
<p><code>std::copy_if</code></p>
</li>
<li>
<p><code>std::move</code> (three-argument variant)</p>
</li>
<li>
<p><code>std::move_backward</code></p>
</li>
<li>
<p><code>std::partition_copy_if</code></p>
</li>
<li>
<p><code>std::remove_copy</code></p>
</li>
<li>
<p><code>std::remove_copy_if</code></p>
</li>
<li>
<p><code>std::replace_copy</code></p>
</li>
<li>
<p><code>std::replace_copy_if</code></p>
</li>
<li>
<p><code>std::swap_ranges</code></p>
</li>
<li>
<p><code>std::transform</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In addition, <code>std::copy_n</code>,
<code>std::fill_n</code> and
<code>std::generate_n</code> do not perform iterator
checking, either, but there is an explicit count which has to be
supplied by the caller, as opposed to an implicit length
indicator in the form of a pair of forward iterators.</p>
</div>
<div class="paragraph">
<p>These output-iterator-expecting functions should only be used
with unlimited-range output iterators, such as iterators
obtained with the <code>std::back_inserter</code>
function.</p>
</div>
<div class="paragraph">
<p>Other functions use single input or forward iterators, which can
read beyond the end of the input range if the caller is not careful:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>std::equal</code></p>
</li>
<li>
<p><code>std::is_permutation</code></p>
</li>
<li>
<p><code>std::mismatch</code></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-CXX-Std-String"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std-String"></a>String Handling with <code>std::string</code></h3>
<div class="paragraph">
<p>The <code>std::string</code> class provides a convenient
way to handle strings. Unlike C strings,
<code>std::string</code> objects have an explicit length
(and can contain embedded NUL characters), and storage for its
characters is managed automatically. This section discusses
<code>std::string</code>, but these observations also
apply to other instances of the
<code>std::basic_string</code> template.</p>
</div>
<div class="paragraph">
<p>The pointer returned by the <code>data()</code> member
function does not necessarily point to a NUL-terminated string.
To obtain a C-compatible string pointer, use
<code>c_str()</code> instead, which adds the NUL
terminator.</p>
</div>
<div class="paragraph">
<p>The pointers returned by the <code>data()</code> and
<code>c_str()</code> functions and iterators are only
valid until certain events happen. It is required that the
exact <code>std::string</code> object still exists (even
if it was initially created as a copy of another string object).
Pointers and iterators are also invalidated when non-const
member functions are called, or functions with a non-const
reference parameter. The behavior of the GCC implementation
deviates from that required by the C++ standard if multiple
threads are present. In general, only the first call to a
non-const member function after a structural modification of the
string (such as appending a character) is invalidating, but this
also applies to member function such as the non-const version of
<code>begin()</code>, in violation of the C++ standard.</p>
</div>
<div class="paragraph">
<p>Particular care is necessary when invoking the
<code>c_str()</code> member function on a temporary
object. This is convenient for calling C functions, but the
pointer will turn invalid as soon as the temporary object is
destroyed, which generally happens when the outermost expression
enclosing the expression on which <code>c_str()</code>
is called completes evaluation. Passing the result of
<code>c_str()</code> to a function which does not store
or otherwise leak that pointer is safe, though.</p>
</div>
<div class="paragraph">
<p>Like with <code>std::vector</code> and
<code>std::array</code>, subscribing with
<code>operator[]</code> does not perform bounds checks.
Use the <code>at(size_type)</code> member function
instead. See <a href="#sect-Defensive_Coding-CXX-Std-Subscript">Containers and <code>operator[]</code></a>.
Furthermore, accessing the terminating NUL character using
<code>operator[]</code> is not possible. (In some
implementations, the <code>c_str()</code> member function
writes the NUL character on demand.)</p>
</div>
<div class="paragraph">
<p>Never write to the pointers returned by
<code>data()</code> or <code>c_str()</code>
after casting away <code>const</code>. If you need a
C-style writable string, use a
<code>std::vector&lt;char&gt;</code> object and its
<code>data()</code> member function. In this case, you
have to explicitly add the terminating NUL character.</p>
</div>
<div class="paragraph">
<p>GCC&#8217;s implementation of <code>std::string</code> is
currently based on reference counting. It is expected that a
future version will remove the reference counting, due to
performance and conformance issues. As a result, code that
implicitly assumes sharing by holding to pointers or iterators
for too long will break, resulting in run-time crashes or worse.
On the other hand, non-const iterator-returning functions will
no longer give other threads an opportunity for invalidating
existing iterators and pointers because iterator invalidation
does not depend on sharing of the internal character array
object anymore.</p>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-CXX-Std-Subscript"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std-Subscript"></a>Containers and <code>operator[]</code></h3>
<div class="paragraph">
<p>Many sequence containers similar to <code>std::vector</code>
provide both <code>operator[](size_type)</code> and a
member function <code>at(size_type)</code>. This applies
to <code>std::vector</code> itself,
<code>std::array</code>, <code>std::string</code>
and other instances of <code>std::basic_string</code>.</p>
</div>
<div class="paragraph">
<p><code>operator[](size_type)</code> is not required by the
standard to perform bounds checking (and the implementation in
GCC does not). In contrast, <code>at(size_type)</code>
must perform such a check. Therefore, in code which is not
performance-critical, you should prefer
<code>at(size_type)</code> over
<code>operator[](size_type)</code>, even though it is
slightly more verbose.</p>
</div>
<div class="paragraph">
<p>The <code>front()</code> and <code>back()</code>
member functions are undefined if a vector object is empty. You
can use <code>vec.at(0)</code> and
<code>vec.at(vec.size() - 1)</code> as checked
replacements. For an empty vector, <code>data()</code> is
defined; it returns an arbitrary pointer, but not necessarily
the NULL pointer.</p>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-CXX-Std-Iterators"><a class="anchor" href="#sect-Defensive_Coding-CXX-Std-Iterators"></a>Iterators</h3>
<div class="paragraph">
<p>Iterators do not perform any bounds checking. Therefore, all
functions that work on iterators should accept them in pairs,
denoting a range, and make sure that iterators are not moved
outside that range. For forward iterators and bidirectional
iterators, you need to check for equality before moving the
first or last iterator in the range. For random-access
iterators, you need to compute the difference before adding or
subtracting an offset. It is not possible to perform the
operation and check for an invalid operator afterwards.</p>
</div>
<div class="paragraph">
<p>Output iterators cannot be compared for equality. Therefore, it
is impossible to write code that detects that it has been
supplied an output area that is too small, and their use should
be avoided.</p>
</div>
<div class="paragraph">
<p>These issues make some of the standard library functions
difficult to use correctly, see <a href="#sect-Defensive_Coding-CXX-Std-Functions-Unpaired_Iterators">Unpaired Iterators</a>.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="bottom" class="text-muted py-3" >
<div class="foot">
<div class="container">
<div class="row footerlinks">
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">About</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Overview">About Fedora</a></dd>
<dd><a href="https://getfedora.org/en/sponsors">Sponsors</a></dd>
<dd><a href="https://fedoramagazine.org">Fedora Magazine</a></dd>
<dd><a href="https://fedoraproject.org/wiki/Legal:Main#Legal">Legal</a></dd>
</dl>
<ul class="list-inline">
<li>
<a href="https://www.facebook.com/TheFedoraProject" class="btn-social btn-outline"><i class="fa fa-fw fa-facebook"></i></a>
</li>
<li>
<a href="https://plus.google.com/112917221531140868607" class="btn-social btn-outline"><i class="fa fa-fw fa-google-plus"></i></a>
</li>
<li>
<a href="https://twitter.com/fedora" class="btn-social btn-outline"><i class="fa fa-fw fa-twitter"></i></a>
</li>
</ul>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title uppercase">Download</h3>
<div class="widget-body">
<dl>
<dd><a href="https://getfedora.org/en/workstation/download">Get Fedora Workstation</a></dd>
<dd><a href="https://getfedora.org/en/server/download">Get Fedora Server</a></dd>
<dd><a href="https://getfedora.org/en/atomic/download">Get Fedora Atomic</a></dd>
<dd><a href="https://spins.fedoraproject.org">Fedora Spins</a></dd>
<dd><a href="https://labs.fedoraproject.org">Fedora Labs</a></dd>
<dd><a href="https://arm.fedoraproject.org">Fedora ARM<span class="sup">&reg;</span></a></dd>
<dd><a href="https://alt.fedoraproject.org/">Alternative Downloads</a></dd>
</dl>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">Support</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Communicating_and_getting_help">Get Help</a></dd>
<dd><a href="https://ask.fedoraproject.org/">Ask Fedora</a></dd>
<dd><a href="https://fedoraproject.org/wiki/Common_F27_bugs">Common Bugs</a></dd>
<dd><a href="https://developer.fedoraproject.org/">Fedora Developer Portal</a></dd>
<dd><a href="https://docs.fedoraproject.org/f27/install-guide/index.html">Installation Guide</a></dd>
</dl>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">Join</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Join">Join Fedora</a></dd>
<dd><a href="http://fedoraplanet.org">Planet Fedora</a></dd>
<dd><a href="https://fedoraproject.org/wiki/SIGs">Fedora SIGs</a></dd>
<dd><a href="https://admin.fedoraproject.org/accounts/">Fedora Account System</a></dd>
<dd><a href="https://fedoracommunity.org/">Fedora Community</a></dd>
</dl>
</div>
</div>
</div> <!-- /row of widgets -->
<div class="row">
<div class="col-md-2">
<div class="widget-body">
<a href="https://www.redhat.com/"><img class="rh-logo" src="../../../master/_images/redhat-logo.png" alt="Red Hat Logo" /></a>
</div>
</div>
<div class="col-md-7">
<div class="widget-body">
<p class="sponsor">Fedora is sponsored by Red Hat.</p>
<p class="sponsor"><a href="https://www.redhat.com/en/technologies/linux-platforms/articles/relationship-between-fedora-and-rhel">Learn more about the relationship between Red Hat and Fedora &raquo;</a></p>
<p class="copy">&copy; 2017 Red Hat, Inc. and others. Please send any comments or corrections to the <a href="https://pagure.io/fedora-docs/docs-fp-o">documentation team</a></p>
</div>
</div>
</div> <!-- /row of widgets -->
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="../../../master/_javascripts/bootstrap-offcanvas.js" type="text/javascript"></script>
<script type="text/javascript">
/*<![CDATA[*/
$(document).ready(function() {
$("[id^='topicGroup']").on('show.bs.collapse', function (event) {
if (!($(event.target).attr('id').match(/^topicSubGroup/))) {
$(this).parent().find("[id^='tgSpan']").toggleClass("fa-angle-right fa-angle-down");
}
});
$("[id^='topicGroup']").on('hide.bs.collapse', function (event) {
if (!($(event.target).attr('id').match(/^topicSubGroup/))) {
$(this).parent().find("[id^='tgSpan']").toggleClass("fa-angle-right fa-angle-down");
}
});
$("[id^='topicSubGroup']").on('show.bs.collapse', function () {
$(this).parent().find("[id^='sgSpan']").toggleClass("fa-caret-right fa-caret-down");
});
$("[id^='topicSubGroup']").on('hide.bs.collapse', function () {
$(this).parent().find("[id^='sgSpan']").toggleClass("fa-caret-right fa-caret-down");
});
});
/*]]>*/
</script>
</body>
</html>