diff --git a/.bazelrc b/.bazelrc
index ab9a27f8..6e130159 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -80,10 +80,10 @@ build:zig-cc-linux-aarch64 --test_env=QEMU_LD_PREFIX=/usr/aarch64-linux-gnu/
 
 build --enable_platform_specific_config
 
-# Use C++17.
-build:linux --cxxopt=-std=c++17
-build:macos --cxxopt=-std=c++17
-build:windows --cxxopt="/std:c++17"
+# Use C++20.
+build:linux --cxxopt=-std=c++20 --host_cxxopt=-std=c++20
+build:macos --cxxopt=-std=c++20 --host_cxxopt=-std=c++20
+build:windows --cxxopt="/std:c++20" --host_cxxopt="/std:c++20"
 
 # Enable symlinks and runfiles on Windows (enabled by default on other platforms).
 startup --windows_enable_symlinks
diff --git a/.clang-tidy b/.clang-tidy
index ceab9ad4..ea657db5 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -19,5 +19,6 @@ Checks:            clang-*,
                    -readability-magic-numbers,
                    -readability-make-member-function-const,
                    -readability-simplify-boolean-expr,
+                   -clang-diagnostic-builtin-macro-redefined,
 
 WarningsAsErrors:  '*'
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index de71b24c..3aa8cf89 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -41,7 +41,7 @@ jobs:
   addlicense:
     name: verify licenses
 
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
 
     steps:
     - uses: actions/checkout@v2
@@ -61,7 +61,7 @@ jobs:
   buildifier:
     name: check format with buildifier
 
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
 
     steps:
     - uses: actions/checkout@v2
@@ -99,29 +99,29 @@ jobs:
   clang_format:
     name: check format with clang-format
 
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
 
     steps:
     - uses: actions/checkout@v2
 
     - name: Install dependencies (Linux)
-      run: sudo apt update -y && sudo apt install -y clang-format-12
+      run: sudo apt update -y && sudo apt install -y clang-format
 
     - name: Format (clang-format)
       run: |
-        find . -name "*.h" -o -name "*.cc" -o -name "*.proto" | grep -v ".pb." | xargs -n1 clang-format-12 -i
+        find . -name "*.h" -o -name "*.cc" -o -name "*.proto" | grep -v ".pb." | xargs -n1 clang-format -i
         git diff --exit-code
 
   clang_tidy:
     name: check format with clang-tidy
 
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
 
     steps:
     - uses: actions/checkout@v2
 
     - name: Install dependencies (Linux)
-      run: sudo apt update -y && sudo apt install -y clang-tidy-12 lld-12 && sudo ln -sf /usr/bin/lld-12 /usr/bin/lld
+      run: sudo apt update -y && sudo apt install -y clang-tidy lld
 
     - name: Bazel cache
       uses: PiotrSikora/cache@v2.1.7-with-skip-cache
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 66ec91bf..548c099a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -118,7 +118,7 @@ jobs:
           os: ubuntu-22.04
           arch: x86_64
           action: test
-          flags: --config=clang-asan-strict --define=crypto=system
+          flags: --config=clang-asan --define=crypto=system
         - name: 'NullVM on Linux/x86_64 with TSan'
           engine: 'null'
           os: ubuntu-22.04
@@ -235,7 +235,7 @@ jobs:
           os: ubuntu-22.04
           arch: x86_64
           action: test
-          flags: --config=clang-asan-strict --define=crypto=system
+          flags: --config=clang-asan --define=crypto=system
         - name: 'Wasmtime on Linux/aarch64'
           engine: 'wasmtime'
           repo: 'com_github_bytecodealliance_wasmtime'
diff --git a/bazel/external/googletest.patch b/bazel/external/googletest.patch
deleted file mode 100644
index 502ef58b..00000000
--- a/bazel/external/googletest.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/BUILD.bazel b/BUILD.bazel
-index 8099642a85..3598661079 100644
---- a/BUILD.bazel
-+++ b/BUILD.bazel
-@@ -40,7 +40,7 @@ exports_files(["LICENSE"])
-
- config_setting(
-     name = "windows",
--    constraint_values = ["@bazel_tools//platforms:windows"],
-+    constraint_values = ["@platforms//os:windows"],
- )
-
- config_setting(
diff --git a/bazel/external/proxy_wasm_cpp_sdk.patch b/bazel/external/proxy_wasm_cpp_sdk.patch
new file mode 100644
index 00000000..c9ef490c
--- /dev/null
+++ b/bazel/external/proxy_wasm_cpp_sdk.patch
@@ -0,0 +1,33 @@
+# Address g++ -std=c++20 variadic template compilation issue:
+# https://github.com/proxy-wasm/proxy-wasm-cpp-host/pull/411#issuecomment-2295429152
+
+diff --git a/proxy_wasm_api.h b/proxy_wasm_api.h
+index 166b49c..263d2b9 100644
+--- a/proxy_wasm_api.h
++++ b/proxy_wasm_api.h
+@@ -1207,7 +1207,7 @@ struct SimpleHistogram {
+ template <typename... Tags> struct Counter : public MetricBase {
+   static Counter<Tags...> *New(std::string_view name, MetricTagDescriptor<Tags>... fieldnames);
+ 
+-  Counter<Tags...>(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
++  Counter(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
+       : Counter<Tags...>(std::string(name), std::vector<MetricTag>({toMetricTag(descriptors)...})) {
+   }
+ 
+@@ -1256,7 +1256,7 @@ inline Counter<Tags...> *Counter<Tags...>::New(std::string_view name,
+ template <typename... Tags> struct Gauge : public MetricBase {
+   static Gauge<Tags...> *New(std::string_view name, MetricTagDescriptor<Tags>... fieldnames);
+ 
+-  Gauge<Tags...>(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
++  Gauge(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
+       : Gauge<Tags...>(std::string(name), std::vector<MetricTag>({toMetricTag(descriptors)...})) {}
+ 
+   SimpleGauge resolve(Tags... f) {
+@@ -1302,6 +1302,6 @@ inline Gauge<Tags...> *Gauge<Tags...>::New(std::string_view name,
+ template <typename... Tags> struct Histogram : public MetricBase {
+   static Histogram<Tags...> *New(std::string_view name, MetricTagDescriptor<Tags>... fieldnames);
+ 
+-  Histogram<Tags...>(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
++  Histogram(std::string_view name, MetricTagDescriptor<Tags>... descriptors)
+       : Histogram<Tags...>(std::string(name),
+                            std::vector<MetricTag>({toMetricTag(descriptors)...})) {}
diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl
index 2586ed21..f31ac434 100644
--- a/bazel/repositories.bzl
+++ b/bazel/repositories.bzl
@@ -133,6 +133,8 @@ def proxy_wasm_cpp_host_repositories():
         sha256 = "89792fc1abca331f29f99870476a04146de5e82ff903bdffca90e6729c1f2470",
         strip_prefix = "proxy-wasm-cpp-sdk-95bb82ce45c41d9100fd1ec15d2ffc67f7f3ceee",
         urls = ["https://github.com/proxy-wasm/proxy-wasm-cpp-sdk/archive/95bb82ce45c41d9100fd1ec15d2ffc67f7f3ceee.tar.gz"],
+        patches = ["@proxy_wasm_cpp_host//bazel/external:proxy_wasm_cpp_sdk.patch"],
+        patch_args = ["-p1"],
     )
 
     # Test dependencies.
@@ -140,11 +142,9 @@ def proxy_wasm_cpp_host_repositories():
     maybe(
         http_archive,
         name = "com_google_googletest",
-        sha256 = "9dc9157a9a1551ec7a7e43daea9a694a0bb5fb8bec81235d8a1e6ef64c716dcb",
-        strip_prefix = "googletest-release-1.10.0",
-        urls = ["https://github.com/google/googletest/archive/release-1.10.0.tar.gz"],
-        patches = ["@proxy_wasm_cpp_host//bazel/external:googletest.patch"],
-        patch_args = ["-p1"],
+        sha256 = "7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926",
+        strip_prefix = "googletest-1.15.2",
+        urls = ["https://github.com/google/googletest/archive/refs/tags/v1.15.2.tar.gz"],
     )
 
     # NullVM dependencies.
diff --git a/test/utility.h b/test/utility.h
index 27b3b049..be60d321 100644
--- a/test/utility.h
+++ b/test/utility.h
@@ -197,4 +197,7 @@ class TestVm : public testing::TestWithParam<std::string> {
   std::string engine_;
 };
 
+// TODO: remove when #412 is fixed.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestVm);
+
 } // namespace proxy_wasm