mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Add sandboxee embedding
- Implement `--sapi_embed_name` and `--sapi_embed_dir` flags - Do not emit full AST-serialization for C++ classes
This commit is contained in:
parent
ae473c4bd1
commit
507eb00a90
|
@ -17,9 +17,11 @@
|
|||
#include "absl/random/random.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
|
@ -57,21 +59,13 @@ std::string GetIncludeGuard(absl::string_view filename) {
|
|||
return guard;
|
||||
}
|
||||
|
||||
// Returns the components of a declaration's qualified name, excluding the
|
||||
// declaration itself.
|
||||
sapi::StatusOr<std::vector<std::string>> GetQualifiedNamePath(
|
||||
const clang::TypeDecl* decl) {
|
||||
// Returns the namespace components of a declaration's qualified name.
|
||||
std::vector<std::string> GetNamespacePath(const clang::TypeDecl* decl) {
|
||||
std::vector<std::string> comps;
|
||||
for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) {
|
||||
if (llvm::isa<clang::TranslationUnitDecl>(ctx)) {
|
||||
continue;
|
||||
if (const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx)) {
|
||||
comps.push_back(nd->getNameAsString());
|
||||
}
|
||||
const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx);
|
||||
if (!nd) {
|
||||
return MakeStatusWithDiagnostic(decl->getBeginLoc(),
|
||||
absl::StrCat("not in a namespace decl"));
|
||||
}
|
||||
comps.push_back(nd->getNameAsString());
|
||||
}
|
||||
std::reverse(comps.begin(), comps.end());
|
||||
return comps;
|
||||
|
@ -79,6 +73,15 @@ sapi::StatusOr<std::vector<std::string>> GetQualifiedNamePath(
|
|||
|
||||
// Serializes the given Clang AST declaration back into compilable source code.
|
||||
std::string PrintAstDecl(const clang::Decl* decl) {
|
||||
// TODO(cblichmann): Make types nicer
|
||||
// - Rewrite typedef to using
|
||||
// - Rewrite function pointers using std::add_pointer_t<>;
|
||||
|
||||
if (const auto* record = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
|
||||
// For C++ classes/structs, only emit a forward declaration.
|
||||
return absl::StrCat(record->isClass() ? "class " : "struct ",
|
||||
std::string(record->getName()));
|
||||
}
|
||||
std::string pretty;
|
||||
llvm::raw_string_ostream os(pretty);
|
||||
decl->print(os);
|
||||
|
@ -120,9 +123,11 @@ sapi::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
|||
const clang::QualType return_type = decl->getDeclaredReturnType();
|
||||
const bool returns_void = return_type->isVoidType();
|
||||
|
||||
const clang::ASTContext& context = decl->getASTContext();
|
||||
|
||||
// "Status<OptionalReturn> FunctionName("
|
||||
absl::StrAppend(&out, MapQualTypeReturn(return_type), " ", function_name,
|
||||
"(");
|
||||
absl::StrAppend(&out, MapQualTypeReturn(context, return_type), " ",
|
||||
function_name, "(");
|
||||
|
||||
struct ParameterInfo {
|
||||
clang::QualType qual;
|
||||
|
@ -134,20 +139,22 @@ sapi::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
|||
for (int i = 0; i < decl->getNumParams(); ++i) {
|
||||
const clang::ParmVarDecl* param = decl->getParamDecl(i);
|
||||
|
||||
ParameterInfo& pi = params.emplace_back();
|
||||
pi.qual = param->getType();
|
||||
pi.name = GetParamName(param, i);
|
||||
ParameterInfo& param_info = params.emplace_back();
|
||||
param_info.qual = param->getType();
|
||||
param_info.name = GetParamName(param, i);
|
||||
|
||||
absl::StrAppend(&out, print_separator);
|
||||
print_separator = ", ";
|
||||
absl::StrAppend(&out, MapQualTypeParameter(pi.qual), " ", pi.name);
|
||||
absl::StrAppend(&out, MapQualTypeParameter(context, param_info.qual), " ",
|
||||
param_info.name);
|
||||
}
|
||||
|
||||
absl::StrAppend(&out, ") {\n");
|
||||
absl::StrAppend(&out, MapQualType(return_type), " v_ret_;\n");
|
||||
absl::StrAppend(&out, MapQualType(context, return_type), " v_ret_;\n");
|
||||
for (const auto& [qual, name] : params) {
|
||||
if (!IsPointerOrReference(qual)) {
|
||||
absl::StrAppend(&out, MapQualType(qual), " v_", name, "(", name, ");\n");
|
||||
absl::StrAppend(&out, MapQualType(context, qual), " v_", name, "(", name,
|
||||
");\n");
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&out, "\nSAPI_RETURN_IF_ERROR(sandbox_->Call(\"",
|
||||
|
@ -167,44 +174,77 @@ sapi::StatusOr<std::string> EmitHeader(
|
|||
std::string out;
|
||||
const std::string include_guard = GetIncludeGuard(options.out_file);
|
||||
absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
|
||||
|
||||
// When embedding the sandboxee, add embed header include
|
||||
if (!options.embed_name.empty()) {
|
||||
// Not using JoinPath() because even on Windows include paths use plain
|
||||
// slashes.
|
||||
std::string include_file(absl::StripSuffix(
|
||||
absl::StrReplaceAll(options.embed_dir, {{"\\", "/"}}), "/"));
|
||||
if (!include_file.empty()) {
|
||||
absl::StrAppend(&include_file, "/");
|
||||
}
|
||||
absl::StrAppend(&include_file, options.embed_name);
|
||||
absl::StrAppendFormat(&out, kEmbedInclude, include_file);
|
||||
}
|
||||
|
||||
// If specified, wrap the generated API in a namespace
|
||||
if (options.has_namespace()) {
|
||||
absl::StrAppendFormat(&out, kNamespaceBeginTemplate,
|
||||
options.namespace_name);
|
||||
}
|
||||
{
|
||||
std::string out_types = "// Types this API depends on\n";
|
||||
bool added_dependent_types = false;
|
||||
for (const clang::QualType& qual : types) {
|
||||
clang::TypeDecl* decl = nullptr;
|
||||
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
|
||||
decl = typedef_type->getDecl();
|
||||
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
decl = enum_type->getDecl();
|
||||
} else {
|
||||
decl = qual->getAsRecordDecl();
|
||||
}
|
||||
|
||||
if (decl) {
|
||||
SAPI_ASSIGN_OR_RETURN(auto ns, GetQualifiedNamePath(decl));
|
||||
if (!ns.empty()) {
|
||||
absl::StrAppend(&out_types, "namespace", (ns[0].empty() ? "" : " "),
|
||||
absl::StrJoin(ns, "::"), " {\n");
|
||||
}
|
||||
// TODO(cblichmann): Make types nicer
|
||||
// - Rewrite typedef to using
|
||||
// - Rewrite function pointers using std::add_pointer_t<>;
|
||||
absl::StrAppend(&out_types, PrintAstDecl(decl), ";");
|
||||
if (!ns.empty()) {
|
||||
absl::StrAppend(&out_types, "\n}");
|
||||
}
|
||||
absl::StrAppend(&out_types, "\n");
|
||||
added_dependent_types = true;
|
||||
// Emit type dependencies
|
||||
// TODO(cblichmann): Coalesce namespaces
|
||||
std::string out_types = "// Types this API depends on\n";
|
||||
bool added_types = false;
|
||||
for (const clang::QualType& qual : types) {
|
||||
clang::TypeDecl* decl = nullptr;
|
||||
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
|
||||
decl = typedef_type->getDecl();
|
||||
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
decl = enum_type->getDecl();
|
||||
} else {
|
||||
decl = qual->getAsRecordDecl();
|
||||
}
|
||||
if (!decl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> ns_path = GetNamespacePath(decl);
|
||||
std::string nested_ns_name;
|
||||
if (!ns_path.empty()) {
|
||||
if (const auto& ns_root = ns_path.front();
|
||||
ns_root == "std" || ns_root == "sapi" || ns_root == "__gnu_cxx") {
|
||||
// Filter out any and all declarations from the C++ standard library,
|
||||
// from SAPI itself and from other well-known namespaces. This avoids
|
||||
// re-declaring things like standard integer types, for example.
|
||||
continue;
|
||||
}
|
||||
nested_ns_name = absl::StrCat(ns_path[0].empty() ? "" : " ",
|
||||
absl::StrJoin(ns_path, "::"));
|
||||
absl::StrAppend(&out_types, "namespace", nested_ns_name, " {\n");
|
||||
}
|
||||
if (added_dependent_types) {
|
||||
absl::StrAppend(&out, out_types);
|
||||
absl::StrAppend(&out_types, PrintAstDecl(decl), ";");
|
||||
if (!ns_path.empty()) {
|
||||
absl::StrAppend(&out_types, "\n} // namespace", nested_ns_name);
|
||||
}
|
||||
absl::StrAppend(&out_types, "\n");
|
||||
added_types = true;
|
||||
}
|
||||
if (added_types) {
|
||||
absl::StrAppend(&out, out_types);
|
||||
}
|
||||
|
||||
// Optionally emit a default sandbox that instantiates an embedded sandboxee
|
||||
if (!options.embed_name.empty()) {
|
||||
// TODO(cblichmann): Make the "Sandbox" suffix configurable.
|
||||
absl::StrAppendFormat(
|
||||
&out, kEmbedClassTemplate, absl::StrCat(options.name, "Sandbox"),
|
||||
absl::StrReplaceAll(options.embed_name, {{"-", "_"}}));
|
||||
}
|
||||
|
||||
// Emit the actual Sandboxed API
|
||||
// TODO(cblichmann): Make the "Api" suffix configurable or at least optional.
|
||||
absl::StrAppendFormat(&out, kClassHeaderTemplate,
|
||||
absl::StrCat(options.name, "Api"));
|
||||
|
@ -215,6 +255,7 @@ sapi::StatusOr<std::string> EmitHeader(
|
|||
}
|
||||
absl::StrAppend(&out, kClassFooterTemplate);
|
||||
|
||||
// Close out the header: close namespace (if needed) and end include guard
|
||||
if (options.has_namespace()) {
|
||||
absl::StrAppendFormat(&out, kNamespaceEndTemplate, options.namespace_name);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ sapi::StatusOr<std::string> EmitHeader(
|
|||
// Common file prolog with auto-generation notice.
|
||||
// Note: The includes will be adjusted by Copybara when converting to/from
|
||||
// internal code. This is intentional.
|
||||
// Text template arguments:
|
||||
// 1. Header guard
|
||||
inline constexpr absl::string_view kHeaderProlog =
|
||||
R"(// AUTO-GENERATED by the Sandboxed API generator.
|
||||
// Edits will be discarded when regenerating this file.
|
||||
|
@ -65,6 +67,12 @@ inline constexpr absl::string_view kHeaderEpilog =
|
|||
R"(
|
||||
#endif // %1$s)";
|
||||
|
||||
// Text template arguments:
|
||||
// 1. Include for embedded sandboxee objects
|
||||
inline constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h"
|
||||
|
||||
)";
|
||||
|
||||
// Text template arguments:
|
||||
// 1. Namespace name
|
||||
inline constexpr absl::string_view kNamespaceBeginTemplate =
|
||||
|
@ -77,6 +85,18 @@ R"(
|
|||
} // namespace %1$s
|
||||
)";
|
||||
|
||||
// Text template arguments:
|
||||
// 1. Class name
|
||||
// 2. Embedded object identifier
|
||||
inline constexpr absl::string_view kEmbedClassTemplate = R"(
|
||||
// Sandbox with embedded sandboxee and default policy
|
||||
class %1$s : public ::sapi::Sandbox {
|
||||
public:
|
||||
%1$s() : ::sapi::Sandbox(%2$s_embed_create()) {}
|
||||
};
|
||||
|
||||
)";
|
||||
|
||||
// Text template arguments:
|
||||
// 1. Class name
|
||||
inline constexpr absl::string_view kClassHeaderTemplate = R"(
|
||||
|
|
|
@ -46,6 +46,8 @@ struct GeneratorOptions {
|
|||
std::string name; // Name of the Sandboxed API
|
||||
std::string namespace_name; // Namespace to wrap the SAPI in
|
||||
std::string out_file; // Output path of the generated header
|
||||
std::string embed_dir; // Directory with embedded includes
|
||||
std::string embed_name; // Identifier of the embed object
|
||||
};
|
||||
|
||||
class GeneratorASTVisitor
|
||||
|
@ -92,8 +94,6 @@ class GeneratorAction : public clang::ASTFrontendAction {
|
|||
: options_(options) {}
|
||||
|
||||
private:
|
||||
friend class GeneratorFactory;
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
||||
clang::CompilerInstance&, llvm::StringRef in_file) override {
|
||||
return absl::make_unique<GeneratorASTConsumer>(std::string(in_file),
|
||||
|
|
|
@ -83,6 +83,8 @@ GeneratorOptions GeneratorOptionsFromFlags() {
|
|||
options.name = *g_sapi_name;
|
||||
options.namespace_name = *g_sapi_ns;
|
||||
options.out_file = *g_sapi_out;
|
||||
options.embed_dir = *g_sapi_embed_dir;
|
||||
options.embed_name = *g_sapi_embed_name;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,24 @@ void GatherRelatedTypes(const clang::ASTContext& context, clang::QualType qual,
|
|||
}
|
||||
}
|
||||
|
||||
std::string MapQualType(clang::QualType qual) {
|
||||
namespace {
|
||||
|
||||
// Removes "const" from a qualified type if it denotes a pointer or reference
|
||||
// type.
|
||||
clang::QualType MaybeRemoveConst(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (IsPointerOrReference(qual)) {
|
||||
clang::QualType pointee_qual = qual->getPointeeType();
|
||||
pointee_qual.removeLocalConst();
|
||||
qual = context.getPointerType(pointee_qual);
|
||||
}
|
||||
return qual;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string MapQualType(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
|
||||
switch (builtin->getKind()) {
|
||||
case clang::BuiltinType::Void:
|
||||
|
@ -156,12 +173,18 @@ std::string MapQualType(clang::QualType qual) {
|
|||
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
return absl::StrCat("::sapi::v::IntBase<",
|
||||
enum_type->getDecl()->getQualifiedNameAsString(), ">");
|
||||
} else if (IsPointerOrReference(qual)) {
|
||||
// Remove "const" qualifier from a pointer or reference type's pointee, as
|
||||
// e.g. const pointers do not work well with SAPI.
|
||||
return absl::StrCat("::sapi::v::Reg<",
|
||||
MaybeRemoveConst(context, qual).getAsString(), ">");
|
||||
}
|
||||
// Best-effort mapping to "int"
|
||||
return "::sapi::v::Int";
|
||||
// Best-effort mapping to "int", leave a comment.
|
||||
return absl::StrCat("::sapi::v::Int /* aka '", qual.getAsString(), "' */");
|
||||
}
|
||||
|
||||
std::string MapQualTypeParameter(clang::QualType qual) {
|
||||
std::string MapQualTypeParameter(const clang::ASTContext& /*context*/,
|
||||
clang::QualType qual) {
|
||||
// TODO(cblichmann): Define additional mappings, as appropriate
|
||||
// _Bool -> bool
|
||||
// unsigned long long -> uint64_t (where applicable)
|
||||
|
@ -169,10 +192,14 @@ std::string MapQualTypeParameter(clang::QualType qual) {
|
|||
return IsPointerOrReference(qual) ? "::sapi::v::Ptr*" : qual.getAsString();
|
||||
}
|
||||
|
||||
std::string MapQualTypeReturn(clang::QualType qual) {
|
||||
return qual->isVoidType() ? "absl::Status"
|
||||
: absl::StrCat("::sapi::StatusOr<",
|
||||
MapQualTypeParameter(qual), ">");
|
||||
std::string MapQualTypeReturn(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (qual->isVoidType()) {
|
||||
return "absl::Status";
|
||||
}
|
||||
// Remove const qualifier like in MapQualType().
|
||||
return absl::StrCat("::sapi::StatusOr<",
|
||||
MaybeRemoveConst(context, qual).getAsString(), ">");
|
||||
}
|
||||
|
||||
} // namespace sapi
|
||||
|
|
|
@ -60,17 +60,19 @@ void GatherRelatedTypes(const clang::ASTContext& context, clang::QualType qual,
|
|||
// Maps a qualified type to a fully qualified SAPI-compatible type name. This
|
||||
// is used for the generated code that invokes the actual function call RPC.
|
||||
// If no mapping can be found, "int" is assumed.
|
||||
std::string MapQualType(clang::QualType qual);
|
||||
std::string MapQualType(const clang::ASTContext& context, clang::QualType qual);
|
||||
|
||||
// Maps a qualified type used as a function parameter to a type name compatible
|
||||
// with the generated Sandboxed API.
|
||||
std::string MapQualTypeParameter(clang::QualType qual);
|
||||
std::string MapQualTypeParameter(const clang::ASTContext& context,
|
||||
clang::QualType qual);
|
||||
|
||||
// Maps a qualified type used as a function return type to a type name
|
||||
// compatible with the generated Sandboxed API. Uses MapQualTypeParameter() and
|
||||
// compatible with the generated Sandboxed API. Uses MapQualTypeParameter() and
|
||||
// wraps the type in a ::sapi::StatusOr<> if qual is non-void. Otherwise returns
|
||||
// absl::Status.
|
||||
std::string MapQualTypeReturn(clang::QualType qual);
|
||||
std::string MapQualTypeReturn(const clang::ASTContext& context,
|
||||
clang::QualType qual);
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user