• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <string>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_format.h"
27 #include "absl/strings/str_join.h"
28 #include "absl/strings/str_replace.h"
29 #include "absl/strings/str_split.h"
30 #include "absl/strings/string_view.h"
31 #include "absl/strings/strip.h"
32 #include "clang/AST/ASTContext.h"
33 #include "clang/AST/Decl.h"
34 #include "clang/AST/DeclBase.h"
35 #include "clang/AST/DeclCXX.h"
36 #include "clang/AST/Type.h"
37 #include "sandboxed_api/tools/clang_generator/diagnostics.h"
38 #include "sandboxed_api/tools/clang_generator/emitter_base.h"
39 #include "sandboxed_api/tools/clang_generator/generator.h"
40 #include "sandboxed_api/tools/clang_generator/types.h"
41 #include "sandboxed_api/util/status_macros.h"
42 
43 namespace sapi {
44 
45 // Common header description with auto-generation notice.
46 constexpr absl::string_view kHeaderDescription =
47     R"(// AUTO-GENERATED by the Sandboxed API generator.
48 // Edits will be discarded when regenerating this file.)";
49 
50 // Common header file prolog with auto-generation notice.
51 // Note: The includes will be adjusted by Copybara when converting to/from
52 //       internal code. This is intentional.
53 // Text template arguments:
54 //   1. Header guard
55 constexpr absl::string_view kHeaderIncludes =
56     R"(
57 #include <cstdint>
58 #include <type_traits>
59 
60 #include "absl/base/macros.h"
61 #include "absl/status/status.h"
62 #include "absl/status/statusor.h"
63 #include "sandboxed_api/sandbox.h"
64 #include "sandboxed_api/util/status_macros.h"
65 #include "sandboxed_api/vars.h"
66 
67 )";
68 
69 // Text template arguments:
70 //   1. Include for embedded sandboxee objects
71 constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h"
72 
73 )";
74 
75 // Text template arguments:
76 //   1. Class name
77 //   2. Embedded object identifier
78 constexpr absl::string_view kEmbedClassTemplate = R"(
79 // Sandbox with embedded sandboxee and default policy
80 class %1$s : public ::sapi::Sandbox {
81  public:
82   %1$s()
83       : ::sapi::Sandbox([]() {
84           static auto* fork_client_context =
85               new ::sapi::ForkClientContext(%2$s_embed_create());
86           return fork_client_context;
87         }()) {}
88 };
89 
90 )";
91 
92 // Sandboxed API class template.
93 // Text template arguments:
94 //   1. Class name
95 constexpr absl::string_view kClassHeaderTemplate = R"(
96 // Sandboxed API
97 class %1$s {
98  public:
99   explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {}
100 
101 
102   ABSL_DEPRECATED("Call sandbox() instead")
103   ::sapi::Sandbox* GetSandbox() const { return sandbox(); }
104   ::sapi::Sandbox* sandbox() const { return sandbox_; }
105 )";
106 
107 // Sandboxed API class template footer.
108 constexpr absl::string_view kClassFooterTemplate = R"(
109  private:
110   ::sapi::Sandbox* sandbox_;
111 };
112 )";
113 
114 // Returns a unique name for a parameter. If `decl` has no name, a unique name
115 // will be generated in the form of `unnamed<index>_`.
GetParamName(const clang::ParmVarDecl * decl,int index)116 std::string GetParamName(const clang::ParmVarDecl* decl, int index) {
117   if (std::string name = decl->getName().str(); !name.empty()) {
118     return absl::StrCat(name, "_");  // Suffix to avoid collisions
119   }
120   return absl::StrCat("unnamed", index, "_");
121 }
122 
123 // Returns a comment for the given function `decl` which represents the
124 // unsandboxed function signature.
PrintFunctionPrototypeComment(const clang::FunctionDecl * decl)125 absl::StatusOr<std::string> PrintFunctionPrototypeComment(
126     const clang::FunctionDecl* decl) {
127   const clang::ASTContext& context = decl->getASTContext();
128 
129   std::string out = absl::StrCat(
130       MapQualTypeParameterForCxx(context, decl->getDeclaredReturnType()), " ",
131       decl->getQualifiedNameAsString(), "(");
132 
133   std::string print_separator;
134   for (int i = 0; i < decl->getNumParams(); ++i) {
135     const clang::ParmVarDecl* param = decl->getParamDecl(i);
136 
137     absl::StrAppend(&out, print_separator);
138     print_separator = ", ";
139     absl::StrAppend(&out,
140                     MapQualTypeParameterForCxx(context, param->getType()));
141     if (std::string name = param->getName().str(); !name.empty()) {
142       absl::StrAppend(&out, " ", name);
143     }
144   }
145   absl::StrAppend(&out, ")");
146 
147   SAPI_ASSIGN_OR_RETURN(
148       std::string formatted,
149       internal::ReformatGoogleStyle(/*filename=*/"input", out, 75));
150   out.clear();
151   for (const auto& line : absl::StrSplit(formatted, '\n')) {
152     absl::StrAppend(&out, "// ", line, "\n");
153   }
154   return out;
155 }
156 
157 // Emits the given function `decl` as SAPI function with a leading comment
158 // documenting the unsandboxed function signature.
EmitFunction(const clang::FunctionDecl * decl)159 absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
160   const clang::QualType return_type = decl->getDeclaredReturnType();
161 
162   // Skip functions returning record by value.
163   if (return_type->isRecordType()) {
164     return MakeStatusWithDiagnostic(
165         decl->getBeginLoc(), absl::StatusCode::kCancelled,
166         "Returning record by value, skipping function.");
167   }
168 
169   SAPI_ASSIGN_OR_RETURN(std::string prototype,
170                         PrintFunctionPrototypeComment(decl));
171   std::string out;
172   absl::StrAppend(&out, "\n", prototype);
173 
174   auto function_name = ToStringView(decl->getName());
175   const bool returns_void = return_type->isVoidType();
176   const clang::ASTContext& context = decl->getASTContext();
177 
178   absl::StrAppend(&out, MapQualTypeReturn(context, return_type), " ",
179                   function_name, "(");
180 
181   struct ParameterInfo {
182     clang::QualType qual;
183     std::string name;
184   };
185   std::vector<ParameterInfo> params;
186 
187   // Process the function parameter list.
188   std::string print_separator;
189   for (int i = 0; i < decl->getNumParams(); ++i) {
190     const clang::ParmVarDecl* param = decl->getParamDecl(i);
191 
192     // Skip functions with record parameters passed by value.
193     if (param->getType()->isRecordType()) {
194       return MakeStatusWithDiagnostic(
195           param->getBeginLoc(), absl::StatusCode::kCancelled,
196           absl::StrCat("Passing record parameter '",
197                        ToStringView(param->getName()),
198                        "' by value, skipping function."));
199     }
200 
201     ParameterInfo& param_info = params.emplace_back();
202     param_info.qual = param->getType();
203     param_info.name = GetParamName(param, i);
204 
205     absl::StrAppend(&out, print_separator);
206     print_separator = ", ";
207     absl::StrAppend(&out, MapQualTypeParameter(context, param_info.qual), " ",
208                     param_info.name);
209   }
210 
211   absl::StrAppend(&out, ") {\n");
212 
213   // Declare the return value of the SAPI function.
214   absl::StrAppend(&out, MapQualType(context, return_type), " v_ret_;\n");
215 
216   // Declare the local variables for the parameters.
217   for (const auto& [qual, name] : params) {
218     if (!IsPointerOrReference(qual)) {
219       absl::StrAppend(&out, MapQualType(context, qual), " v_", name, "(", name,
220                       ");\n");
221     }
222   }
223 
224   // Call the sandboxed function.
225   absl::StrAppend(&out, "\nSAPI_RETURN_IF_ERROR(sandbox_->Call(\"",
226                   function_name, "\", &v_ret_");
227   for (const auto& [qual, name] : params) {
228     absl::StrAppend(&out, ", ", IsPointerOrReference(qual) ? "" : "&v_", name);
229   }
230 
231   // End the sandboxed function call and return `ok` if the unsandboxed function
232   // returns void, or else return the value of the SAPI function.
233   absl::StrAppend(&out, "));\nreturn ",
234                   (returns_void ? "::absl::OkStatus()" : "v_ret_.GetValue()"),
235                   ";\n}\n");
236   return out;
237 }
238 
239 // Emits the SAPI header.
EmitHeader(const std::vector<std::string> & function_definitions,const std::vector<const RenderedType * > & rendered_types,const GeneratorOptions & options)240 absl::StatusOr<std::string> EmitHeader(
241     const std::vector<std::string>& function_definitions,
242     const std::vector<const RenderedType*>& rendered_types,
243     const GeneratorOptions& options) {
244   // Log a warning message if the number of requested functions is not equal to
245   // the number of functions generated.
246   if (!options.function_names.empty() &&
247       (options.function_names.size() != function_definitions.size())) {
248     LOG(WARNING) << "Generated output has fewer functions than expected - some "
249                     "function signatures might use language features that "
250                     "SAPI does not support. For debugging, we recommend you "
251                     "compare the list of functions in your sapi_library() rule "
252                     "with the generated *.sapi.h file. Expected: "
253                  << options.function_names.size()
254                  << ", generated: " << function_definitions.size();
255   }
256   std::string out;
257   const std::string include_guard = GetIncludeGuard(options.out_file);
258   absl::StrAppend(&out, kHeaderDescription);
259   absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
260   absl::StrAppend(&out, kHeaderIncludes);
261 
262   // When embedding the sandboxee, add embed header include
263   if (!options.embed_name.empty()) {
264     // Not using JoinPath() because even on Windows include paths use plain
265     // slashes.
266     std::string include_file(absl::StripSuffix(
267         absl::StrReplaceAll(options.embed_dir, {{"\\", "/"}}), "/"));
268     if (!include_file.empty()) {
269       absl::StrAppend(&include_file, "/");
270     }
271     absl::StrAppend(&include_file, options.embed_name);
272     absl::StrAppendFormat(&out, kEmbedInclude, include_file);
273   }
274 
275   // If specified, wrap the generated API in a namespace
276   if (options.has_namespace()) {
277     absl::StrAppendFormat(&out, kNamespaceBeginTemplate,
278                           options.namespace_name);
279   }
280 
281   // Emit type dependencies
282   if (!rendered_types.empty()) {
283     absl::StrAppend(&out, "// Types this API depends on\n");
284     std::string last_ns_name = options.namespace_name;
285     for (const RenderedType* rt : rendered_types) {
286       const auto& [ns_name, spelling] = *rt;
287       if (last_ns_name != ns_name) {
288         if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
289           absl::StrAppend(&out, "}  // namespace ", last_ns_name, "\n\n");
290         }
291 
292         if (!ns_name.empty() && ns_name != options.namespace_name) {
293           absl::StrAppend(&out, "namespace ", ns_name, " {\n");
294         }
295         last_ns_name = ns_name;
296       }
297 
298       absl::StrAppend(&out, spelling, ";\n");
299     }
300     if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
301       absl::StrAppend(&out, "}  // namespace ", last_ns_name, "\n\n");
302     }
303   }
304 
305   // Optionally emit a default sandbox that instantiates an embedded sandboxee
306   if (!options.embed_name.empty()) {
307     absl::StrAppendFormat(
308         &out, kEmbedClassTemplate, absl::StrCat(options.name, "Sandbox"),
309         absl::StrReplaceAll(options.embed_name, {{"-", "_"}}));
310   }
311 
312   // Emit the actual Sandboxed API
313   absl::StrAppendFormat(&out, kClassHeaderTemplate,
314                         absl::StrCat(options.name, "Api"));
315   absl::StrAppend(&out, absl::StrJoin(function_definitions, "\n"));
316   absl::StrAppend(&out, kClassFooterTemplate);
317 
318   // Close out the header: close namespace (if needed) and end include guard
319   if (options.has_namespace()) {
320     absl::StrAppendFormat(&out, kNamespaceEndTemplate, options.namespace_name);
321   }
322   absl::StrAppendFormat(&out, kHeaderEpilog, include_guard);
323   return out;
324 }
325 
AddFunction(clang::FunctionDecl * decl)326 absl::Status Emitter::AddFunction(clang::FunctionDecl* decl) {
327   if (rendered_functions_.insert(decl->getQualifiedNameAsString()).second) {
328     SAPI_ASSIGN_OR_RETURN(std::string function, EmitFunction(decl));
329     rendered_functions_ordered_.push_back(function);
330   }
331   return absl::OkStatus();
332 }
333 
EmitHeader(const GeneratorOptions & options)334 absl::StatusOr<std::string> Emitter::EmitHeader(
335     const GeneratorOptions& options) {
336   SAPI_ASSIGN_OR_RETURN(const std::string header,
337                         ::sapi::EmitHeader(rendered_functions_ordered_,
338                                            rendered_types_ordered_, options));
339   return internal::ReformatGoogleStyle(options.out_file, header);
340 }
341 
342 }  // namespace sapi
343