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