diff --git a/include/proxy-wasm/limits.h b/include/proxy-wasm/limits.h index cb3e569e8..d02d4c5e3 100644 --- a/include/proxy-wasm/limits.h +++ b/include/proxy-wasm/limits.h @@ -23,3 +23,10 @@ #ifndef PROXY_WASM_HOST_MAX_WASM_MEMORY_SIZE_BYTES #define PROXY_WASM_HOST_MAX_WASM_MEMORY_SIZE_BYTES (1024 * 1024 * 1024) #endif + +// Maximum allowed random_get buffer size. This value is consistent with +// the JavaScript Crypto.getRandomValues() maximum buffer size. +// See: https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues +#ifndef PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES +#define PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES (64 * 1024) +#endif diff --git a/src/exports.cc b/src/exports.cc index c203946b8..01aeb9623 100644 --- a/src/exports.cc +++ b/src/exports.cc @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // +#include "include/proxy-wasm/limits.h" #include "include/proxy-wasm/wasm.h" #include @@ -884,6 +885,12 @@ Word wasi_unstable_clock_time_get(Word clock_id, uint64_t /*precision*/, // __wasi_errno_t __wasi_random_get(uint8_t *buf, size_t buf_len); Word wasi_unstable_random_get(Word result_buf_ptr, Word buf_len) { + if (buf_len > PROXY_WASM_HOST_WASI_RANDOM_GET_MAX_SIZE_BYTES) { + return 28; // __WASI_EINVAL + } + if (buf_len == 0) { + return 0; // __WASI_ESUCCESS + } auto *context = contextOrEffectiveContext(); std::vector random(buf_len); RAND_bytes(random.data(), random.size()); diff --git a/test/BUILD b/test/BUILD index 34481d83e..93373486d 100644 --- a/test/BUILD +++ b/test/BUILD @@ -74,8 +74,10 @@ cc_test( data = [ "//test/test_data:clock.wasm", "//test/test_data:env.wasm", + "//test/test_data:random.wasm", ], linkstatic = 1, + shard_count = 8, deps = [ ":utility_lib", "//:lib", diff --git a/test/exports_test.cc b/test/exports_test.cc index 40e0359d1..026019c03 100644 --- a/test/exports_test.cc +++ b/test/exports_test.cc @@ -89,5 +89,73 @@ TEST_P(TestVm, Clock) { EXPECT_TRUE(context->isLogged("realtime: ")); } +TEST_P(TestVm, RandomZero) { + auto source = readTestWasmFile("random.wasm"); + ASSERT_FALSE(source.empty()); + auto wasm = TestWasm(std::move(vm_)); + ASSERT_TRUE(wasm.load(source, false)); + ASSERT_TRUE(wasm.initialize()); + + WasmCallVoid<1> run; + wasm.wasm_vm()->getFunction("run", &run); + ASSERT_TRUE(run != nullptr); + run(wasm.vm_context(), Word{0}); + + // Check application logs. + auto *context = dynamic_cast(wasm.vm_context()); + EXPECT_TRUE(context->isLogged("random_get(0) succeeded.")); +} + +TEST_P(TestVm, RandomSmall) { + auto source = readTestWasmFile("random.wasm"); + ASSERT_FALSE(source.empty()); + auto wasm = TestWasm(std::move(vm_)); + ASSERT_TRUE(wasm.load(source, false)); + ASSERT_TRUE(wasm.initialize()); + + WasmCallVoid<1> run; + wasm.wasm_vm()->getFunction("run", &run); + ASSERT_TRUE(run != nullptr); + run(wasm.vm_context(), Word{32}); + + // Check application logs. + auto *context = dynamic_cast(wasm.vm_context()); + EXPECT_TRUE(context->isLogged("random_get(32) succeeded.")); +} + +TEST_P(TestVm, RandomLarge) { + auto source = readTestWasmFile("random.wasm"); + ASSERT_FALSE(source.empty()); + auto wasm = TestWasm(std::move(vm_)); + ASSERT_TRUE(wasm.load(source, false)); + ASSERT_TRUE(wasm.initialize()); + + WasmCallVoid<1> run; + wasm.wasm_vm()->getFunction("run", &run); + ASSERT_TRUE(run != nullptr); + run(wasm.vm_context(), Word{64 * 1024}); + + // Check application logs. + auto *context = dynamic_cast(wasm.vm_context()); + EXPECT_TRUE(context->isLogged("random_get(65536) succeeded.")); +} + +TEST_P(TestVm, RandomTooLarge) { + auto source = readTestWasmFile("random.wasm"); + ASSERT_FALSE(source.empty()); + auto wasm = TestWasm(std::move(vm_)); + ASSERT_TRUE(wasm.load(source, false)); + ASSERT_TRUE(wasm.initialize()); + + WasmCallVoid<1> run; + wasm.wasm_vm()->getFunction("run", &run); + ASSERT_TRUE(run != nullptr); + run(wasm.vm_context(), Word{65 * 1024}); + + // Check application logs. + auto *context = dynamic_cast(wasm.vm_context()); + EXPECT_TRUE(context->isLogged("random_get(66560) failed.")); +} + } // namespace } // namespace proxy_wasm diff --git a/test/test_data/BUILD b/test/test_data/BUILD index 3d09fa57e..553d43b14 100644 --- a/test/test_data/BUILD +++ b/test/test_data/BUILD @@ -53,3 +53,9 @@ wasm_rust_binary( srcs = ["clock.rs"], wasi = True, ) + +wasm_rust_binary( + name = "random.wasm", + srcs = ["random.rs"], + wasi = True, +) diff --git a/test/test_data/random.rs b/test/test_data/random.rs new file mode 100644 index 000000000..271434bb7 --- /dev/null +++ b/test/test_data/random.rs @@ -0,0 +1,38 @@ +// Copyright 2022 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. + +#[no_mangle] +pub extern "C" fn proxy_abi_version_0_2_0() {} + +#[no_mangle] +pub extern "C" fn proxy_on_memory_allocate(_: usize) -> *mut u8 { + std::ptr::null_mut() +} + +// TODO(PiotrSikora): switch to "getrandom" crate. +pub mod wasi_snapshot_preview1 { + #[link(wasm_import_module = "wasi_snapshot_preview1")] + extern "C" { + pub fn random_get(buf: *mut u8, buf_len: usize) -> u16; + } +} + +#[no_mangle] +pub extern "C" fn run(size: usize) { + let mut buf: Vec = Vec::with_capacity(size); + match unsafe { wasi_snapshot_preview1::random_get(buf.as_mut_ptr(), size) } { + 0 => println!("random_get({}) succeeded.", size), + _ => println!("random_get({}) failed.", size), + } +}