1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/tools/clang_generator/emitter.h"
16
17 #include <algorithm>
18 #include <cstdint>
19 #include <string>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/container/flat_hash_set.h"
24 #include "absl/container/node_hash_set.h"
25 #include "absl/random/random.h"
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/ascii.h"
29 #include "absl/strings/match.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/str_join.h"
33 #include "absl/strings/str_replace.h"
34 #include "absl/strings/str_split.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/strings/strip.h"
37 #include "clang/AST/ASTContext.h"
38 #include "clang/AST/Decl.h"
39 #include "clang/AST/DeclCXX.h"
40 #include "clang/AST/DeclTemplate.h"
41 #include "clang/AST/QualTypeNames.h"
42 #include "clang/AST/Type.h"
43 #include "clang/Format/Format.h"
44 #include "sandboxed_api/tools/clang_generator/diagnostics.h"
45 #include "sandboxed_api/tools/clang_generator/generator.h"
46 #include "sandboxed_api/tools/clang_generator/types.h"
47 #include "sandboxed_api/util/status_macros.h"
48
49 namespace sapi {
50
51 // Common file prolog with auto-generation notice.
52 // Note: The includes will be adjusted by Copybara when converting to/from
53 // internal code. This is intentional.
54 // Text template arguments:
55 // 1. Header guard
56 constexpr absl::string_view kHeaderProlog =
57 R"(// AUTO-GENERATED by the Sandboxed API generator.
58 // Edits will be discarded when regenerating this file.
59
60 #ifndef %1$s
61 #define %1$s
62
63 #include <cstdint>
64 #include <type_traits>
65
66 #include "absl/base/macros.h"
67 #include "absl/status/status.h"
68 #include "absl/status/statusor.h"
69 #include "sandboxed_api/sandbox.h"
70 #include "sandboxed_api/util/status_macros.h"
71 #include "sandboxed_api/vars.h"
72
73 )";
74 constexpr absl::string_view kHeaderEpilog =
75 R"(
76 #endif // %1$s)";
77
78 // Text template arguments:
79 // 1. Include for embedded sandboxee objects
80 constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h"
81
82 )";
83
84 // Text template arguments:
85 // 1. Namespace name
86 constexpr absl::string_view kNamespaceBeginTemplate =
87 R"(
88 namespace %1$s {
89
90 )";
91 constexpr absl::string_view kNamespaceEndTemplate =
92 R"(
93 } // namespace %1$s
94 )";
95
96 // Text template arguments:
97 // 1. Class name
98 // 2. Embedded object identifier
99 constexpr absl::string_view kEmbedClassTemplate = R"(
100 // Sandbox with embedded sandboxee and default policy
101 class %1$s : public ::sapi::Sandbox {
102 public:
103 %1$s() : ::sapi::Sandbox(%2$s_embed_create()) {}
104 };
105
106 )";
107
108 // Text template arguments:
109 // 1. Class name
110 constexpr absl::string_view kClassHeaderTemplate = R"(
111 // Sandboxed API
112 class %1$s {
113 public:
114 explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {}
115
116 ABSL_DEPRECATED("Call sandbox() instead")
117 ::sapi::Sandbox* GetSandbox() const { return sandbox(); }
118 ::sapi::Sandbox* sandbox() const { return sandbox_; }
119 )";
120
121 constexpr absl::string_view kClassFooterTemplate = R"(
122 private:
123 ::sapi::Sandbox* sandbox_;
124 };
125 )";
126
127 namespace internal {
128
ReformatGoogleStyle(const std::string & filename,const std::string & code,int column_limit)129 absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
130 const std::string& code,
131 int column_limit) {
132 // Configure code style based on Google style, but enforce pointer alignment
133 clang::format::FormatStyle style =
134 clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
135 style.DerivePointerAlignment = false;
136 style.PointerAlignment = clang::format::FormatStyle::PAS_Left;
137 if (column_limit >= 0) {
138 style.ColumnLimit = column_limit;
139 }
140
141 clang::tooling::Replacements replacements = clang::format::reformat(
142 style, code, llvm::ArrayRef(clang::tooling::Range(0, code.size())),
143 filename);
144
145 llvm::Expected<std::string> formatted_header =
146 clang::tooling::applyAllReplacements(code, replacements);
147 if (!formatted_header) {
148 return absl::InternalError(llvm::toString(formatted_header.takeError()));
149 }
150 return *formatted_header;
151 }
152
153 } // namespace internal
154
GetIncludeGuard(absl::string_view filename)155 std::string GetIncludeGuard(absl::string_view filename) {
156 if (filename.empty()) {
157 static auto* bit_gen = new absl::BitGen();
158 return absl::StrCat(
159 // Copybara will transform the string. This is intentional.
160 "SANDBOXED_API_GENERATED_HEADER_",
161 absl::AsciiStrToUpper(absl::StrCat(
162 absl::Hex(absl::Uniform<uint64_t>(*bit_gen), absl::kZeroPad16))),
163 "_");
164 }
165
166 constexpr absl::string_view kUnderscorePrefix = "SAPI_";
167 std::string guard;
168 guard.reserve(filename.size() + kUnderscorePrefix.size() + 1);
169 for (auto c : filename) {
170 if (absl::ascii_isalpha(c)) {
171 guard += absl::ascii_toupper(c);
172 continue;
173 }
174 if (guard.empty()) {
175 guard = kUnderscorePrefix;
176 }
177 if (absl::ascii_isdigit(c)) {
178 guard += c;
179 } else if (guard.back() != '_') {
180 guard += '_';
181 }
182 }
183 if (!absl::EndsWith(guard, "_")) {
184 guard += '_';
185 }
186 return guard;
187 }
188
189 // Returns the namespace components of a declaration's qualified name.
GetNamespacePath(const clang::TypeDecl * decl)190 std::vector<std::string> GetNamespacePath(const clang::TypeDecl* decl) {
191 std::vector<std::string> comps;
192 for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) {
193 if (const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx)) {
194 comps.push_back(nd->getName().str());
195 }
196 }
197 std::reverse(comps.begin(), comps.end());
198 return comps;
199 }
200
PrintRecordTemplateArguments(const clang::CXXRecordDecl * record)201 std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) {
202 const auto* template_inst_decl = record->getTemplateInstantiationPattern();
203 if (!template_inst_decl) {
204 return "";
205 }
206 const auto* template_decl = template_inst_decl->getDescribedClassTemplate();
207 if (!template_decl) {
208 return "";
209 }
210 const auto* template_params = template_decl->getTemplateParameters();
211 if (!template_params) {
212 return "";
213 }
214 clang::ASTContext& context = record->getASTContext();
215 std::vector<std::string> params;
216 params.reserve(template_params->size());
217 for (const auto& template_param : *template_params) {
218 if (const auto* p =
219 llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_param)) {
220 // TODO(cblichmann): Should be included by CollectRelatedTypes().
221 params.push_back(clang::TypeName::getFullyQualifiedName(
222 p->getType().getDesugaredType(context), context,
223 context.getPrintingPolicy()));
224 } else { // Also covers template template parameters
225 params.push_back("typename");
226 }
227 absl::StrAppend(¶ms.back(), " /*",
228 std::string(template_param->getName()), "*/");
229 }
230 return absl::StrCat("template <", absl::StrJoin(params, ", "), ">");
231 }
232
233 // Serializes the given Clang AST declaration back into compilable source code.
PrintDecl(const clang::Decl * decl)234 std::string PrintDecl(const clang::Decl* decl) {
235 std::string pretty;
236 llvm::raw_string_ostream os(pretty);
237 decl->print(os);
238 return os.str();
239 }
240
241 // Returns the spelling for a given declaration will be emitted to the final
242 // header. This may rewrite declarations (like converting typedefs to using,
243 // etc.). Note that the resulting spelling will need to be wrapped inside a
244 // namespace if the original declaration was inside one.
GetSpelling(const clang::Decl * decl)245 std::string GetSpelling(const clang::Decl* decl) {
246 // TODO(cblichmann): Make types nicer
247 // - Rewrite typedef to using
248 // - Rewrite function pointers using std::add_pointer_t<>;
249
250 if (const auto* typedef_decl = llvm::dyn_cast<clang::TypedefNameDecl>(decl)) {
251 // Special case: anonymous enum/struct
252 if (auto* tag_decl = typedef_decl->getAnonDeclWithTypedefName()) {
253 return absl::StrCat("typedef ", PrintDecl(tag_decl), " ",
254 ToStringView(typedef_decl->getName()));
255 }
256 }
257
258 if (const auto* record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
259 if (record_decl->hasDefinition() &&
260 // Aggregates capture all C-like structs, but also structs with
261 // non-static members that have default initializers.
262 record_decl->isAggregate() &&
263 // Make sure to skip types with user-defined methods (including
264 // constructors).
265 record_decl->methods().empty()) {
266 return PrintDecl(decl);
267 }
268 // For unsupported types or types with no definition, only emit a forward
269 // declaration.
270 return absl::StrCat(PrintRecordTemplateArguments(record_decl),
271 record_decl->isClass() ? "class " : "struct ",
272 ToStringView(record_decl->getName()));
273 }
274 return PrintDecl(decl);
275 }
276
GetParamName(const clang::ParmVarDecl * decl,int index)277 std::string GetParamName(const clang::ParmVarDecl* decl, int index) {
278 if (std::string name = decl->getName().str(); !name.empty()) {
279 return absl::StrCat(name, "_"); // Suffix to avoid collisions
280 }
281 return absl::StrCat("unnamed", index, "_");
282 }
283
PrintFunctionPrototypeComment(const clang::FunctionDecl * decl)284 absl::StatusOr<std::string> PrintFunctionPrototypeComment(
285 const clang::FunctionDecl* decl) {
286 const clang::ASTContext& context = decl->getASTContext();
287
288 std::string out = absl::StrCat(
289 MapQualTypeParameterForCxx(context, decl->getDeclaredReturnType()), " ",
290 decl->getQualifiedNameAsString(), "(");
291
292 std::string print_separator;
293 for (int i = 0; i < decl->getNumParams(); ++i) {
294 const clang::ParmVarDecl* param = decl->getParamDecl(i);
295
296 absl::StrAppend(&out, print_separator);
297 print_separator = ", ";
298 absl::StrAppend(&out,
299 MapQualTypeParameterForCxx(context, param->getType()));
300 if (std::string name = param->getName().str(); !name.empty()) {
301 absl::StrAppend(&out, " ", name);
302 }
303 }
304 absl::StrAppend(&out, ")");
305
306 SAPI_ASSIGN_OR_RETURN(
307 std::string formatted,
308 internal::ReformatGoogleStyle(/*filename=*/"input", out, 75));
309 out.clear();
310 for (const auto& line : absl::StrSplit(formatted, '\n')) {
311 absl::StrAppend(&out, "// ", line, "\n");
312 }
313 return out;
314 }
315
EmitFunction(const clang::FunctionDecl * decl)316 absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
317 const clang::QualType return_type = decl->getDeclaredReturnType();
318 if (return_type->isRecordType()) {
319 return MakeStatusWithDiagnostic(
320 decl->getBeginLoc(), absl::StatusCode::kCancelled,
321 "returning record by value, skipping function");
322 }
323 std::string out;
324
325 SAPI_ASSIGN_OR_RETURN(std::string prototype,
326 PrintFunctionPrototypeComment(decl));
327 absl::StrAppend(&out, "\n", prototype);
328
329 auto function_name = ToStringView(decl->getName());
330 const bool returns_void = return_type->isVoidType();
331
332 const clang::ASTContext& context = decl->getASTContext();
333
334 // "Status<OptionalReturn> FunctionName("
335 absl::StrAppend(&out, MapQualTypeReturn(context, return_type), " ",
336 function_name, "(");
337
338 struct ParameterInfo {
339 clang::QualType qual;
340 std::string name;
341 };
342 std::vector<ParameterInfo> params;
343
344 std::string print_separator;
345 for (int i = 0; i < decl->getNumParams(); ++i) {
346 const clang::ParmVarDecl* param = decl->getParamDecl(i);
347 if (param->getType()->isRecordType()) {
348 return MakeStatusWithDiagnostic(
349 param->getBeginLoc(), absl::StatusCode::kCancelled,
350 absl::StrCat("passing record parameter '",
351 ToStringView(param->getName()),
352 "' by value, skipping function"));
353 }
354
355 ParameterInfo& param_info = params.emplace_back();
356 param_info.qual = param->getType();
357 param_info.name = GetParamName(param, i);
358
359 absl::StrAppend(&out, print_separator);
360 print_separator = ", ";
361 absl::StrAppend(&out, MapQualTypeParameter(context, param_info.qual), " ",
362 param_info.name);
363 }
364
365 absl::StrAppend(&out, ") {\n");
366 absl::StrAppend(&out, MapQualType(context, return_type), " v_ret_;\n");
367 for (const auto& [qual, name] : params) {
368 if (!IsPointerOrReference(qual)) {
369 absl::StrAppend(&out, MapQualType(context, qual), " v_", name, "(", name,
370 ");\n");
371 }
372 }
373 absl::StrAppend(&out, "\nSAPI_RETURN_IF_ERROR(sandbox_->Call(\"",
374 function_name, "\", &v_ret_");
375 for (const auto& [qual, name] : params) {
376 absl::StrAppend(&out, ", ", IsPointerOrReference(qual) ? "" : "&v_", name);
377 }
378 absl::StrAppend(&out, "));\nreturn ",
379 (returns_void ? "::absl::OkStatus()" : "v_ret_.GetValue()"),
380 ";\n}\n");
381 return out;
382 }
383
EmitHeader(const std::vector<std::string> & function_definitions,const std::vector<const RenderedType * > & rendered_types,const GeneratorOptions & options)384 absl::StatusOr<std::string> EmitHeader(
385 const std::vector<std::string>& function_definitions,
386 const std::vector<const RenderedType*>& rendered_types,
387 const GeneratorOptions& options) {
388 std::string out;
389 const std::string include_guard = GetIncludeGuard(options.out_file);
390 absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
391 // When embedding the sandboxee, add embed header include
392 if (!options.embed_name.empty()) {
393 // Not using JoinPath() because even on Windows include paths use plain
394 // slashes.
395 std::string include_file(absl::StripSuffix(
396 absl::StrReplaceAll(options.embed_dir, {{"\\", "/"}}), "/"));
397 if (!include_file.empty()) {
398 absl::StrAppend(&include_file, "/");
399 }
400 absl::StrAppend(&include_file, options.embed_name);
401 absl::StrAppendFormat(&out, kEmbedInclude, include_file);
402 }
403
404 // If specified, wrap the generated API in a namespace
405 if (options.has_namespace()) {
406 absl::StrAppendFormat(&out, kNamespaceBeginTemplate,
407 options.namespace_name);
408 }
409
410 // Emit type dependencies
411 if (!rendered_types.empty()) {
412 absl::StrAppend(&out, "// Types this API depends on\n");
413 std::string last_ns_name = options.namespace_name;
414 for (const RenderedType* rt : rendered_types) {
415 const auto& [ns_name, spelling] = *rt;
416 if (last_ns_name != ns_name) {
417 if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
418 absl::StrAppend(&out, "} // namespace ", last_ns_name, "\n\n");
419 }
420
421 if (!ns_name.empty() && ns_name != options.namespace_name) {
422 absl::StrAppend(&out, "namespace ", ns_name, " {\n");
423 }
424 last_ns_name = ns_name;
425 }
426
427 absl::StrAppend(&out, spelling, ";\n");
428 }
429 if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
430 absl::StrAppend(&out, "} // namespace ", last_ns_name, "\n\n");
431 }
432 }
433
434 // Optionally emit a default sandbox that instantiates an embedded sandboxee
435 if (!options.embed_name.empty()) {
436 // TODO(cblichmann): Make the "Sandbox" suffix configurable.
437 absl::StrAppendFormat(
438 &out, kEmbedClassTemplate, absl::StrCat(options.name, "Sandbox"),
439 absl::StrReplaceAll(options.embed_name, {{"-", "_"}}));
440 }
441
442 // Emit the actual Sandboxed API
443 // TODO(cblichmann): Make the "Api" suffix configurable or at least optional.
444 absl::StrAppendFormat(&out, kClassHeaderTemplate,
445 absl::StrCat(options.name, "Api"));
446 absl::StrAppend(&out, absl::StrJoin(function_definitions, "\n"));
447 absl::StrAppend(&out, kClassFooterTemplate);
448
449 // Close out the header: close namespace (if needed) and end include guard
450 if (options.has_namespace()) {
451 absl::StrAppendFormat(&out, kNamespaceEndTemplate, options.namespace_name);
452 }
453 absl::StrAppendFormat(&out, kHeaderEpilog, include_guard);
454 return out;
455 }
456
EmitType(clang::TypeDecl * type_decl)457 void Emitter::EmitType(clang::TypeDecl* type_decl) {
458 if (!type_decl) {
459 return;
460 }
461
462 // Skip types defined in system headers.
463 // TODO(cblichmann): Instead of this and the hard-coded entities below, we
464 // should map types and add the correct (system) headers to
465 // the generated output.
466 if (type_decl->getASTContext().getSourceManager().isInSystemHeader(
467 type_decl->getBeginLoc())) {
468 return;
469 }
470
471 const std::vector<std::string> ns_path = GetNamespacePath(type_decl);
472 std::string ns_name;
473 if (!ns_path.empty()) {
474 const auto& ns_root = ns_path.front();
475 // Filter out declarations from the C++ standard library, from SAPI itself
476 // and from other well-known namespaces.
477 if (ns_root == "std" || ns_root == "__gnu_cxx" || ns_root == "sapi") {
478 return;
479 }
480 if (ns_root == "absl") {
481 // Skip Abseil internal namespaces
482 if (ns_path.size() > 1 && absl::EndsWith(ns_path[1], "_internal")) {
483 return;
484 }
485 // Skip types from Abseil that will already be included in the generated
486 // header.
487 if (auto name = ToStringView(type_decl->getName());
488 name == "CordMemoryAccounting" || name == "Duration" ||
489 name == "LogEntry" || name == "LogSeverity" || name == "Span" ||
490 name == "StatusCode" || name == "StatusToStringMode" ||
491 name == "SynchLocksHeld" || name == "SynchWaitParams" ||
492 name == "Time" || name == "string_view" || name == "tid_t") {
493 return;
494 }
495 }
496 // Skip Protocol Buffers namespaces
497 if (ns_root == "google" && ns_path.size() > 1 && ns_path[1] == "protobuf") {
498 return;
499 }
500 ns_name = absl::StrJoin(ns_path, "::");
501 }
502
503 std::string spelling = GetSpelling(type_decl);
504 if (const auto& [it, inserted] = rendered_types_.emplace(ns_name, spelling);
505 inserted) {
506 rendered_types_ordered_.push_back(&*it);
507 }
508 }
509
AddTypeDeclarations(const std::vector<clang::TypeDecl * > & type_decls)510 void Emitter::AddTypeDeclarations(
511 const std::vector<clang::TypeDecl*>& type_decls) {
512 for (clang::TypeDecl* type_decl : type_decls) {
513 EmitType(type_decl);
514 }
515 }
516
AddFunction(clang::FunctionDecl * decl)517 absl::Status Emitter::AddFunction(clang::FunctionDecl* decl) {
518 if (rendered_functions_.insert(decl->getQualifiedNameAsString()).second) {
519 SAPI_ASSIGN_OR_RETURN(std::string function, EmitFunction(decl));
520 rendered_functions_ordered_.push_back(function);
521 }
522 return absl::OkStatus();
523 }
524
EmitHeader(const GeneratorOptions & options)525 absl::StatusOr<std::string> Emitter::EmitHeader(
526 const GeneratorOptions& options) {
527 SAPI_ASSIGN_OR_RETURN(const std::string header,
528 ::sapi::EmitHeader(rendered_functions_ordered_,
529 rendered_types_ordered_, options));
530 return internal::ReformatGoogleStyle(options.out_file, header);
531 }
532
533 } // namespace sapi
534