Skip to content

Commit 86334bb

Browse files
Sandboxed API Teamcopybara-github
authored andcommitted
Add support for SANDBOX_NULL_TERMINATED annotation.
This handles simple cases of `const char*` parameters that are null-terminated C strings and don't have a separate size parameter. We do not handle outputs to start with. PiperOrigin-RevId: 896024996 Change-Id: I5713924c89eadb2c6b67f625ee3db9d0875efd42
1 parent 1d16925 commit 86334bb

10 files changed

Lines changed: 112 additions & 14 deletions

File tree

sandboxed_api/annotations.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@
7272
#define SANDBOX_ELEM_SIZED_BY(elem_size_arg) \
7373
[[clang::annotate("sandbox", "elem_sized_by", #elem_size_arg)]]
7474

75+
// Pointer argument annotations that denote the pointee data is a
76+
// null-terminated C string. Only `const char*` parameters are supported.
77+
// The annotation does not support return types for now.
78+
//
79+
// For example:
80+
// int my_open(const char* path SANDBOX_IN_PTR SANDBOX_NULL_TERMINATED);
81+
#define SANDBOX_NULL_TERMINATED \
82+
[[clang::annotate("sandbox", "null_terminated")]]
83+
7584
// Annotations for sandboxee and host thunks.
7685
// These just mean that we also emit these into the sandbox / host code.
7786
// They can be used to hook function calls.

sandboxed_api/annotations_unimplemented.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@
3939
// to the sandboxee.
4040
#define SANDBOX_CALLBACK [[clang::annotate("sandbox", "callback")]]
4141

42-
// TODO(b/491826252): Annotation for null-terminated strings.
43-
#define SANDBOX_NULL_TERMINATED \
44-
[[clang::annotate("sandbox", "null_terminated")]]
45-
4642
// TODO(b/491826267): Annotation for return values that alias a parameter.
4743
#define SANDBOX_ALIAS_PTR [[clang::annotate("sandbox", "alias_ptr")]]
4844

sandboxed_api/tests/testcases/lwbox_sandbox.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Since we can't use both SANDBOX_IGNORE_FUNCS and SANDBOX_FUNCS in the
44
// same file, we need a separate annotation file for SANDBOX_FUNCS.
55
SANDBOX_FUNCS(mylib_is_sandboxed, mylib_scalar_types, mylib_add, mylib_copy,
6-
mylib_copy_raw, mylib_take_enum, mylib_expected_syscall1,
7-
mylib_expected_syscall2, mylib_unexpected_syscall1,
8-
mylib_unexpected_syscall2, mylib_func_with_todo);
6+
mylib_copy_raw, mylib_strlen, mylib_take_enum,
7+
mylib_expected_syscall1, mylib_expected_syscall2,
8+
mylib_unexpected_syscall1, mylib_unexpected_syscall2,
9+
mylib_func_with_todo);

sandboxed_api/tests/testcases/replaced_library.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ void mylib_copy_raw(const char* src, char* dst, size_t size) {
5050
memcpy(dst, src, size);
5151
}
5252

53+
size_t mylib_strlen(const char* str) { return strlen(str); }
54+
5355
int mylib_add(int x, int y) { return x + y; }
5456

5557
// Sanitizer instrumentation may break argument value tracking.

sandboxed_api/tests/testcases/replaced_library.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void mylib_copy_raw(const char* src SANDBOX_IN_PTR SANDBOX_ELEM_SIZED_BY(size),
4040
char* dst SANDBOX_OUT_PTR SANDBOX_ELEM_SIZED_BY(size),
4141
size_t size);
4242

43+
size_t mylib_strlen(const char* str SANDBOX_IN_PTR SANDBOX_NULL_TERMINATED);
44+
4345
MyLibEnum mylib_take_enum(MyLibEnum e);
4446

4547
void mylib_expected_syscall1();

sandboxed_api/tests/testcases/replaced_library_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,12 @@ TEST(Test, Copy) {
4747
EXPECT_EQ(std::string(dst_buf, sizeof(dst_buf)), "telle");
4848
}
4949

50+
TEST(Test, NullTerminatedStrlen) {
51+
EXPECT_EQ(mylib_strlen(""), 0);
52+
EXPECT_EQ(mylib_strlen("hello"), 5);
53+
54+
char buf[12] = {'h', 'e', 'l', 'l', 'o', '\n', 't', 'h', 'e', 'r', 'e', '\0'};
55+
EXPECT_EQ(mylib_strlen(buf), 11);
56+
}
57+
5058
} // namespace

sandboxed_api/tools/clang_generator/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ cc_library(
5353
"@abseil-cpp//absl/container:flat_hash_map",
5454
"@abseil-cpp//absl/container:flat_hash_set",
5555
"@abseil-cpp//absl/container:node_hash_set",
56+
"@abseil-cpp//absl/functional:overload",
5657
"@abseil-cpp//absl/log",
5758
"@abseil-cpp//absl/log:die_if_null",
5859
"@abseil-cpp//absl/random",

sandboxed_api/tools/clang_generator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ target_link_libraries(sapi_generator PUBLIC
110110
absl::btree
111111
absl::flat_hash_set
112112
absl::node_hash_set
113+
absl::overload
113114
absl::random_random
114115
absl::status
115116
absl::statusor

sandboxed_api/tools/clang_generator/sandboxed_library_emitter.cc

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
#include <optional>
2121
#include <string>
2222
#include <utility>
23+
#include <variant>
2324
#include <vector>
2425

2526
#include "absl/container/flat_hash_set.h"
27+
#include "absl/functional/overload.h"
2628
#include "absl/log/log.h"
2729
#include "absl/status/status.h"
2830
#include "absl/status/statusor.h"
@@ -61,8 +63,10 @@ constexpr absl::string_view kWrapperPrefix = "sapi_wrapper_";
6163
// This can be used to strip the annotations from the input string.
6264
std::string StripAnnotations(const std::string& input) {
6365
static const auto* macros_no_args = new std::vector<std::string>{
64-
"SANDBOX_IN_PTR", "SANDBOX_OUT_PTR", "SANDBOX_INOUT_PTR",
65-
"SANDBOX_OPAQUE_PTR", "SANDBOX_HOST_OPAQUE_PTR", "SANDBOX_HOST_STATE_VAR",
66+
"SANDBOX_IN_PTR", "SANDBOX_OUT_PTR",
67+
"SANDBOX_INOUT_PTR", "SANDBOX_OPAQUE_PTR",
68+
"SANDBOX_HOST_OPAQUE_PTR", "SANDBOX_HOST_STATE_VAR",
69+
"SANDBOX_NULL_TERMINATED",
6670
};
6771
std::string output = input;
6872
for (const auto& macro : *macros_no_args) {
@@ -339,6 +343,29 @@ struct StringViewArg : SandboxedLibraryEmitter::Arg {
339343
}
340344
};
341345

346+
// A null-terminated C string. These are always "input" to the library.
347+
struct ConstCStrArg : SandboxedLibraryEmitter::Arg {
348+
ConstCStrArg(absl::string_view name, PointerDir ptr_dir)
349+
: Arg(name, "const char*") {
350+
if (ptr_dir != PointerDir::kIn) {
351+
LOG(FATAL) << "ConstCStrArg pointer direction must be kIn";
352+
}
353+
}
354+
355+
std::string EmitHostPreCall() const override {
356+
return absl::Substitute("sapi::v::ConstCStr sapi_tmp_$0($0);\n", name_);
357+
}
358+
std::string EmitHostArgs() const override {
359+
return absl::Substitute("sapi_tmp_$0.PtrBefore()", name_);
360+
}
361+
std::string EmitSandboxeeParams() const override {
362+
return absl::Substitute("$0 $1", type_, name_);
363+
}
364+
std::string EmitSandboxeeArgs() const override {
365+
return absl::Substitute("$0", name_);
366+
}
367+
};
368+
342369
// Handles in/out/inout pointers (singular and arrays).
343370
struct PointerArg : SandboxedLibraryEmitter::Arg {
344371
PointerArg(absl::string_view name, absl::string_view type, PointerDir ptr_dir,
@@ -1090,8 +1117,14 @@ SandboxedLibraryEmitter::ConvertImpl(absl::string_view name,
10901117
// Check whether this pointer even needs syncing or is an opaque handle.
10911118
if (annotations.ptr_dir == PointerDir::kSandboxOpaque ||
10921119
annotations.ptr_dir == PointerDir::kHostOpaque) {
1120+
// Shouldn't be elem_sized_by or null_terminated.
1121+
if (!std::holds_alternative<std::monostate>(annotations.size_type)) {
1122+
return absl::InvalidArgumentError(absl::Substitute(
1123+
"pointer argument $0 is opaque and should not be sized (kind $1)",
1124+
name, annotations.size_type.index()));
1125+
}
10931126
return std::make_unique<PointerArg>(name, type_name, *annotations.ptr_dir,
1094-
annotations.elem_sized_by);
1127+
std::nullopt);
10951128
}
10961129

10971130
if (!type->getPointeeType()->isArithmeticType()) {
@@ -1109,8 +1142,36 @@ SandboxedLibraryEmitter::ConvertImpl(absl::string_view name,
11091142
return absl::InvalidArgumentError(
11101143
absl::Substitute("pointer argument $0 has unknown direction", name));
11111144
}
1112-
return std::make_unique<PointerArg>(name, type_name, *ptr_dir,
1113-
annotations.elem_sized_by);
1145+
return std::visit(
1146+
absl::Overload{[&](const std::monostate&)
1147+
-> absl::StatusOr<SandboxedLibraryEmitter::ArgPtr> {
1148+
return std::make_unique<PointerArg>(
1149+
name, type_name, *ptr_dir, std::nullopt);
1150+
},
1151+
[&](const ElemSizedBy& elem_sized_by)
1152+
-> absl::StatusOr<SandboxedLibraryEmitter::ArgPtr> {
1153+
return std::make_unique<PointerArg>(
1154+
name, type_name, *ptr_dir, elem_sized_by.expr);
1155+
},
1156+
[&](const NullTerminated&)
1157+
-> absl::StatusOr<SandboxedLibraryEmitter::ArgPtr> {
1158+
if (ptr_dir != PointerDir::kIn) {
1159+
return absl::InvalidArgumentError(absl::Substitute(
1160+
"pointer argument $0 is null-terminated but not "
1161+
"an input pointer ($1)",
1162+
name, *ptr_dir));
1163+
}
1164+
// For now only handle `const char*` (vs `char*`)
1165+
if (!type->getPointeeType()->isCharType() ||
1166+
!type->getPointeeType().isConstQualified()) {
1167+
return absl::InvalidArgumentError(absl::Substitute(
1168+
"pointer argument $0 is null-terminated but not "
1169+
"a const char*",
1170+
name));
1171+
}
1172+
return std::make_unique<ConstCStrArg>(name, *ptr_dir);
1173+
}},
1174+
annotations.size_type);
11141175
}
11151176
return nullptr;
11161177
}
@@ -1173,9 +1234,19 @@ SandboxedLibraryEmitter::ParseAnnotations(absl::string_view name,
11731234
annotations.ptr_dir = PointerDir::kHostOpaque;
11741235
} else if (ann.name == "elem_sized_by") {
11751236
num_args = 2;
1237+
if (!std::holds_alternative<std::monostate>(annotations.size_type)) {
1238+
return absl::InvalidArgumentError(absl::Substitute(
1239+
"arg $0: cannot be both null-terminated and elem_sized_by", name));
1240+
}
11761241
if (!ann.args.empty()) {
1177-
annotations.elem_sized_by = ann.args[0];
1242+
annotations.size_type = ElemSizedBy{ann.args[0]};
1243+
}
1244+
} else if (ann.name == "null_terminated") {
1245+
if (!std::holds_alternative<std::monostate>(annotations.size_type)) {
1246+
return absl::InvalidArgumentError(absl::Substitute(
1247+
"arg $0: cannot be both null-terminated and elem_sized_by", name));
11781248
}
1249+
annotations.size_type = NullTerminated{};
11791250
} else {
11801251
num_args = 0;
11811252
}

sandboxed_api/tools/clang_generator/sandboxed_library_emitter.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <memory>
1919
#include <optional>
2020
#include <string>
21+
#include <variant>
2122
#include <vector>
2223

2324
#include "absl/container/flat_hash_map.h"
@@ -89,9 +90,15 @@ class SandboxedLibraryEmitter : public EmitterBase {
8990
~Func();
9091
};
9192

93+
struct ElemSizedBy {
94+
std::string expr;
95+
};
96+
97+
struct NullTerminated {};
98+
9299
struct Annotations {
93100
std::optional<PointerDir> ptr_dir;
94-
std::optional<std::string> elem_sized_by;
101+
std::variant<std::monostate, ElemSizedBy, NullTerminated> size_type;
95102
};
96103

97104
absl::Status AddFunction(clang::FunctionDecl* decl) override;

0 commit comments

Comments
 (0)