Skip to content

Harden Pairs de/serialization. #305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ cc_library(
"src/bytecode_util.cc",
"src/context.cc",
"src/exports.cc",
"src/pairs_util.cc",
"src/shared_data.cc",
"src/shared_data.h",
"src/shared_queue.cc",
Expand All @@ -62,6 +63,7 @@ cc_library(
],
hdrs = [
"include/proxy-wasm/bytecode_util.h",
"include/proxy-wasm/pairs_util.h",
"include/proxy-wasm/signature_util.h",
],
linkopts = select({
Expand Down
30 changes: 0 additions & 30 deletions include/proxy-wasm/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,6 @@ struct RegisterForeignFunction {

namespace exports {

template <typename Pairs> size_t pairsSize(const Pairs &result) {
size_t size = 4; // number of headers
for (auto &p : result) {
size += 8; // size of key, size of value
size += p.first.size() + 1; // null terminated key
size += p.second.size() + 1; // null terminated value
}
return size;
}

template <typename Pairs> void marshalPairs(const Pairs &result, char *buffer) {
char *b = buffer;
*reinterpret_cast<uint32_t *>(b) = htowasm(result.size());
b += sizeof(uint32_t);
for (auto &p : result) {
*reinterpret_cast<uint32_t *>(b) = htowasm(p.first.size());
b += sizeof(uint32_t);
*reinterpret_cast<uint32_t *>(b) = htowasm(p.second.size());
b += sizeof(uint32_t);
}
for (auto &p : result) {
memcpy(b, p.first.data(), p.first.size());
b += p.first.size();
*b++ = 0;
memcpy(b, p.second.data(), p.second.size());
b += p.second.size();
*b++ = 0;
}
}

// ABI functions exported from host to wasm.

Word get_configuration(Word value_ptr_ptr, Word value_size_ptr);
Expand Down
10 changes: 10 additions & 0 deletions include/proxy-wasm/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@
#ifndef PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES
#define PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES (64 * 1024)
#endif

// Maximum allowed size of Pairs buffer to deserialize.
#ifndef PROXY_WASM_HOST_PAIRS_MAX_BYTES
#define PROXY_WASM_HOST_PAIRS_MAX_BYTES (1024 * 1024)
#endif

// Maximum allowed number of pairs in a Pairs buffer to deserialize.
#ifndef PROXY_WASM_HOST_PAIRS_MAX_COUNT
#define PROXY_WASM_HOST_PAIRS_MAX_COUNT 1024
#endif
63 changes: 63 additions & 0 deletions include/proxy-wasm/pairs_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2016-2019 Envoy Project Authors
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <string>
#include <string_view>
#include <vector>

namespace proxy_wasm {

using Pairs = std::vector<std::pair<std::string_view, std::string_view>>;
using StringPairs = std::vector<std::pair<std::string, std::string>>;

class PairsUtil {
public:
/**
* pairsSize returns the buffer size required to serialize Pairs.
* @param pairs Pairs to serialize.
* @return size of the output buffer.
*/
static size_t pairsSize(const Pairs &pairs);

static size_t pairsSize(const StringPairs &stringpairs) {
Pairs views(stringpairs.begin(), stringpairs.end());
return pairsSize(views);
}

/**
* marshalPairs serializes Pairs to output buffer.
* @param pairs Pairs to serialize.
* @param buffer output buffer.
* @param size size of the output buffer.
* @return indicates whether serialization succeeded or not.
*/
static bool marshalPairs(const Pairs &pairs, char *buffer, size_t size);

static bool marshalPairs(const StringPairs &stringpairs, char *buffer, size_t size) {
Pairs views(stringpairs.begin(), stringpairs.end());
return marshalPairs(views, buffer, size);
}

/**
* toPairs deserializes input buffer to Pairs.
* @param buffer serialized input buffer.
* @return deserialized Pairs or an empty instance in case of deserialization failure.
*/
static Pairs toPairs(std::string_view buffer);
};

} // namespace proxy_wasm
82 changes: 26 additions & 56 deletions src/exports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
//
#include "include/proxy-wasm/limits.h"
#include "include/proxy-wasm/pairs_util.h"
#include "include/proxy-wasm/wasm.h"

#include <openssl/rand.h>
Expand Down Expand Up @@ -58,55 +59,6 @@ RegisterForeignFunction::RegisterForeignFunction(const std::string &name, WasmFo

namespace exports {

namespace {

Pairs toPairs(std::string_view buffer) {
Pairs result;
const char *b = buffer.data();
if (buffer.size() < sizeof(uint32_t)) {
return {};
}
auto size = wasmtoh(*reinterpret_cast<const uint32_t *>(b));
b += sizeof(uint32_t);
if (sizeof(uint32_t) + size * 2 * sizeof(uint32_t) > buffer.size()) {
return {};
}
result.resize(size);
for (uint32_t i = 0; i < size; i++) {
result[i].first = std::string_view(nullptr, wasmtoh(*reinterpret_cast<const uint32_t *>(b)));
b += sizeof(uint32_t);
result[i].second = std::string_view(nullptr, wasmtoh(*reinterpret_cast<const uint32_t *>(b)));
b += sizeof(uint32_t);
}
for (auto &p : result) {
p.first = std::string_view(b, p.first.size());
b += p.first.size() + 1;
p.second = std::string_view(b, p.second.size());
b += p.second.size() + 1;
}
return result;
}

template <typename Pairs>
bool getPairs(ContextBase *context, const Pairs &result, uint64_t ptr_ptr, uint64_t size_ptr) {
if (result.empty()) {
return context->wasm()->copyToPointerSize("", ptr_ptr, size_ptr);
}
uint64_t size = pairsSize(result);
uint64_t ptr = 0;
char *buffer = static_cast<char *>(context->wasm()->allocMemory(size, &ptr));
marshalPairs(result, buffer);
if (!context->wasmVm()->setWord(ptr_ptr, Word(ptr))) {
return false;
}
if (!context->wasmVm()->setWord(size_ptr, Word(size))) {
return false;
}
return true;
}

} // namespace

// General ABI.

Word set_property(Word key_ptr, Word key_size, Word value_ptr, Word value_size) {
Expand Down Expand Up @@ -200,7 +152,7 @@ Word send_local_response(Word response_code, Word response_code_details_ptr,
if (!details || !body || !additional_response_header_pairs) {
return WasmResult::InvalidMemoryAccess;
}
auto additional_headers = toPairs(additional_response_header_pairs.value());
auto additional_headers = PairsUtil::toPairs(additional_response_header_pairs.value());
context->sendLocalResponse(response_code, body.value(), std::move(additional_headers),
grpc_status, details.value());
context->wasm()->stopNextIteration(true);
Expand Down Expand Up @@ -435,7 +387,25 @@ Word get_header_map_pairs(Word type, Word ptr_ptr, Word size_ptr) {
if (result != WasmResult::Ok) {
return result;
}
if (!getPairs(context, pairs, ptr_ptr, size_ptr)) {
if (pairs.empty()) {
if (!context->wasm()->copyToPointerSize("", ptr_ptr, size_ptr)) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
}
uint64_t size = PairsUtil::pairsSize(pairs);
uint64_t ptr = 0;
char *buffer = static_cast<char *>(context->wasm()->allocMemory(size, &ptr));
if (buffer == nullptr) {
return WasmResult::InvalidMemoryAccess;
}
if (!PairsUtil::marshalPairs(pairs, buffer, size)) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setWord(ptr_ptr, Word(ptr))) {
return WasmResult::InvalidMemoryAccess;
}
if (!context->wasmVm()->setWord(size_ptr, Word(size))) {
return WasmResult::InvalidMemoryAccess;
}
return WasmResult::Ok;
Expand All @@ -451,7 +421,7 @@ Word set_header_map_pairs(Word type, Word ptr, Word size) {
return WasmResult::InvalidMemoryAccess;
}
return context->setHeaderMapPairs(static_cast<WasmHeaderMapType>(type.u64_),
toPairs(data.value()));
PairsUtil::toPairs(data.value()));
}

Word get_header_map_size(Word type, Word result_ptr) {
Expand Down Expand Up @@ -549,8 +519,8 @@ Word http_call(Word uri_ptr, Word uri_size, Word header_pairs_ptr, Word header_p
if (!uri || !body || !header_pairs || !trailer_pairs) {
return WasmResult::InvalidMemoryAccess;
}
auto headers = toPairs(header_pairs.value());
auto trailers = toPairs(trailer_pairs.value());
auto headers = PairsUtil::toPairs(header_pairs.value());
auto trailers = PairsUtil::toPairs(trailer_pairs.value());
uint32_t token = 0;
// NB: try to write the token to verify the memory before starting the async
// operation.
Expand Down Expand Up @@ -619,7 +589,7 @@ Word grpc_call(Word service_ptr, Word service_size, Word service_name_ptr, Word
return WasmResult::InvalidMemoryAccess;
}
uint32_t token = 0;
auto initial_metadata = toPairs(initial_metadata_pairs.value());
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
auto result = context->grpcCall(service.value(), service_name.value(), method_name.value(),
initial_metadata, request.value(),
std::chrono::milliseconds(timeout_milliseconds), &token);
Expand All @@ -645,7 +615,7 @@ Word grpc_stream(Word service_ptr, Word service_size, Word service_name_ptr, Wor
return WasmResult::InvalidMemoryAccess;
}
uint32_t token = 0;
auto initial_metadata = toPairs(initial_metadata_pairs.value());
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
auto result = context->grpcStream(service.value(), service_name.value(), method_name.value(),
initial_metadata, &token);
if (result != WasmResult::Ok) {
Expand Down
Loading