Skip to content

Commit

Permalink
[randstruct] Add randomize structure layout support
Browse files Browse the repository at this point in the history
The Randstruct feature is a compile-time hardening technique that
randomizes the field layout for designated structures of a code base.
Admittedly, this is mostly useful for closed-source releases of code,
since the randomization seed would need to be available for public and
open source applications.

Why implement it? This patch set enhances Clang’s feature parity with
that of GCC which already has the Randstruct feature. It's used by the
Linux kernel in certain structures to help thwart attacks that depend on
structure layouts in memory.

This patch set is a from-scratch reimplementation of the Randstruct
feature that was originally ported to GCC. The patches for the GCC
implementation can be found here:

  https://www.openwall.com/lists/kernel-hardening/2017/04/06/14

Link: https://lists.llvm.org/pipermail/cfe-dev/2019-March/061607.html
Co-authored-by: Cole Nixon <nixontcole@gmail.com>
Co-authored-by: Connor Kuehl <cipkuehl@gmail.com>
Co-authored-by: James Foster <jafosterja@gmail.com>
Co-authored-by: Jeff Takahashi <jeffrey.takahashi@gmail.com>
Co-authored-by: Jordan Cantrell <jordan.cantrell@mail.com>
Co-authored-by: Nikk Forbus <nicholas.forbus@gmail.com>
Co-authored-by: Tim Pugh <nwtpugh@gmail.com>
Co-authored-by: Bill Wendling <isanbard@gmail.com>
Signed-off-by: Bill Wendling <isanbard@gmail.com>

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D121556
  • Loading branch information
connorkuehl authored and isanbard committed Apr 8, 2022
1 parent 2aa575f commit 3f0587d
Show file tree
Hide file tree
Showing 21 changed files with 893 additions and 2 deletions.
14 changes: 14 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ Major New Features
There is an analogous ``zero_call_used_regs`` attribute to allow for finer
control of this feature.

- Clang now supports randomizing structure layout in C. This feature is a
compile-time hardening technique, making it more difficult for an attacker to
retrieve data from structures. Specify randomization with the
``randomize_layout`` attribute. The corresponding ``no_randomize_layout``
attribute can be used to turn the feature off.

A seed value is required to enable randomization, and is deterministic based
on a seed value. Use the ``-frandomize-layout-seed=`` or
``-frandomize-layout-seed-file=`` flags.

.. note::

Randomizing structure layout is a C-only feature.

Bug Fixes
------------------
- ``CXXNewExpr::getArraySize()`` previously returned a ``llvm::Optional``
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4051,6 +4051,12 @@ class RecordDecl : public TagDecl {
RecordDeclBits.ParamDestroyedInCallee = V;
}

bool isRandomized() const { return RecordDeclBits.IsRandomized; }

void setIsRandomized(bool V) { RecordDeclBits.IsRandomized = V; }

void reorderFields(const SmallVectorImpl<Decl *> &Fields);

/// Determines whether this declaration represents the
/// injected class name.
///
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ class alignas(8) Decl {
friend class ASTReader;
friend class CXXClassMemberWrapper;
friend class LinkageComputer;
friend class RecordDecl;
template<typename decl_type> friend class Redeclarable;

/// Access - Used by C++ decls for the access specifier.
Expand Down Expand Up @@ -1540,10 +1541,13 @@ class DeclContext {

/// Represents the way this type is passed to a function.
uint64_t ArgPassingRestrictions : 2;

/// Indicates whether this struct has had its field layout randomized.
uint64_t IsRandomized : 1;
};

/// Number of non-inherited bits in RecordDeclBitfields.
enum { NumRecordDeclBits = 14 };
enum { NumRecordDeclBits = 15 };

/// Stores the bits used by OMPDeclareReductionDecl.
/// If modified NumOMPDeclareReductionDeclBits and the accessor
Expand Down
38 changes: 38 additions & 0 deletions clang/include/clang/AST/Randstruct.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===- Randstruct.h - Interfact for structure randomization -------*- C++ -*-=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains the interface for Clang's structure field layout
// randomization.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_RANDSTRUCT_H
#define LLVM_CLANG_AST_RANDSTRUCT_H

namespace llvm {
template <typename T> class ArrayRef;
template <typename T> class SmallVectorImpl;
class StringRef;
} // end namespace llvm

namespace clang {

class ASTContext;
class Decl;
class RecordDecl;

namespace randstruct {

bool randomizeStructureLayout(const ASTContext &Context, llvm::StringRef Name,
llvm::ArrayRef<Decl *> Fields,
llvm::SmallVectorImpl<Decl *> &FinalOrdering);

} // namespace randstruct
} // namespace clang

#endif // LLVM_CLANG_AST_RANDSTRUCT_H
15 changes: 15 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3957,3 +3957,18 @@ def HLSLNumThreads: InheritableAttr {
let LangOpts = [HLSL];
let Documentation = [NumThreadsDocs];
}

def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
let Documentation = [ClangRandomizeLayoutDocs];
let LangOpts = [COnly];
}

def NoRandomizeLayout : InheritableAttr {
let Spellings = [GCC<"no_randomize_layout">];
let Subjects = SubjectList<[Record]>;
let Documentation = [ClangRandomizeLayoutDocs];
let LangOpts = [COnly];
}
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
32 changes: 32 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -6379,3 +6379,35 @@ dictate the thread id. Total number of threads executed is ``X * Y * Z``.
The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads
}];
}

def ClangRandomizeLayoutDocs : Documentation {
let Category = DocCatDecl;
let Heading = "randomize_layout, no_randomize_layout";
let Content = [{
The attribute ``randomize_layout``, when attached to a C structure, selects it
for structure layout field randomization; a compile-time hardening technique. A
"seed" value, is specified via the ``-frandomize-layout-seed=`` command line flag.
For example:

.. code-block:: bash

SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'`
make ... CFLAGS="-frandomize-layout-seed=$SEED" ...

You can also supply the seed in a file with ``-frandomize-layout-seed-file=``.
For example:

.. code-block:: bash

od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n' > /tmp/seed_file.txt
make ... CFLAGS="-frandomize-layout-seed-file=/tmp/seed_file.txt" ...

The randomization is deterministic based for a given seed, so the entire
program should be compiled with the same seed, but keep the seed safe
otherwise.

The attribute ``no_randomize_layout``, when attached to a C structure,
instructs the compiler that this structure should not have its field layout
randomized.
}];
}
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ def err_drv_amdgpu_ieee_without_no_honor_nans : Error<
"invalid argument '-mno-amdgpu-ieee' only allowed with relaxed NaN handling">;
def err_drv_argument_not_allowed_with : Error<
"invalid argument '%0' not allowed with '%1'">;
def err_drv_cannot_open_randomize_layout_seed_file : Error<
"cannot read randomize layout seed file '%0'">;
def err_drv_invalid_version_number : Error<
"invalid version number in '%0'">;
def err_drv_no_linker_llvm_support : Error<
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11582,5 +11582,7 @@ def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numt
def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">;
def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">;

// Layout randomization warning.
def err_cast_from_randomized_struct : Error<
"casting from randomized structure pointer type %0 to %1">;
} // end of sema component.

3 changes: 3 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ class LangOptions : public LangOptionsBase {
/// The default stream kind used for HIP kernel launching.
GPUDefaultStreamKind GPUDefaultStream;

/// The seed used by the randomize structure layout feature.
std::string RandstructSeed;

LangOptions();

// Define accessors/mutators for language options of enumeration type.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,12 @@ defm merge_all_constants : BoolFOption<"merge-all-constants",
def fmessage_length_EQ : Joined<["-"], "fmessage-length=">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Format message diagnostics so that they fit within N columns">,
MarshallingInfoInt<DiagnosticOpts<"MessageLength">>;
def frandomize_layout_seed_EQ : Joined<["-"], "frandomize-layout-seed=">,
MetaVarName<"<seed>">, Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"The seed used by the randomize structure layout feature">;
def frandomize_layout_seed_file_EQ : Joined<["-"], "frandomize-layout-seed-file=">,
MetaVarName<"<file>">, Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"File holding the seed used by the randomize structure layout feature">;
def fms_compatibility : Flag<["-"], "fms-compatibility">, Group<f_Group>, Flags<[CC1Option, CoreOption]>,
HelpText<"Enable full Microsoft Visual C++ compatibility">,
MarshallingInfoFlag<LangOpts<"MSVCCompat">>;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ add_clang_library(clangAST
ParentMap.cpp
PrintfFormatString.cpp
QualTypeNames.cpp
Randstruct.cpp
RawCommentList.cpp
RecordLayout.cpp
RecordLayoutBuilder.cpp
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "clang/AST/ODRHash.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Randstruct.h"
#include "clang/AST/Redeclarable.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
Expand Down Expand Up @@ -4583,6 +4584,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
setHasNonTrivialToPrimitiveCopyCUnion(false);
setParamDestroyedInCallee(false);
setArgPassingRestrictions(APK_CanPassInRegs);
setIsRandomized(false);
}

RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC,
Expand Down Expand Up @@ -4666,6 +4668,12 @@ bool RecordDecl::isMsStruct(const ASTContext &C) const {
return hasAttr<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
}

void RecordDecl::reorderFields(const SmallVectorImpl<Decl *> &Fields) {
std::tie(FirstDecl, LastDecl) = DeclContext::BuildDeclChain(Fields, false);
LastDecl->NextInContextAndBits.setPointer(nullptr);
setIsRandomized(true);
}

void RecordDecl::LoadFieldsFromExternalStorage() const {
ExternalASTSource *Source = getASTContext().getExternalSource();
assert(hasExternalLexicalStorage() && Source && "No external storage?");
Expand Down
Loading

0 comments on commit 3f0587d

Please sign in to comment.