diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4d9279a..7f08cc3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -49,6 +49,13 @@
 For more detailed instructions, see the
 [Gerrit User Guide](https://gerrit-review.googlesource.com/Documentation/intro-user.html).
 
+As an alternative to pushing to `refs/for/main`: if you have Chromium's
+`depot_tools` installed, you can simply run `git cl upload` to upload a change.
+This also has the advantage of automatically running any relevant `PRESUBMIT.py`
+checks. See [depot_tools
+documentation](https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools.html)
+for more info.
+
 ### Copyright headers
 New files contributed directly to BoringSSL should use the following copyright
 header, where `YEAR` is the year the file was added:
diff --git a/crypto/internal.h b/crypto/internal.h
index 429f360..e1dcf223 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -1458,6 +1458,9 @@
 
 // CRYPTO_addc_* returns |x + y + carry|, and sets |*out_carry| to the carry
 // bit. |carry| must be zero or one.
+
+// NOTE: Unoptimized GCC builds may compile these builtins to non-constant-time
+// code. For correct constant-time behavior, ensure builds are optimized.
 #if OPENSSL_HAS_BUILTIN(__builtin_addc)
 
 inline unsigned int CRYPTO_addc_impl(unsigned int x, unsigned int y,
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
new file mode 100644
index 0000000..6d138da
--- /dev/null
+++ b/infra/config/PRESUBMIT.py
@@ -0,0 +1,24 @@
+# Copyright 2025 The BoringSSL Authors
+#
+# 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
+#
+#     https://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.
+
+"""Enforces consistency in lucicfg definitions for BoringSSL's CI and CQ.
+
+Run by the presubmit API in depot_tools, e.g. by running `git cl presubmit`.
+"""
+
+PRESUBMIT_VERSION = '2.0.0'
+
+def CheckLucicfgGenOutputMain(input_api, output_api):
+  return input_api.RunTests(input_api.canned_checks.CheckLucicfgGenOutput(
+      input_api, output_api, 'main.star'))
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 65e718b..a3eae35 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -24,6 +24,7 @@
     gerrit_cq_ability {
       committer_list: "project-boringssl-committers"
       dry_run_access_list: "project-boringssl-tryjob-access"
+      new_patchset_run_access_list: "project-boringssl-tryjob-access"
     }
     tryjob {
       builders {
@@ -167,6 +168,10 @@
         name: "boringssl/try/linux_rel"
       }
       builders {
+        name: "boringssl/try/linux_rust"
+        includable_only: true
+      }
+      builders {
         name: "boringssl/try/linux_sde"
         includable_only: true
       }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index d528ee9..58b2ae1 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -3309,6 +3309,32 @@
       }
     }
     builders {
+      name: "linux_rust"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.flex.try"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "cmake_args": {'
+        '    "RUST_BINDINGS": "x86_64-unknown-linux-gnu"'
+        '  },'
+        '  "recipe": "boringssl",'
+        '  "rust": true'
+        '}'
+      execution_timeout_secs: 1800
+      service_account: "boringssl-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
       name: "linux_sde"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "cpu:x86-64"
diff --git a/infra/config/generated/project.cfg b/infra/config/generated/project.cfg
index c7b762a..093f6e5 100644
--- a/infra/config/generated/project.cfg
+++ b/infra/config/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "boringssl"
 access: "group:all"
 lucicfg {
-  version: "1.45.3"
+  version: "1.45.6"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/main.star b/infra/config/main.star
index 07c3019..6cbe93c 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -45,6 +45,10 @@
             groups = "project-boringssl-tryjob-access",
         ),
         acl.entry(
+            roles = acl.CQ_NEW_PATCHSET_RUN_TRIGGERER,
+            groups = "project-boringssl-tryjob-access",
+        ),
+        acl.entry(
             roles = acl.SCHEDULER_OWNER,
             groups = "project-boringssl-admins",
         ),
@@ -126,6 +130,17 @@
         short_name = None,
         execution_timeout = None,
         properties = {}):
+    """Defines a CI builder.
+
+    Args:
+      name: The name to use for the builder.
+      host: The host to run on.
+      recipe: The recipe to run.
+      category: Category in which to display the builder in the console view.
+      short_name: The short name for the builder in the console view.
+      execution_timeout: Overrides the default timeout.
+      properties: Properties to pass to the recipe.
+    """
     dimensions = dict(host["dimensions"])
     dimensions["pool"] = "luci.flex.ci"
     caches = [swarming.cache("gocache"), swarming.cache("gopath")]
@@ -166,6 +181,17 @@
         cq_enabled = True,
         execution_timeout = None,
         properties = {}):
+    """Defines a CQ builder.
+
+    Args:
+      name: The name to use for the builder.
+      host: The host to run on.
+      recipe: The recipe to run.
+      cq_enabled: Whether the try builder is enabled by default. (If false,
+        the builder is includable_only.)
+      execution_timeout: Overrides the default timeout.
+      properties: Properties to pass to the recipe.
+    """
     dimensions = dict(host["dimensions"])
     dimensions["pool"] = "luci.flex.try"
     if execution_timeout == None:
@@ -216,6 +242,24 @@
         cq_compile_only = None,
         execution_timeout = None,
         properties = {}):
+    """Defines both a CI builder and similarly-configured CQ builder.
+
+    Args:
+      name: The name to use for both builders.
+      host: The host to run on.
+      recipe: The recipe to run.
+      category: Category in which to display the builder in the console view.
+      short_name: The short name for the builder in the console view.
+      cq_enabled: Whether the try builder is enabled by default. (If false,
+        the builder is includable_only.)
+      cq_compile_only: If cq_compile_only is specified, we generate both a
+        disabled builder that matches the CI builder, and a compile-only
+        builder. The compile-only builder is controlled by cq_enabled.
+        cq_compile_only also specifies the host to run on, because the
+        compile-only builder usually has weaker requirements.
+      execution_timeout: Overrides the default timeout.
+      properties: Properties to pass to the recipe.
+    """
     ci_builder(
         name,
         host,
@@ -226,11 +270,6 @@
         properties = properties,
     )
 
-    # If cq_compile_only is specified, we generate both a disabled builder that
-    # matches the CI builder, and a compile-only builder. The compile-only
-    # builder is controlled by cq_enabled. cq_compile_only also specifies the
-    # host to run on, because the compile-only builder usually has weaker
-    # requirements.
     cq_builder(
         name,
         host,
@@ -377,7 +416,6 @@
     },
 )
 
-
 # delocate works on aarch64. Test this by also building the static library mode
 # for android_aarch64_fips. Additionally, urandom_test doesn't work in shared
 # library builds, so this gives Android FIPS coverage for urandom_test.
@@ -790,6 +828,19 @@
         },
     },
 )
+
+# TODO(crbug.com/42290446): Enable on both CQ and CI.
+cq_builder(
+    "linux_rust",
+    LINUX_HOST,
+    cq_enabled = False,
+    properties = {
+        "cmake_args": {
+            "RUST_BINDINGS": "x86_64-unknown-linux-gnu",
+        },
+        "rust": True,
+    },
+)
 both_builders(
     "linux_sde",
     LINUX_HOST,
diff --git a/rust/bssl-sys/src/lib.rs b/rust/bssl-sys/src/lib.rs
index 3502a69..a55bdff 100644
--- a/rust/bssl-sys/src/lib.rs
+++ b/rust/bssl-sys/src/lib.rs
@@ -1,8 +1,15 @@
 #![no_std]
 
+// unnecessary_transmutes, needed to work around a Rust bug, is not available in
+// older Rusts. Stable lacks any way to condition code on Rust version, so the
+// workaroud for a Rust bug below needs this additional Rust workaround.
+#![allow(unknown_lints)]
+
 #![allow(non_upper_case_globals)]
 #![allow(non_camel_case_types)]
 #![allow(non_snake_case)]
+
+// Work around https://github.com/rust-lang/rust-bindgen/issues/2807
 #![allow(unnecessary_transmutes)]
 
 use core::ffi::c_ulong;