CVE-2025-13229
Overview
Background
- Wasm Custom Descriptors
- an in-progress WasmGC proposal that attaches a runtime descriptor object to a struct type, enabling casts/tests against a particular descriptor rather than only a static type index.
- Exact type
- a reference type marked so that only its exact type index (not any strict subtype) satisfies it, tracked via
is_exact()/AsExact()and thekExactMatchOnlyexactness. - `ref.func`
- the Wasm instruction (and its constant-expression form) that produces a function reference; its result type must not over-claim exactness for imported functions.
- Descriptor subtyping restriction
- the rule (encoded by the regression test) that a described type and its descriptor must have matching subtyping, so a described struct cannot declare an unrelated explicit supertype.
Root Cause Analysis
In several sites V8’s Custom Descriptors support concluded that a Wasm reference was of an “exact” type, or that subtyping was more permissive than the spec allows, when it was not. The ref.func paths are a clear instance: in both src/wasm/constant-expression-interface.cc and src/wasm/function-body-decoder-impl.h, the result of ref.func was unconditionally passed through AsExactIfEnabled, stamping the reference as an exact type even for imported functions — but an import only guarantees that the actual function is a subtype of the declared import type (for function types f1 <: f2), so the actual runtime function may be a strict subtype. Claiming exactness lets downstream ref.cast/ref.test and the Turboshaft reducer treat the reference as the exact type and drop checks, yielding type confusion.
The patch restricts the exact stamp to non-imported functions (function_index >= module_->num_imported_functions) and returns an inexact type for imports. The same commit also tightens descriptor subtyping (the regression test flips CheckValid to CheckInvalid(/type 4 has invalid explicit supertype 2/)), adds the custom-descriptor guard in the reducer, and makes IsCanonicalSubtype honor exactness.
The fix works because a reference is now marked exact only when its exactness is actually guaranteed.
ref.func stamped imported function references as exact even though an import only guarantees subtyping; restricting the exact stamp to non-imported functions removes the false exactness claim that downstream cast/optimization logic relied on.Attack Path
- Import a function under a broader type The attacker supplies a Wasm module (Custom Descriptors enabled) that imports a function whose actual type is a strict subtype of the declared import type.
- Produce a `ref.func` for the import
Pre-patch,
ref.funcmarks the resulting reference exact viaAsExactIfEnabled, over-claiming the type. - Feed the reference to an exact-typed check
A
ref.cast/ref.testor the Turboshaft reducer trusts the exact stamp and eliminates the runtime type check. - Type confusion The function reference is used as the wrong (exact) type, so a call or field access proceeds against a mismatched signature/layout (inferred).
Impact Assessment
Files Changed
src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.ccsrc/compiler/turboshaft/wasm-gc-typed-optimization-reducer.hsrc/wasm/canonical-types.ccsrc/wasm/canonical-types.hsrc/wasm/constant-expression-interface.ccsrc/wasm/function-body-decoder-impl.h
Audit Directions
- Exactness at value productionAudit every producer that stamps a Wasm reference type (
ref.func, allocations, boundary conversions) to ensureAsExact/AsExactIfEnabledis applied only when exactness is guaranteed, never for imports. - Import subtyping vs. exactnessReview paths that treat imported entities (functions, globals, tables) as exact rather than as subtypes of their declared import types.
- Descriptor/described subtypingCheck validation of descriptor and described types (e.g. explicit supertype relationships) to ensure their subtyping is required to match, per the flipped regression test.
Patch
From 48c3551179299151b044b2c161566465497c0407 Mon Sep 17 00:00:00 2001
From: Jakob Kummerow <jkummerow@chromium.org>
Date: Tue, 23 Sep 2025 15:39:41 +0200
Subject: [PATCH] [wasm-custom-desc] Fix subtyping
This updates the restrictions on subtyping of descriptors, in
anticipation of upcoming spec changes:
- descriptors and described types must have matching subtyping
- ref.func for imported functions returns inexact types (for
now; long-term solution TBD)
And it fixes our implementation:
- `ProcessBranchOnTarget` erroneously still thought it could
compute reachability based on static types when Custom
Descriptors are in play
- `JSToWasmObject` was missing support for exact types
- `ref.get_desc` must not return an exact type when the actual
type on the stack is exact, but a non-trivial subtype of the
instruction's type immediate.
Bug: 403372470
Fixed: 446113731, 446113732, 446122633, 446124892, 446124893
Change-Id: Ic79ab08d906a2e21e66b76e9d96eebb4ebb7a8e5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6973586
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#102697}
---
diff --git a/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.cc b/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.cc
index c8d6693..f6905e6 100644
--- a/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.cc
+++ b/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.cc
@@ -445,7 +445,10 @@
} else {
DCHECK_EQ(branch.if_false, &target);
if (wasm::IsSubtypeOf(GetResolvedType(check.object()), check.config.to,
- module_)) {
+ module_) &&
+ // When checking for a particular custom descriptor, static types
+ // cannot predict the outcome.
+ !(IsCastToCustomDescriptor(module_, check.config))) {
// The type check always succeeds, the target is impossible to be
// reached.
DCHECK_EQ(target.PredecessorCount(), 1);
diff --git a/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.h b/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.h
index a571fe4..2ba8fbe 100644
--- a/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.h
+++ b/src/compiler/turboshaft/wasm-gc-typed-optimization-reducer.h
@@ -18,6 +18,13 @@
namespace v8::internal::compiler::turboshaft {
+inline bool IsCastToCustomDescriptor(const wasm::WasmModule* module,
+ WasmTypeCheckConfig config) {
+ return config.to.has_index() &&
+ module->type(config.to.ref_index()).has_descriptor() &&
+ config.exactness == compiler::kExactMatchOnly;
+}
+
// The WasmGCTypedOptimizationReducer infers type information based on the input
// graph and reduces type checks and casts based on that information.
//
@@ -179,7 +186,7 @@
bool to_nullable = cast_op.config.to.is_nullable();
if (wasm::IsHeapSubtypeOf(type.heap_type(), cast_op.config.to.heap_type(),
module_) &&
- !IsCastToCustomDescriptor(cast_op.config)) {
+ !IsCastToCustomDescriptor(module_, cast_op.config)) {
if (to_nullable || type.is_non_nullable()) {
// The inferred type is already as specific as the cast target, the
// cast is guaranteed to always succeed and can therefore be removed.
@@ -247,7 +254,7 @@
type_check.config.to.heap_type(), module_) &&
// When checking for a particular custom descriptor, static types
// cannot guarantee success.
- !(IsCastToCustomDescriptor(type_check.config))) {
+ !(IsCastToCustomDescriptor(module_, type_check.config))) {
if (to_nullable || type.is_non_nullable()) {
// The inferred type is guaranteed to be a subtype of the checked
// type.
@@ -435,12 +442,6 @@
}
private:
- bool IsCastToCustomDescriptor(WasmTypeCheckConfig config) {
- return config.to.has_index() &&
- module_->type(config.to.ref_index()).has_descriptor() &&
- config.exactness == compiler::kExactMatchOnly;
- }
-
Graph& graph_ = __ modifiable_input_graph();
const wasm::WasmModule* module_ = __ data() -> wasm_module();
WasmGCTypeAnalyzer analyzer_{__ data(), graph_, __ phase_zone()};
diff --git a/src/wasm/canonical-types.cc b/src/wasm/canonical-types.cc
index 359ca5e..50120e0 100644
--- a/src/wasm/canonical-types.cc
+++ b/src/wasm/canonical-types.cc
@@ -243,15 +243,19 @@
}
bool TypeCanonicalizer::IsCanonicalSubtype(CanonicalTypeIndex sub_index,
- CanonicalTypeIndex super_index) {
+ CanonicalValueType super_type) {
+ DCHECK(super_type.has_index());
// Fast path without synchronization:
- if (sub_index == super_index) return true;
+ if (sub_index == super_type.ref_index()) return true;
+ // If the supertype is exact, then only the equality case above is
+ // successful.
+ if (super_type.is_exact()) return false;
// Multiple threads could try to register and access recursive groups
// concurrently.
// TODO(manoskouk): Investigate if we can improve this synchronization.
base::MutexGuard mutex_guard(&mutex_);
- return IsCanonicalSubtype_Locked(sub_index, super_index);
+ return IsCanonicalSubtype_Locked(sub_index, super_type.ref_index());
}
bool TypeCanonicalizer::IsCanonicalSubtype_Locked(
CanonicalTypeIndex sub_index, CanonicalTypeIndex super_index) const {
@@ -266,16 +270,6 @@
return false;
}
-bool TypeCanonicalizer::IsCanonicalSubtype(ModuleTypeIndex sub_index,
- ModuleTypeIndex super_index,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
- CanonicalTypeIndex canonical_super =
- super_module->canonical_type_id(super_index);
- CanonicalTypeIndex canonical_sub = sub_module->canonical_type_id(sub_index);
- return IsCanonicalSubtype(canonical_sub, canonical_super);
-}
-
bool TypeCanonicalizer::IsHeapSubtype(CanonicalTypeIndex sub,
CanonicalTypeIndex super) const {
DCHECK_NE(sub, super);
diff --git a/src/wasm/canonical-types.h b/src/wasm/canonical-types.h
index b992652..5301224 100644
--- a/src/wasm/canonical-types.h
+++ b/src/wasm/canonical-types.h
@@ -85,17 +85,12 @@
V8_EXPORT_PRIVATE const CanonicalArrayType* LookupArray(
CanonicalTypeIndex index) const;
- // Returns if {canonical_sub_index} is a canonical subtype of
- // {canonical_super_index}.
+ // Returns if {sub_index} is a canonical subtype of {super_type}, which must
+ // be an indexed type. Interprets {sub_index} as (exact sub_index), which is
+ // appropriate for checking the actual type of a thing against a required
+ // type.
V8_EXPORT_PRIVATE bool IsCanonicalSubtype(CanonicalTypeIndex sub_index,
- CanonicalTypeIndex super_index);
-
- // Returns if the type at {sub_index} in {sub_module} is a subtype of the
- // type at {super_index} in {super_module} after canonicalization.
- V8_EXPORT_PRIVATE bool IsCanonicalSubtype(ModuleTypeIndex sub_index,
- ModuleTypeIndex super_index,
- const WasmModule* sub_module,
- const WasmModule* super_module);
+ CanonicalValueType super_type);
// Deletes recursive groups. Used by fuzzers to avoid accumulating memory, and
// used by specific tests e.g. for serialization / deserialization.
diff --git a/src/wasm/constant-expression-interface.cc b/src/wasm/constant-expression-interface.cc
index 0bd4c138..757b4a1 100644
--- a/src/wasm/constant-expression-interface.cc
+++ b/src/wasm/constant-expression-interface.cc
@@ -124,8 +124,13 @@
bool function_is_shared = module_->type(sig_index).is_shared;
CanonicalValueType type =
CanonicalValueType::Ref(module_->canonical_type_id(sig_index),
- function_is_shared, RefTypeKind::kFunction)
- .AsExactIfEnabled(decoder->enabled_);
+ function_is_shared, RefTypeKind::kFunction);
+ // Imported functions can be subtypes of their static import type,
+ // for non-imported functions we can return an exact type.
+ if (decoder->enabled_.has_custom_descriptors() &&
+ function_index >= module_->num_imported_functions) {
+ type = type.AsExact();
+ }
DirectHandle<WasmFuncRef> func_ref =
WasmTrustedInstanceData::GetOrCreateFuncRef(
isolate_,
diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h
index 6aa071e..5a6a2ec 100644
--- a/src/wasm/function-body-decoder-impl.h
+++ b/src/wasm/function-body-decoder-impl.h
@@ -4243,9 +4243,17 @@
if (!this->ValidateFunction(this->pc_ + 1, imm)) return 0;
ModuleTypeIndex index = this->module_->functions[imm.index].sig_index;
const TypeDefinition& type_def = this->module_->type(index);
- Value* value =
- Push(ValueType::Ref(index, type_def.is_shared, RefTypeKind::kFunction)
- .AsExactIfEnabled(this->enabled_));
+ ValueType result_type =
+ ValueType::Ref(index, type_def.is_shared, RefTypeKind::kFunction);
+ // For imported functions, we must return an inexact type, because
+ // importing checks subtyping, i.e. for function types f1 <: f2, it is
Regression Test / PoC
diff --git a/test/mjsunit/wasm/custom-descriptors-validity.js b/test/mjsunit/wasm/custom-descriptors-validity.js
index 9b3118f2..204d0a5 100644
--- a/test/mjsunit/wasm/custom-descriptors-validity.js
+++ b/test/mjsunit/wasm/custom-descriptors-validity.js
@@ -119,7 +119,7 @@
builder.addStruct({describes: 2, supertype: 1}); // 3
});
-CheckValid((builder) => {
+CheckInvalid(/type 4 has invalid explicit supertype 2/, (builder) => {
builder.addStruct({final: false}); // 0
}, (builder) => {
builder.addStruct({descriptor: 2}); // 1
Original Bug Report
Wasm type confusion due to custom descriptors spec unsoundness on `ref.func` exact typing
README
This is potentially a Wasm spec-level unsoundness issue. However, there seem to be no public spec documenting how ref.func and other operations that yield references work in conjunction with custom descriptors. I am reporting this privately to Google Chrome/V8 as Google seems to be leading the spec work and implementation of custom descriptors the first and practically has the most affected users in the wild.
As this might be a spec-level unsoundness which the Wasm community needs to know, I believe that the issue necessitates a quicker disclosure deadline than what is enforced by default by Chrome’s disclosure policies. It would be great if we can coordinate the disclosure within a week from the reported date - please contact me through email or through the comments. Unless otherwise coordinated, the spec unsoundness issue is subject to a 14-day disclosure deadline after which the issue, without any specific mentions of Chrome/V8, will be disclosed publicly on https://github.com/WebAssembly/custom-descriptors/issues. The Chrome/V8 security team is allowed to responsibly disclose this to other vendors and personnel involved in WebAssembly work (at CG meetings or whatnot) under the conditions that 1. the reporter is credited, and 2. is given at least a day’s advance notice.
VULNERABILITY DETAILS
Summary
Wasm type confusion due to invalidly typing ref.func function types as exact. Imported functions are allowed to be subtypes of the declared function type, and ref.func returns the original reference for equivalence with the original import. However, ref.func is typed as exact based on its declared function type which is unsound. By abusing WasmGCTypeAnalyzer reachability analysis, this may be pivoted into arbitrary Wasm type confusion.
Custom descriptors feature is exposed in the wild by default through Origin Trials from M141, which is currently at Beta and very soon reaches (Early) Stable. This bug is not caused by a recent code change and has existed from the very first feature implementation (approx. 6 months) due to a likely inherent spec unsoundness.
> This bug is notable in that ref.func operation itself is completely unrelated to custom descriptors, yet the addition of exactness introduces unsoundness.
Details
WebAssembly Custom Descriptors proposal introduces exactness as part of heaptype. An object may only be typed as exact if it is exactly an instance of that type, but not for its subtypes. Most importantly, this is not only valid for struct types but for all indexed types including function types.
Currently, V8 types ref.func as exact types of its declared function type:
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/function-body-decoder-impl.h;drc=43df3221d3b0c1ea8f7d54394a00cb8656204085;l=4240
DECODE(RefFunc) {
this->detected_->add_reftypes();
IndexImmediate imm(this, this->pc_ + 1, "function index", validate);
if (!this->ValidateFunction(this->pc_ + 1, imm)) return 0;
ModuleTypeIndex index = this->module_->functions[imm.index].sig_index;
const TypeDefinition& type_def = this->module_->type(index);
Value* value =
Push(ValueType::Ref(index, type_def.is_shared, RefTypeKind::kFunction)
.AsExactIfEnabled(this->enabled_)); // [!] typed as exact based on declared function type
CALL_INTERFACE_IF_OK_AND_REACHABLE(RefFunc, imm.index, value);
return 1 + imm.length;
}
Other decoders, e.g. ModuleDecoderImpl::consume_init_expr(), ConstantExpressionInterface::RefFunc(), also does the same and types it as exact.
However, imported functions may very well be subtypes of the declared type based on Wasm spec. Importing a subtyped function does not violate any JS-Wasm boundary type checks and is perfectly legal:
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/module-instantiate.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=733
ImportCallKind ResolvedWasmImport::ComputeKind(
DirectHandle<WasmTrustedInstanceData> trusted_instance_data, int func_index,
const wasm::CanonicalSig* expected_sig, CanonicalTypeIndex expected_sig_id,
WellKnownImport preknown_import) {
// ...
if (!trusted_function_data_.is_null()) {
if (Tagged<WasmExportedFunctionData> data;
TryCast(*trusted_function_data_, &data)) {
if (!data->MatchesSignature(expected_sig_id)) { // [!] allows subtype
return ImportCallKind::kLinkError;
}
uint32_t function_index = static_cast<uint32_t>(data->function_index());
if (function_index >=
data->instance_data()->module()->num_imported_functions) {
return ImportCallKind::kWasmToWasm;
}
// ...
}
}
// ...
}
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=3085
bool WasmExportedFunctionData::MatchesSignature(
wasm::CanonicalTypeIndex other_canonical_type_index) {
return wasm::GetTypeCanonicalizer()->IsCanonicalSubtype( // [!] allows subtype
sig_index(), other_canonical_type_index);
}
Plus, imported functions preserve their reference for equivalence.
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/module-instantiate.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=2335
bool InstanceBuilder::ProcessImportedFunction(
DirectHandle<WasmTrustedInstanceData> trusted_instance_data,
int import_index, int func_index, DirectHandle<Object> value,
WellKnownImport preknown_import) {
// ...
// Store any {WasmExternalFunction} callable in the instance before the call
// is resolved to preserve its identity. This handles exported functions as
// well as functions constructed via other means (e.g. WebAssembly.Function).
if (WasmExternalFunction::IsWasmExternalFunction(*value)) {
trusted_instance_data->func_refs()->set( // [!] stores imported function, later returned for ref.func
func_index, Cast<WasmExternalFunction>(*value)->func_ref());
}
// ...
}
This indicates that ref.func must NOT be typed as an exact type, at the very least for imported functions, as its actual type may be a subtype. However, it is statically typed as an exact (super)type. Consider the following case:
- We have function types
f2 <: f1 - Module 1 exports
fn : f2. - Module 2 imports it as
fn : f1. This is legal. - Module 2 executes
ref.func $fn, yielding typeref exact $f1. This is obviously wrong as its real exact type isref exact $f2, but the next line will show it even more obviously. - Module 2 executes
ref.cast $f1+ref.cast $f2on the object. This results inref exact $f1 -> ref $f1 -> ref $f2casting, which succeeds because the first is a static upcast and the second succeeds dynamically. However, the two typesref exact $f1andref $f2are unrelated in subtyping hierarchy; the cast should never have succeeded.- Note that without custom descriptors and thus without exactness, the cast should succeed!
So how is this exploitable? Enter WasmGCTypeAnalyzer again, which I have first demonstrated that it can be used to pivot seemingly unexploitable Wasm type confusions into attacker-chosen, arbitrary type confusions (b/372269618, b/373703277, b/374790906, b/377620832, …). After a series of exploits WasmGCTypeAnalyzer has been hardened such that most operations that lead to an unreachable state are replaced to trap or otherwise conform with the statically reachable case unconditionally. But we can still run around the mitigations and exploit subtle discrepancies as shown in the below Wasm code, pulled out from the repro (replace v = f1, v2 = f2, imp = fn):
kExprBlock, kWasmVoid,
// make typer track function type as ref v2
kExprRefFunc, $imp, // decoder: ref exact v (mistyped!), typer: ref v
kExprLocalTee, 3,
kGCPrefix, kExprRefCast, $sig_v_v, // decoder: ref v (upcast), typer: ref v
kGCPrefix, kExprRefCast, $sig_v_v2, // decoder: ref v2, typer: ref v2, runtime: cast succeed
kExprDrop,
// now refine it with ref exact v
kExprLocalGet, 3, // decoder: ref exact v, typer: ref v2
kGCPrefix, kExprRefCastNull, kWasmExact, $sig_v_v, // decoder: ref null exact v (upcast), typer: ref v2
// exploit implicit type refinement w/ inconsistent types from decoder
kExprBrOnNonNull, 0, // typer: (ref exact v) & (ref v2) = bot on branch taken, runtime: branch taken
kExprUnreachable,
kExprEnd,
kExprBrOnNonNull leads to RefineTypeKnowledge(is_null.object(), is_null.type.AsNonNull(), branch), where is_null.type is taken from the decoder’s static typing information. The typer already tracks the object’s type as ref v2, and as (ref exact v) & (ref v2) = bot following code is marked as statically unreachable while it is in fact reached dynamically. Note that both sides of the branch is typed as unreachable, and that there is no statically “reachability compliant” result of IsNull that the operation should get reduced into (aside from replacing it with an unconditional trap).
Exploiting such reachability analysis bug has been shown to be possible in prior reports via loop reprocessing bypass, and thus is omitted.
Although I cannot find a spec that describes how ref.func should be typed with custom descriptors, it seems that there is enough brokenness across other implementations potentially indicating a spec issue (again, if a spec even exists). Binaryen also documents that they implement this exactness typing rule in PR #7600.
Bisect
Bug introduced by WebAssembly Custom Descriptors, on Origin Trials from M141 and onwards. More specifically, it is introduced in commit 25a0fc85 and dc8cec44 which makes ref.func exact.
VERSION
Chrome Version: M141~
Operating System: All
REPRODUCTION CASE
Attached as poc.js which exploits this issue to alias two unrelated struct types, then uses this type confusion to trigger an arbitrary caged write within the sandbox.
Also attached is rce.html which exploits this issue, together with the wrapper-wasmcpt-uaf v8sbx bypass, to gain RCE and print out /flag/flag to stdout.
You might want to pass --experimental-wasm-custom-descriptors on d8 or --enable-blink-features=WebAssemblyCustomDescriptors on Chrome to simulate Origin Trials behavior.
FOR CRASHES, PLEASE INCLUDE THE FOLLOWING ADDITIONAL INFORMATION
Type of crash: Renderer
Crash State: Crashes on arbitrary caged write attempt from JIT-compiled Wasm function with poc.js / RCE with rce.html
CREDIT INFORMATION
Reporter credit: Seunghyun Lee (@0x10n) of CMU CSD / CyLab
- https://crrev.com/c/6415951
- https://crrev.com/c/6504002
- https://github.com/WebAssembly/binaryen/pull/7600
- https://github.com/WebAssembly/custom-descriptors/issues
- https://issuetracker.google.com/issues/372269618
- https://issuetracker.google.com/issues/373703277
- https://issuetracker.google.com/issues/374790906
- https://issuetracker.google.com/issues/377620832
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/function-body-decoder-impl.h;drc=43df3221d3b0c1ea8f7d54394a00cb8656204085;l=4240
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/module-instantiate.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=2335
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/module-instantiate.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=733
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.cc;drc=e75b1ba2a99aa352048745d44754c5de8be273d8;l=3085