Skip to content

Commit 7b27afe

Browse files
author
Alfredo Mazzinghi
committed
Introduce clang-tidy misc-cheri-representable-subobject checker.
This check inspects structure fields and emits a warning if the field offset or size may be rounded when creating a capability for the sub-object. This assumes that the parent structure is suitably aligned to a representable boundary for its size.
1 parent ece37d2 commit 7b27afe

File tree

8 files changed

+218
-0
lines changed

8 files changed

+218
-0
lines changed

clang-tools-extra/clang-tidy/misc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
44
)
55

66
add_clang_library(clangTidyMiscModule
7+
CheriRepresentableSubobjectCheck.cpp
78
DefinitionsInHeadersCheck.cpp
89
MiscTidyModule.cpp
910
MisleadingBidirectional.cpp
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//===--- CheriRepresentableSubobjectCheck.cpp - clang-tidy ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CheriRepresentableSubobjectCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/RecordLayout.h"
12+
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include "clang/Basic/TargetInfo.h"
14+
#include "llvm/CHERI/cheri-compressed-cap/cheri_compressed_cap.h"
15+
#include "llvm/Support/Debug.h"
16+
17+
#include <limits>
18+
#include <utility>
19+
20+
#define DEBUG_TYPE "cheri-representable-subobject-check"
21+
22+
using namespace clang::ast_matchers;
23+
24+
namespace {
25+
26+
template <typename CC>
27+
std::pair<uint64_t, uint64_t>
28+
getSubobjectRangeImpl(typename CC::addr_t ReqBase,
29+
typename CC::addr_t ReqSize) {
30+
typename CC::offset_t MaxTop =
31+
static_cast<typename CC::offset_t>(
32+
std::numeric_limits<typename CC::addr_t>::max()) +
33+
1;
34+
typename CC::cap_t TmpCap = CC::make_max_perms_cap(0, 0, MaxTop);
35+
CC::setbounds(&TmpCap, ReqBase, ReqBase + ReqSize);
36+
37+
return {TmpCap.base(), TmpCap.top64()};
38+
}
39+
40+
std::pair<uint64_t, uint64_t> getSubobjectRange(const clang::TargetInfo &TI,
41+
uint64_t ReqBase,
42+
uint64_t ReqSize) {
43+
const uint64_t CapabilityWidth = TI.getCHERICapabilityWidth();
44+
45+
if (CapabilityWidth == 128) {
46+
// XXX Need to support Morello as well
47+
return getSubobjectRangeImpl<CompressedCap128>(ReqBase, ReqSize);
48+
} else if (CapabilityWidth == 64) {
49+
return getSubobjectRangeImpl<CompressedCap64>(ReqBase, ReqSize);
50+
} else {
51+
// Unsupported / unexpected
52+
assert(false && "Should not be reached, unsupported capability width");
53+
}
54+
}
55+
56+
} // namespace
57+
58+
namespace clang {
59+
namespace tidy {
60+
namespace misc {
61+
62+
bool CheriRepresentableSubobjectCheck::isLanguageVersionSupported(const LangOptions &LangOpts) const {
63+
return (LangOpts.getCheriBounds() > LangOptions::CBM_Conservative);
64+
}
65+
66+
void CheriRepresentableSubobjectCheck::registerMatchers(MatchFinder *Finder) {
67+
Finder->addMatcher(recordDecl(isDefinition()).bind("r"), this);
68+
}
69+
70+
void CheriRepresentableSubobjectCheck::check(
71+
const MatchFinder::MatchResult &Result) {
72+
const TargetInfo &TI = Result.Context->getTargetInfo();
73+
const auto *MatchedDecl = Result.Nodes.getNodeAs<RecordDecl>("r");
74+
if (!MatchedDecl->getIdentifier())
75+
return;
76+
if (MatchedDecl->isInvalidDecl())
77+
return;
78+
79+
const ASTRecordLayout &Layout =
80+
Result.Context->getASTRecordLayout(MatchedDecl);
81+
// For each field, determine whether it is representable
82+
for (const FieldDecl *Field : MatchedDecl->fields()) {
83+
// Not sure whether we care about distinguishing bitfields
84+
uint64_t Offset = Layout.getFieldOffset(Field->getFieldIndex()) / 8;
85+
TypeInfo Info = Result.Context->getTypeInfo(Field->getType());
86+
uint64_t Size = Info.Width / 8;
87+
88+
auto BaseAndTop = getSubobjectRange(TI, Offset, Size);
89+
90+
if (BaseAndTop.first != Offset) {
91+
diag(Field->getLocation(),
92+
"Field %0 (%1) at %2 in %3 with size %4 may not be representable: "
93+
"offset will be imprecisely aligned to %5")
94+
<< Field << Field->getType() << Offset << MatchedDecl << Size
95+
<< BaseAndTop.first;
96+
}
97+
if (Offset + Size != BaseAndTop.second) {
98+
diag(Field->getLocation(),
99+
"Field %0 (%1) at %2 in %3 with size %4 may not be representable: "
100+
"top will be imprecisely aligned to %5")
101+
<< Field << Field->getType() << Offset << MatchedDecl << Size
102+
<< BaseAndTop.second;
103+
}
104+
}
105+
}
106+
107+
} // namespace misc
108+
} // namespace tidy
109+
} // namespace clang
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===--- CheriRepresentableSubobjectCheck.h - clang-tidy --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CHERIREPRESENTABLESUBOBJECTCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CHERIREPRESENTABLESUBOBJECTCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang {
15+
namespace tidy {
16+
namespace misc {
17+
18+
/// CHERI-specific checker to detect possible unrepresentable sub-object bounds.
19+
///
20+
/// Note that this makes the assumption that the container structure is aligned
21+
/// to a representable boundary.
22+
class CheriRepresentableSubobjectCheck : public ClangTidyCheck {
23+
public:
24+
CheriRepresentableSubobjectCheck(StringRef Name, ClangTidyContext *Context)
25+
: ClangTidyCheck(Name, Context) {}
26+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
27+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
28+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
29+
};
30+
31+
} // namespace misc
32+
} // namespace tidy
33+
} // namespace clang
34+
35+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CHERIREPRESENTABLESUBOBJECTCHECK_H

clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../ClangTidy.h"
1010
#include "../ClangTidyModule.h"
1111
#include "../ClangTidyModuleRegistry.h"
12+
#include "CheriRepresentableSubobjectCheck.h"
1213
#include "DefinitionsInHeadersCheck.h"
1314
#include "MisleadingBidirectional.h"
1415
#include "MisleadingIdentifier.h"
@@ -33,6 +34,8 @@ namespace misc {
3334
class MiscModule : public ClangTidyModule {
3435
public:
3536
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
37+
CheckFactories.registerCheck<CheriRepresentableSubobjectCheck>(
38+
"misc-cheri-representable-subobject");
3639
CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
3740
"misc-definitions-in-headers");
3841
CheckFactories.registerCheck<MisleadingBidirectionalCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ New checks
238238
Finds virtual classes whose destructor is neither public and virtual nor
239239
protected and non-virtual.
240240

241+
- New :doc:`misc-cheri-representable-subobject
242+
<clang-tidy/checks/misc-cheri-representable-subobject>` check.
243+
244+
Finds structure fields that may result in imprecise sub-object bounds. This occurs because the
245+
offset of the field or its size are not representable by a CHERI capability.
246+
241247
- New :doc:`misc-misleading-bidirectional <clang-tidy/checks/misc-misleading-bidirectional>` check.
242248

243249
Inspects string literal and comments for unterminated bidirectional Unicode

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ Clang-Tidy Checks
211211
`llvmlibc-callee-namespace <llvmlibc-callee-namespace.html>`_,
212212
`llvmlibc-implementation-in-namespace <llvmlibc-implementation-in-namespace.html>`_,
213213
`llvmlibc-restrict-system-libc-headers <llvmlibc-restrict-system-libc-headers.html>`_, "Yes"
214+
`misc-cheri-representable-subobject <misc-cheri-representable-subobject.html>`_, "Yes"
214215
`misc-definitions-in-headers <misc-definitions-in-headers.html>`_, "Yes"
215216
`misc-misleading-bidirectional <misc-misleading-bidirectional.html>`_,
216217
`misc-misleading-identifier <misc-misleading-identifier.html>`_,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.. title:: clang-tidy - misc-cheri-representable-subobject
2+
3+
misc-cheri-representable-subobject
4+
==================================
5+
6+
Find structures that contain problematic fields for CHERI sub-object bounds.
7+
When the compiler is configured to narrow sub-object bounds, there is no
8+
guarantee that the target structure field is representable.
9+
10+
In the following case:
11+
12+
.. code-block:: c
13+
14+
// Any size not representable by the target CHERI architecture
15+
#define LARGE (1 << 15)
16+
17+
struct foo {
18+
int32_t value; // offset = 0, size = 4
19+
char buffer[LARGE]; // offset = 4, size = 32KiB
20+
};
21+
22+
the ``buffer`` field will be prompted with a warning, because the CHERI
23+
capability for ``&foo.buffer`` is not representable, under the assumption
24+
that ``struct foo`` will be properly aligned for its size by the allocator.
25+
26+
There are two warnings. One is triggered when the field offset is
27+
aligned down for representablity. Another warning is triggered when the
28+
length of the field does not match the representable length of the capability.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %check_clang_tidy -check-suffix=RV64 %s misc-cheri-representable-subobject %t -- -- -target riscv64-unknown-freebsd -march=rv64imafdcxcheri -mabi=l64pc128d -cheri-bounds=subobject-safe
2+
// RUN: %check_clang_tidy -check-suffix=RV32 %s misc-cheri-representable-subobject %t -- -- -target riscv32-unknown-freebsd -march=rv32imafdcxcheri -mabi=il32pc64 -cheri-bounds=subobject-safe
3+
4+
struct test_large_subobject {
5+
int skew_offset;
6+
char large_buffer[((1 << 13) - 7)];
7+
};
8+
// CHECK-MESSAGES-RV64: :[[@LINE-2]]:8: warning: Field 'large_buffer' ('char[8185]') at 4 in 'test_large_subobject' with size 8185 may not be representable: offset will be imprecisely aligned to 0 [misc-cheri-representable-subobject]
9+
// CHECK-MESSAGES-RV64: :[[@LINE-3]]:8: warning: Field 'large_buffer' ('char[8185]') at 4 in 'test_large_subobject' with size 8185 may not be representable: top will be imprecisely aligned to 8192 [misc-cheri-representable-subobject]
10+
// CHECK-MESSAGES-RV32: :[[@LINE-4]]:8: warning: Field 'large_buffer' ('char[8185]') at 4 in 'test_large_subobject' with size 8185 may not be representable: offset will be imprecisely aligned to 0 [misc-cheri-representable-subobject]
11+
// CHECK-MESSAGES-RV32: :[[@LINE-5]]:8: warning: Field 'large_buffer' ('char[8185]') at 4 in 'test_large_subobject' with size 8185 may not be representable: top will be imprecisely aligned to 8192 [misc-cheri-representable-subobject]
12+
13+
struct test_small_subobject {
14+
int int_value;
15+
long long_value;
16+
char small_buffer[256];
17+
void *pointer_value;
18+
};
19+
// CHECK-MESSAGES-RV32: :[[@LINE-3]]:8: warning: Field 'small_buffer' ('char[256]') at 8 in 'test_small_subobject' with size 256 may not be representable: offset will be imprecisely aligned to 0 [misc-cheri-representable-subobject]
20+
// CHECK-MESSAGES-RV32: :[[@LINE-4]]:8: warning: Field 'small_buffer' ('char[256]') at 8 in 'test_small_subobject' with size 256 may not be representable: top will be imprecisely aligned to 288 [misc-cheri-representable-subobject]
21+
22+
struct test_mixed {
23+
char pre1[33024];
24+
char buf[2941200];
25+
};
26+
// CHECK-MESSAGES-RV64: :[[@LINE-2]]:8: warning: Field 'buf' ('char[2941200]') at 33024 in 'test_mixed' with size 2941200 may not be representable: offset will be imprecisely aligned to 32768 [misc-cheri-representable-subobject]
27+
// CHECK-MESSAGES-RV64: :[[@LINE-3]]:8: warning: Field 'buf' ('char[2941200]') at 33024 in 'test_mixed' with size 2941200 may not be representable: top will be imprecisely aligned to 2977792 [misc-cheri-representable-subobject]
28+
// CHECK-MESSAGES-RV32: :[[@LINE-5]]:8: warning: Field 'pre1' ('char[33024]') at 0 in 'test_mixed' with size 33024 may not be representable: top will be imprecisely aligned to 36864 [misc-cheri-representable-subobject]
29+
// CHECK-MESSAGES-RV32: :[[@LINE-5]]:8: warning: Field 'buf' ('char[2941200]') at 33024 in 'test_mixed' with size 2941200 may not be representable: offset will be imprecisely aligned to 0 [misc-cheri-representable-subobject]
30+
// CHECK-MESSAGES-RV32: :[[@LINE-6]]:8: warning: Field 'buf' ('char[2941200]') at 33024 in 'test_mixed' with size 2941200 may not be representable: top will be imprecisely aligned to 3145728 [misc-cheri-representable-subobject]
31+
32+
struct test_flexible {
33+
int int_value;
34+
char flexbuf[];
35+
};

0 commit comments

Comments
 (0)