File: StackAllocatedChecker.cpp

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (147 lines) | stat: -rw-r--r-- 4,733 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "StackAllocatedChecker.h"

#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Frontend/CompilerInstance.h"

namespace chrome_checker {

namespace {

const char kStackAllocatedFieldError[] =
    "Non-stack-allocated type '%0' has a field '%1' which is a stack-allocated "
    "type, pointer/reference to a stack-allocated type, or template "
    "instantiation with a stack-allocated type as template parameter.";

const clang::Type* StripReferences(const clang::Type* type) {
  while (type) {
    if (type->isArrayType()) {
      type = type->getPointeeOrArrayElementType();
    } else if (type->isPointerType() || type->isReferenceType()) {
      type = type->getPointeeType().getTypePtrOrNull();
    } else {
      break;
    }
  }
  return type;
}

}  // namespace

bool StackAllocatedPredicate::IsStackAllocated(
    const clang::CXXRecordDecl* record) const {
  if (!record) {
    return false;
  }
  auto iter = cache_.find(record);
  if (iter != cache_.end()) {
    return iter->second;
  }

  bool stack_allocated = false;

  // Check member fields
  for (clang::Decl* decl : record->decls()) {
    clang::TypeAliasDecl* alias = clang::dyn_cast<clang::TypeAliasDecl>(decl);
    if (!alias) {
      continue;
    }
    if (alias->getName() == "IsStackAllocatedTypeMarker") {
      stack_allocated = true;
      break;
    }
  }

  // Check base classes
  if (record->hasDefinition()) {
    for (clang::CXXRecordDecl::base_class_const_iterator it =
             record->bases_begin();
         !stack_allocated && it != record->bases_end(); ++it) {
      clang::CXXRecordDecl* parent_record =
          it->getType().getTypePtr()->getAsCXXRecordDecl();
      stack_allocated = IsStackAllocated(parent_record);
    }
  }

  // If we don't create a cache record now, it's possible to get into infinite
  // mutual recursion between the base class check (above) and the template
  // parameter check (below).
  iter = cache_.insert({record, stack_allocated}).first;

  // Check template parameters. This is aggressive and can cause false positives
  // -- a templated class doesn't necessarily store instances of its type
  // parameters, in which case it need not be stack-allocated. In practice,
  // though, this kind of false positive is rare; and conservatively marking
  // this type as stack-allocated will catch cases where a type parameter
  // doesn't have a full type definition in the translation unit.
  if (auto* field_record_template =
          clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(record)) {
    const auto& template_args = field_record_template->getTemplateArgs();
    for (unsigned i = 0; i < template_args.size(); i++) {
      if (template_args[i].getKind() == clang::TemplateArgument::Type) {
        const auto* type =
            StripReferences(template_args[i].getAsType().getTypePtrOrNull());
        if (type && IsStackAllocated(type->getAsCXXRecordDecl())) {
          stack_allocated = true;
        }
      }
    }
  }

  iter->second = stack_allocated;
  return stack_allocated;
}

StackAllocatedChecker::StackAllocatedChecker(clang::CompilerInstance& compiler)
    : compiler_(compiler),
      stack_allocated_field_error_signature_(
          compiler.getDiagnostics().getCustomDiagID(
              clang::DiagnosticsEngine::Error,
              kStackAllocatedFieldError)) {}

void StackAllocatedChecker::Check(clang::CXXRecordDecl* record) {
  if (!record->isCompleteDefinition()) {
    return;
  }
  // If this type is stack allocated, no need to check fields.
  if (predicate_.IsStackAllocated(record)) {
    return;
  }
  for (clang::RecordDecl::field_iterator it = record->field_begin();
       it != record->field_end(); ++it) {
    clang::FieldDecl* field = *it;
    bool ignore = false;
    for (auto annotation : field->specific_attrs<clang::AnnotateAttr>()) {
      if (annotation->getAnnotation() == "stack_allocated_ignore") {
        ignore = true;
        break;
      }
    }
    if (ignore) {
      continue;
    }
    const clang::Type* type =
        StripReferences(field->getType().getTypePtrOrNull());
    if (!type) {
      continue;
    }

    auto* field_record = type->getAsCXXRecordDecl();
    if (!field_record) {
      continue;
    }

    if (predicate_.IsStackAllocated(field_record)) {
      compiler_.getDiagnostics().Report(field->getLocation(),
                                        stack_allocated_field_error_signature_)
          << record->getName() << field->getNameAsString();
    }
  }
}

}  // namespace chrome_checker