Skip to content

Commit

Permalink
Handle incomplete array types for no_preserve_cheri_tags
Browse files Browse the repository at this point in the history
If we see something like extern struct NoCaps unsized_array[]; we can still
assume that it does not contain tags since global variables have a defined
type. The only exception here are arrays of type char.
  • Loading branch information
arichardson committed Jul 15, 2021
1 parent 30abe9f commit 09e3f46
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 1 deletion.
10 changes: 9 additions & 1 deletion clang/lib/CodeGen/CodeGenTypes.cpp
Expand Up @@ -1040,7 +1040,8 @@ llvm::PreserveCheriTags CodeGenTypes::copyShouldPreserveTags(const Expr *E) {
findUnderlyingVarDecl(E, &ConservativeTypeApproximation);
if (UnderlyingVar) {
QualType VarTy = UnderlyingVar->getType();
assert(!VarTy->isIncompleteType() && "Unexpected incomplete type");
assert(!VarTy->isDependentType() && !VarTy->containsErrors() &&
"Unexpected dependent/error type");
if (VarTy->isReferenceType()) {
// If the variable declaration is a C++ reference we can assume that the
// effective type of the object matches the type of the reference since
Expand Down Expand Up @@ -1124,6 +1125,13 @@ CodeGenTypes::copyShouldPreserveTagsForPointee(QualType Pointee,
} else if (Pointee->isIncompleteType()) {
// We don't know if incomplete types contain capabilities, so be
// conservative and assume that they might.
// The only exception here are incomplete array types (e.g. extern int x[])
// since we don't care about the size of the type, just whether it can
// contain capabilities.
if (Pointee->isIncompleteArrayType()) {
return copyShouldPreserveTagsForPointee(
Context.getBaseElementType(Pointee), EffectiveTypeKnown);
}
return llvm::PreserveCheriTags::Unknown;
} else if (auto *RD = Pointee->getAsRecordDecl()) {
// TODO: should maybe add ASTContext::neverContainsCapabilities()
Expand Down
140 changes: 140 additions & 0 deletions clang/test/CodeGen/cheri/no-tag-copy-global.c
@@ -0,0 +1,140 @@
// RUN: %riscv64_cheri_purecap_cc1 %s -emit-llvm -o - -O0 | FileCheck %s --check-prefixes=CHECK --implicit-check-not=llvm.memcpy
/// Check that we can add the no_preserve_cheri_tags attribute for copies
/// to/from globals (as those have a fixed effective type).

static __uint128_t u128 = 0;
extern __uint128_t *u128_ptr;
extern __uint128_t u128_array[4];
extern __uint128_t u128_array_unsized[];

void test_u128(void *buf) {
// CHECK-LABEL: @test_u128(
// No need to preserve tags since we can see the definition:
__builtin_memcpy(buf, &u128, sizeof(u128));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 16, i1 false) [[NO_PRESERVE_TAGS:#[0-9]+]]{{$}}

// We can see the variable declaration but it's a pointer so we can't add the attribute
__builtin_memcpy(buf, u128_ptr, sizeof(*u128_ptr));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 16, i1 false){{$}}

// Here we can see the array definition, so no need to preserve tags:
__builtin_memcpy(buf, u128_array, sizeof(u128_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 64, i1 false) [[NO_PRESERVE_TAGS]]{{$}}
// Same for unsized arrays:
__builtin_memcpy(buf, u128_array_unsized, 80);
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 80, i1 false) [[NO_PRESERVE_TAGS]]{{$}}
}

// CHECK: declare void @llvm.memcpy.p200i8.p200i8.i64(

static unsigned __intcap ucap = 0;
extern unsigned __intcap *ucap_ptr;
extern unsigned __intcap ucap_array[4];
extern unsigned __intcap ucap_array_unsized[];

void test_ucap(void *buf) {
// CHECK-LABEL: @test_ucap(
// In all of these cases we should add the must_preserve_tags attribute since
// we are copying a capability type.
__builtin_memcpy(buf, &ucap, sizeof(ucap));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 16, i1 false) [[MUST_PRESERVE_UINTCAP:#[0-9]+]]{{$}}
__builtin_memcpy(buf, ucap_ptr, sizeof(*ucap_ptr));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 16, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}}
__builtin_memcpy(buf, ucap_array, sizeof(ucap_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 64, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}}
__builtin_memcpy(buf, ucap_array_unsized, 80);
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 80, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}}
}

struct NoCaps {
int i;
long l[15];
};
extern struct NoCaps nocap_struct;
extern struct NoCaps *nocap_struct_ptr;
extern struct NoCaps nocap_struct_array[4];
extern struct NoCaps nocap_struct_array_unsized[];

void test_struct_nocaps(void *buf) {
// CHECK-LABEL: @test_struct_nocaps(
// No need to preserve tags since we can see the definition:
__builtin_memcpy(buf, &nocap_struct, sizeof(nocap_struct));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 128, i1 false) [[NO_PRESERVE_TAGS]]{{$}}

// We can see the variable declaration but it's a pointer so we can't add the attribute
__builtin_memcpy(buf, nocap_struct_ptr, sizeof(*nocap_struct_ptr));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 128, i1 false){{$}}

// Here we can see the array definition, so no need to preserve tags:
__builtin_memcpy(buf, nocap_struct_array, sizeof(nocap_struct_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 512, i1 false) [[NO_PRESERVE_TAGS]]{{$}}
// Same for unsized arrays:
__builtin_memcpy(buf, nocap_struct_array_unsized, sizeof(struct NoCaps) * 3);
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 384, i1 false) [[NO_PRESERVE_TAGS]]{{$}}
}

struct WithCaps {
int i;
union {
long l;
void *p;
} u;
};
extern struct WithCaps withcap_struct;
extern struct WithCaps *withcap_struct_ptr;
extern struct WithCaps withcap_struct_array[4];
extern struct WithCaps withcap_struct_array_unsized[];

void test_struct_with_caps(void *buf) {
// CHECK-LABEL: @test_struct_with_caps(
// In all of these cases we should add the must_preserve_tags attribute since
// we are copying a capability type.
__builtin_memcpy(buf, &withcap_struct, sizeof(withcap_struct));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 32, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP:#[0-9]+]]{{$}}
__builtin_memcpy(buf, withcap_struct_ptr, sizeof(*withcap_struct_ptr));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 32, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}}
__builtin_memcpy(buf, withcap_struct_array, sizeof(withcap_struct_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 128, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}}
__builtin_memcpy(buf, withcap_struct_array_unsized, 80);
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 80, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}}
}

extern char global_char_array[32];
extern unsigned char global_uchar_array[32];
extern signed char global_schar_array[32];

void test_char_array(void *buf) {
// CHECK-LABEL: @test_char_array(
// Arrays of type "char" are special and can be assumed to hold any type.
// We extend this to signed/unsigned char as well to avoid potentially surprising behaviour.
// None of these cases should add the no_preserve_tags attribute:
__builtin_memcpy(buf, &global_char_array, sizeof(global_char_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 32, i1 false){{$}}
__builtin_memcpy(buf, &global_uchar_array, sizeof(global_uchar_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 32, i1 false){{$}}
__builtin_memcpy(buf, &global_schar_array, sizeof(global_schar_array));
// CHECK: call void @llvm.memcpy.p200i8.p200i8.i64(
// CHECK-SAME: , i64 32, i1 false){{$}}
}

// CHECK: [[NO_PRESERVE_TAGS]] = { no_preserve_cheri_tags }
// CHECK: [[MUST_PRESERVE_UINTCAP]] = { must_preserve_cheri_tags "frontend-memtransfer-type"="'unsigned __intcap'" }
// CHECK: [[MUST_PRESERVE_STRUCT_WITHCAP]] = { must_preserve_cheri_tags "frontend-memtransfer-type"="'struct WithCaps'" }

0 comments on commit 09e3f46

Please sign in to comment.