Skip to content

Commit 1584ba1

Browse files
PiotrSikoraknm3000
authored andcommitted
Harden Pairs de/serialization. (proxy-wasm#305)
Reported by Chris Ertl from Google Security. Signed-off-by: Piotr Sikora <piotrsikora@google.com>
1 parent e4b7a8d commit 1584ba1

File tree

9 files changed

+332
-86
lines changed

9 files changed

+332
-86
lines changed

BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ cc_library(
5252
"src/bytecode_util.cc",
5353
"src/context.cc",
5454
"src/exports.cc",
55+
"src/pairs_util.cc",
5556
"src/shared_data.cc",
5657
"src/shared_data.h",
5758
"src/shared_queue.cc",
@@ -62,6 +63,7 @@ cc_library(
6263
],
6364
hdrs = [
6465
"include/proxy-wasm/bytecode_util.h",
66+
"include/proxy-wasm/pairs_util.h",
6567
"include/proxy-wasm/signature_util.h",
6668
],
6769
linkopts = select({

include/proxy-wasm/exports.h

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -62,36 +62,6 @@ struct RegisterForeignFunction {
6262

6363
namespace exports {
6464

65-
template <typename Pairs> size_t pairsSize(const Pairs &result) {
66-
size_t size = 4; // number of headers
67-
for (auto &p : result) {
68-
size += 8; // size of key, size of value
69-
size += p.first.size() + 1; // null terminated key
70-
size += p.second.size() + 1; // null terminated value
71-
}
72-
return size;
73-
}
74-
75-
template <typename Pairs> void marshalPairs(const Pairs &result, char *buffer) {
76-
char *b = buffer;
77-
*reinterpret_cast<uint32_t *>(b) = htowasm(result.size());
78-
b += sizeof(uint32_t);
79-
for (auto &p : result) {
80-
*reinterpret_cast<uint32_t *>(b) = htowasm(p.first.size());
81-
b += sizeof(uint32_t);
82-
*reinterpret_cast<uint32_t *>(b) = htowasm(p.second.size());
83-
b += sizeof(uint32_t);
84-
}
85-
for (auto &p : result) {
86-
memcpy(b, p.first.data(), p.first.size());
87-
b += p.first.size();
88-
*b++ = 0;
89-
memcpy(b, p.second.data(), p.second.size());
90-
b += p.second.size();
91-
*b++ = 0;
92-
}
93-
}
94-
9565
// ABI functions exported from host to wasm.
9666

9767
Word get_configuration(Word value_ptr_ptr, Word value_size_ptr);

include/proxy-wasm/limits.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@
3030
#ifndef PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES
3131
#define PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES (64 * 1024)
3232
#endif
33+
34+
// Maximum allowed size of Pairs buffer to deserialize.
35+
#ifndef PROXY_WASM_HOST_PAIRS_MAX_BYTES
36+
#define PROXY_WASM_HOST_PAIRS_MAX_BYTES (1024 * 1024)
37+
#endif
38+
39+
// Maximum allowed number of pairs in a Pairs buffer to deserialize.
40+
#ifndef PROXY_WASM_HOST_PAIRS_MAX_COUNT
41+
#define PROXY_WASM_HOST_PAIRS_MAX_COUNT 1024
42+
#endif

include/proxy-wasm/pairs_util.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2016-2019 Envoy Project Authors
2+
// Copyright 2020 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
#pragma once
17+
18+
#include <string>
19+
#include <string_view>
20+
#include <vector>
21+
22+
namespace proxy_wasm {
23+
24+
using Pairs = std::vector<std::pair<std::string_view, std::string_view>>;
25+
using StringPairs = std::vector<std::pair<std::string, std::string>>;
26+
27+
class PairsUtil {
28+
public:
29+
/**
30+
* pairsSize returns the buffer size required to serialize Pairs.
31+
* @param pairs Pairs to serialize.
32+
* @return size of the output buffer.
33+
*/
34+
static size_t pairsSize(const Pairs &pairs);
35+
36+
static size_t pairsSize(const StringPairs &stringpairs) {
37+
Pairs views(stringpairs.begin(), stringpairs.end());
38+
return pairsSize(views);
39+
}
40+
41+
/**
42+
* marshalPairs serializes Pairs to output buffer.
43+
* @param pairs Pairs to serialize.
44+
* @param buffer output buffer.
45+
* @param size size of the output buffer.
46+
* @return indicates whether serialization succeeded or not.
47+
*/
48+
static bool marshalPairs(const Pairs &pairs, char *buffer, size_t size);
49+
50+
static bool marshalPairs(const StringPairs &stringpairs, char *buffer, size_t size) {
51+
Pairs views(stringpairs.begin(), stringpairs.end());
52+
return marshalPairs(views, buffer, size);
53+
}
54+
55+
/**
56+
* toPairs deserializes input buffer to Pairs.
57+
* @param buffer serialized input buffer.
58+
* @return deserialized Pairs or an empty instance in case of deserialization failure.
59+
*/
60+
static Pairs toPairs(std::string_view buffer);
61+
};
62+
63+
} // namespace proxy_wasm

src/exports.cc

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// limitations under the License.
1515
//
1616
#include "include/proxy-wasm/limits.h"
17+
#include "include/proxy-wasm/pairs_util.h"
1718
#include "include/proxy-wasm/wasm.h"
1819

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

5960
namespace exports {
6061

61-
namespace {
62-
63-
Pairs toPairs(std::string_view buffer) {
64-
Pairs result;
65-
const char *b = buffer.data();
66-
if (buffer.size() < sizeof(uint32_t)) {
67-
return {};
68-
}
69-
auto size = wasmtoh(*reinterpret_cast<const uint32_t *>(b));
70-
b += sizeof(uint32_t);
71-
if (sizeof(uint32_t) + size * 2 * sizeof(uint32_t) > buffer.size()) {
72-
return {};
73-
}
74-
result.resize(size);
75-
for (uint32_t i = 0; i < size; i++) {
76-
result[i].first = std::string_view(nullptr, wasmtoh(*reinterpret_cast<const uint32_t *>(b)));
77-
b += sizeof(uint32_t);
78-
result[i].second = std::string_view(nullptr, wasmtoh(*reinterpret_cast<const uint32_t *>(b)));
79-
b += sizeof(uint32_t);
80-
}
81-
for (auto &p : result) {
82-
p.first = std::string_view(b, p.first.size());
83-
b += p.first.size() + 1;
84-
p.second = std::string_view(b, p.second.size());
85-
b += p.second.size() + 1;
86-
}
87-
return result;
88-
}
89-
90-
template <typename Pairs>
91-
bool getPairs(ContextBase *context, const Pairs &result, uint64_t ptr_ptr, uint64_t size_ptr) {
92-
if (result.empty()) {
93-
return context->wasm()->copyToPointerSize("", ptr_ptr, size_ptr);
94-
}
95-
uint64_t size = pairsSize(result);
96-
uint64_t ptr = 0;
97-
char *buffer = static_cast<char *>(context->wasm()->allocMemory(size, &ptr));
98-
marshalPairs(result, buffer);
99-
if (!context->wasmVm()->setWord(ptr_ptr, Word(ptr))) {
100-
return false;
101-
}
102-
if (!context->wasmVm()->setWord(size_ptr, Word(size))) {
103-
return false;
104-
}
105-
return true;
106-
}
107-
108-
} // namespace
109-
11062
// General ABI.
11163

11264
Word set_property(Word key_ptr, Word key_size, Word value_ptr, Word value_size) {
@@ -200,7 +152,7 @@ Word send_local_response(Word response_code, Word response_code_details_ptr,
200152
if (!details || !body || !additional_response_header_pairs) {
201153
return WasmResult::InvalidMemoryAccess;
202154
}
203-
auto additional_headers = toPairs(additional_response_header_pairs.value());
155+
auto additional_headers = PairsUtil::toPairs(additional_response_header_pairs.value());
204156
context->sendLocalResponse(response_code, body.value(), std::move(additional_headers),
205157
grpc_status, details.value());
206158
context->wasm()->stopNextIteration(true);
@@ -435,7 +387,25 @@ Word get_header_map_pairs(Word type, Word ptr_ptr, Word size_ptr) {
435387
if (result != WasmResult::Ok) {
436388
return result;
437389
}
438-
if (!getPairs(context, pairs, ptr_ptr, size_ptr)) {
390+
if (pairs.empty()) {
391+
if (!context->wasm()->copyToPointerSize("", ptr_ptr, size_ptr)) {
392+
return WasmResult::InvalidMemoryAccess;
393+
}
394+
return WasmResult::Ok;
395+
}
396+
uint64_t size = PairsUtil::pairsSize(pairs);
397+
uint64_t ptr = 0;
398+
char *buffer = static_cast<char *>(context->wasm()->allocMemory(size, &ptr));
399+
if (buffer == nullptr) {
400+
return WasmResult::InvalidMemoryAccess;
401+
}
402+
if (!PairsUtil::marshalPairs(pairs, buffer, size)) {
403+
return WasmResult::InvalidMemoryAccess;
404+
}
405+
if (!context->wasmVm()->setWord(ptr_ptr, Word(ptr))) {
406+
return WasmResult::InvalidMemoryAccess;
407+
}
408+
if (!context->wasmVm()->setWord(size_ptr, Word(size))) {
439409
return WasmResult::InvalidMemoryAccess;
440410
}
441411
return WasmResult::Ok;
@@ -451,7 +421,7 @@ Word set_header_map_pairs(Word type, Word ptr, Word size) {
451421
return WasmResult::InvalidMemoryAccess;
452422
}
453423
return context->setHeaderMapPairs(static_cast<WasmHeaderMapType>(type.u64_),
454-
toPairs(data.value()));
424+
PairsUtil::toPairs(data.value()));
455425
}
456426

457427
Word get_header_map_size(Word type, Word result_ptr) {
@@ -549,8 +519,8 @@ Word http_call(Word uri_ptr, Word uri_size, Word header_pairs_ptr, Word header_p
549519
if (!uri || !body || !header_pairs || !trailer_pairs) {
550520
return WasmResult::InvalidMemoryAccess;
551521
}
552-
auto headers = toPairs(header_pairs.value());
553-
auto trailers = toPairs(trailer_pairs.value());
522+
auto headers = PairsUtil::toPairs(header_pairs.value());
523+
auto trailers = PairsUtil::toPairs(trailer_pairs.value());
554524
uint32_t token = 0;
555525
// NB: try to write the token to verify the memory before starting the async
556526
// operation.
@@ -619,7 +589,7 @@ Word grpc_call(Word service_ptr, Word service_size, Word service_name_ptr, Word
619589
return WasmResult::InvalidMemoryAccess;
620590
}
621591
uint32_t token = 0;
622-
auto initial_metadata = toPairs(initial_metadata_pairs.value());
592+
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
623593
auto result = context->grpcCall(service.value(), service_name.value(), method_name.value(),
624594
initial_metadata, request.value(),
625595
std::chrono::milliseconds(timeout_milliseconds), &token);
@@ -645,7 +615,7 @@ Word grpc_stream(Word service_ptr, Word service_size, Word service_name_ptr, Wor
645615
return WasmResult::InvalidMemoryAccess;
646616
}
647617
uint32_t token = 0;
648-
auto initial_metadata = toPairs(initial_metadata_pairs.value());
618+
auto initial_metadata = PairsUtil::toPairs(initial_metadata_pairs.value());
649619
auto result = context->grpcStream(service.value(), service_name.value(), method_name.value(),
650620
initial_metadata, &token);
651621
if (result != WasmResult::Ok) {

0 commit comments

Comments
 (0)