Skip to content
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
1 change: 0 additions & 1 deletion include/proxy-wasm/null_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ struct NullVm : public WasmVm {
bool setWord(uint64_t pointer, Word data) override;
bool getWord(uint64_t pointer, Word *data) override;
size_t getWordSize() override;
std::string_view getCustomSection(std::string_view name) override;
std::string_view getPrecompiledSectionName() override;

#define _FORWARD_GET_FUNCTION(_T) \
Expand Down
8 changes: 0 additions & 8 deletions include/proxy-wasm/wasm_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,6 @@ class WasmVm {
*/
virtual size_t getWordSize() = 0;

/**
* Get the contents of the custom section with the given name or "" if it does not exist.
* @param name the name of the custom section to get.
* @return the contents of the custom section (if any). The result will be empty if there
* is no such section.
*/
virtual std::string_view getCustomSection(std::string_view name) = 0;

/**
* Get the name of the custom section that contains precompiled module.
* @return the name of the custom section that contains precompiled module.
Expand Down
244 changes: 244 additions & 0 deletions src/common/bytecode_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright 2021 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.

#include "src/common/bytecode_util.h"
#include <cstring>

namespace proxy_wasm {
namespace common {

bool BytecodeUtil::checkWasmHeader(std::string_view bytecode) {
// Wasm file header is 8 bytes (magic number + version).
static const uint8_t wasm_magic_number[4] = {0x00, 0x61, 0x73, 0x6d};
return bytecode.size() < 8 || !::memcmp(bytecode.data(), wasm_magic_number, 4);
}

bool BytecodeUtil::getAbiVersion(std::string_view bytecode, proxy_wasm::AbiVersion &ret) {
ret = proxy_wasm::AbiVersion::Unknown;
// Check Wasm header.
if (!checkWasmHeader(bytecode)) {
return false;
}

// Skip the Wasm header.
const char *pos = bytecode.data() + 8;
const char *end = bytecode.data() + bytecode.size();
while (pos < end) {
if (pos + 1 > end) {
return false;
}
const auto section_type = *pos++;
uint32_t section_len = 0;
if (!parseVarint(pos, end, section_len) || pos + section_len > end) {
return false;
}
if (section_type == 7 /* export section */) {
uint32_t export_vector_size = 0;
if (!parseVarint(pos, end, export_vector_size) || pos + export_vector_size > end) {
return false;
}
// Search thourgh exports.
for (uint32_t i = 0; i < export_vector_size; i++) {
// Parse name of the export.
uint32_t export_name_size = 0;
if (!parseVarint(pos, end, export_name_size) || pos + export_name_size > end) {
return false;
}
const auto name_begin = pos;
pos += export_name_size;
if (pos + 1 > end) {
return false;
}
// Check if it is a function type export
if (*pos++ == 0x00) {
const std::string export_name = {name_begin, export_name_size};
// Check the name of the function.
if (export_name == "proxy_abi_version_0_1_0") {
ret = AbiVersion::ProxyWasm_0_1_0;
return true;
} else if (export_name == "proxy_abi_version_0_2_0") {
ret = AbiVersion::ProxyWasm_0_2_0;
return true;
} else if (export_name == "proxy_abi_version_0_2_1") {
ret = AbiVersion::ProxyWasm_0_2_1;
return true;
}
}
// Skip export's index.
if (!parseVarint(pos, end, export_name_size)) {
return false;
}
}
return true;
} else {
pos += section_len;
}
}
return true;
}

bool BytecodeUtil::getCustomSection(std::string_view bytecode, std::string_view name,
std::string_view &ret) {
// Check Wasm header.
if (!checkWasmHeader(bytecode)) {
return false;
}

// Skip the Wasm header.
const char *pos = bytecode.data() + 8;
const char *end = bytecode.data() + bytecode.size();
while (pos < end) {
if (pos + 1 > end) {
return false;
}
const auto section_type = *pos++;
uint32_t section_len = 0;
if (!parseVarint(pos, end, section_len) || pos + section_len > end) {
return false;
}
if (section_type == 0) {
// Custom section.
const auto section_data_start = pos;
uint32_t section_name_len = 0;
if (!BytecodeUtil::parseVarint(pos, end, section_name_len) || pos + section_name_len > end) {
return false;
}
if (section_name_len == name.size() && ::memcmp(pos, name.data(), section_name_len) == 0) {
pos += section_name_len;
ret = {pos, static_cast<size_t>(section_data_start + section_len - pos)};
return true;
}
pos = section_data_start + section_len;
} else {
// Skip other sections.
pos += section_len;
}
}
return true;
};

bool BytecodeUtil::getFunctionNameIndex(std::string_view bytecode,
std::unordered_map<uint32_t, std::string> &ret) {
std::string_view name_section = {};
if (!BytecodeUtil::getCustomSection(bytecode, "name", name_section)) {
return false;
};
if (!name_section.empty()) {
const char *pos = name_section.data();
const char *end = name_section.data() + name_section.size();
while (pos < end) {
const auto subsection_id = *pos++;
uint32_t subsection_size = 0;
if (!parseVarint(pos, end, subsection_size) || pos + subsection_size > end) {
return false;
}

if (subsection_id != 1) {
// Skip other subsctions.
pos += subsection_size;
} else {
// Enters function name subsection.
const auto start = pos;
uint32_t namemap_vector_size = 0;
if (!parseVarint(pos, end, namemap_vector_size) || pos + namemap_vector_size > end) {
return false;
}
for (uint32_t i = 0; i < namemap_vector_size; i++) {
uint32_t func_index = 0;
if (!parseVarint(pos, end, func_index)) {
return false;
}

uint32_t func_name_size = 0;
if (!parseVarint(pos, end, func_name_size) || pos + func_name_size > end) {
return false;
}
ret.insert({func_index, std::string(pos, func_name_size)});
pos += func_name_size;
}
if (start + subsection_size != pos) {
return false;
}
}
}
}
return true;
}

bool BytecodeUtil::getStrippedSource(std::string_view bytecode, std::string &ret) {
// Check Wasm header.
if (!checkWasmHeader(bytecode)) {
return false;
}

// Skip the Wasm header.
const char *pos = bytecode.data() + 8;
const char *end = bytecode.data() + bytecode.size();
while (pos < end) {
const auto section_start = pos;
if (pos + 1 > end) {
return false;
}
const auto section_type = *pos++;
uint32_t section_len = 0;
if (!parseVarint(pos, end, section_len) || pos + section_len > end) {
return false;
}
if (section_type == 0 /* custom section */) {
const auto section_data_start = pos;
uint32_t section_name_len = 0;
if (!parseVarint(pos, end, section_name_len) || pos + section_name_len > end) {
return false;
}
auto section_name = std::string_view(pos, section_name_len);
if (section_name.find("precompiled_") != std::string::npos) {
// If this is the first "precompiled_" section, then save everything
// before it, otherwise skip it.
if (ret.empty()) {
const char *start = bytecode.data();
ret.append(start, section_start);
}
}
pos = section_data_start + section_len;
} else {
pos += section_len;
// Save this section if we already saw a custom "precompiled_" section.
if (!ret.empty()) {
ret.append(section_start, pos);
}
}
}
if (ret.empty()) {
// Copy the original source code if it is empty.
ret = std::string(bytecode);
}
return true;
}

bool BytecodeUtil::parseVarint(const char *&pos, const char *end, uint32_t &ret) {
uint32_t shift = 0;
char b;
do {
if (pos + 1 > end) {
return false;
}
b = *pos++;
ret += (b & 0x7f) << shift;
shift += 7;
} while ((b & 0x80) != 0);
return ret != static_cast<uint32_t>(-1);
}

} // namespace common
} // namespace proxy_wasm
77 changes: 77 additions & 0 deletions src/common/bytecode_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2021 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_view>
#include <vector>
#include <unordered_map>

#include "include/proxy-wasm/wasm_vm.h"

namespace proxy_wasm {
namespace common {

// Utilitiy functions which directly operate on Wasm bytecodes.
class BytecodeUtil {
public:
/**
* checkWasmHeader validates Wasm header.
* @param bytecode is the target bytecode.
* @return indicates whether the bytecode has valid Wasm header.
*/
static bool checkWasmHeader(std::string_view bytecode);

/**
* getAbiVersion extracts ABI version from the bytecode.
* @param bytecode is the target bytecode.
* @param ret is the reference to store the extracted ABI version or UnKnown if it doesn't exist.
* @return indicates whether parsing succeeded or not.
*/
static bool getAbiVersion(std::string_view bytecode, proxy_wasm::AbiVersion &ret);

/**
* getCustomSection extract the view of the custom section for a given name.
* @param bytecode is the target bytecode.
* @param name is the name of the custom section.
* @param ret is the reference to store the resulting view to the custom section.
* @return indicates whether parsing succeeded or not.
*/
static bool getCustomSection(std::string_view bytecode, std::string_view name,
std::string_view &ret);

/**
* getFunctionNameIndex constructs the map from function indexes to function names stored in
* the function name subsection in "name" custom section.
* See https://webassembly.github.io/spec/core/appendix/custom.html#binary-funcnamesec for detail.
* @param bytecode is the target bytecode.
* @param ret is the reference to store map from function indexes to function names.
* @return indicates whether parsing succeeded or not.
*/
static bool getFunctionNameIndex(std::string_view bytecode,
std::unordered_map<uint32_t, std::string> &ret);

/**
* getStrippedSource gets Wasm module without Custom Sections to save some memory in workers.
* @param bytecode is the original bytecode.
* @param ret is the reference to the stripped bytecode or a copy of the original bytecode.
* @return indicates whether parsing succeeded or not.
*/
static bool getStrippedSource(std::string_view bytecode, std::string &ret);

private:
static bool parseVarint(const char *&begin, const char *end, uint32_t &ret);
};

} // namespace common
} // namespace proxy_wasm
5 changes: 0 additions & 5 deletions src/null/null_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,6 @@ bool NullVm::getWord(uint64_t pointer, Word *data) {

size_t NullVm::getWordSize() { return sizeof(uint64_t); }

std::string_view NullVm::getCustomSection(std::string_view /* name */) {
// Return nothing: there is no WASM file.
return {};
}

std::string_view NullVm::getPrecompiledSectionName() {
// Return nothing: there is no WASM file.
return {};
Expand Down
Loading