bug描述 Describe the Bug
- Affected component:
paddle/phi/kernels/cpu/embedding_kernel.cc (EmbeddingCPUFunctor::apply)
- Public API:
paddle.nn.Embedding / paddle.nn.functional.embedding (with padding_idx set)
- Severity: High
- CVSS 3.1: 8.2 (
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H)
- CWE: CWE-125 Out-of-bounds Read
- Status: Confirmed on
paddlepaddle==3.3.0 (heap disclosure + SIGSEGV) and via AddressSanitizer
- Affected versions: v3.3.0 release tag and current
develop HEAD (verified)
Affected Component
File: paddle/phi/kernels/cpu/embedding_kernel.cc (line 53 on develop HEAD)
Function: phi::EmbeddingCPUFunctor<T, Context>::apply
Vulnerable Code
for (int64_t i = 0; i < ids_numel; ++i) {
if (padding_idx_ == kNoPadding && ids[i] != padding_idx_) { // <-- gate
PADDLE_ENFORCE_LT(ids[i], row_number, ...); // range check runs ONLY
PADDLE_ENFORCE_GE(ids[i], 0, ...); // when no padding_idx
}
}
...
for (int64_t i = 0; i < ids_numel; ++i) {
if (padding_idx_ != kNoPadding && ids[i] == padding_idx_) {
memset(output + i * row_width, 0, row_width * sizeof(T));
} else {
memcpy(output + i * row_width,
table + ids[i] * row_width, // <-- OOB: ids[i] unchecked
row_width * sizeof(T));
}
}
Root Cause
The id-range validation is incorrectly scoped: it is nested inside
if (padding_idx_ == kNoPadding && ...). kNoPadding is -1, so as soon as an embedding
layer is created with any padding_idx (e.g. the ubiquitous padding_idx=0 for NLP
models), the bounds check is skipped for every id, and the copy loop indexes the weight table
with an attacker-controlled ids[i] (int64). Only the padding-row zeroing should be
conditional on padding_idx; the range check must be unconditional.
Proof of Concept (public API, paddlepaddle==3.3.0)
(a) Heap memory disclosure — an out-of-range id returns raw heap bytes past the table:
import paddle
paddle.set_device("cpu")
emb = paddle.nn.Embedding(16, 8, padding_idx=0) # valid ids: 0..15
out = emb(paddle.to_tensor([[40]], dtype="int64")) # id 40 is out of range
print(out.numpy().reshape(-1))
# -> [1.11e+27, 1.36e-14, 2.81e+23, 1.46e-19, 0.0, 7.14e+31, 0.0, 0.0]
# (uninitialised heap contents beyond the weight matrix)
(b) Remote crash (SIGSEGV) — a large id dereferences an unmapped page:
emb = paddle.nn.Embedding(16, 8, padding_idx=0)
emb(paddle.to_tensor([[10**9]], dtype="int64"))
Evidence
Real-wheel crash with Paddle's own C++ traceback naming the exact sink:
C++ Traceback (most recent call last):
0 paddle::pybind::eager_api_embedding(...)
2 paddle::experimental::embedding(...)
3 void phi::EmbeddingKernel<float, phi::CPUContext>(...)
4 void phi::EmbeddingCPUFunctor<float, phi::CPUContext>::apply<long>()
FatalError: `Segmentation fault` is detected by the operating system.
[SignalInfo: *** SIGSEGV (@0x78f89e000) received by PID ... ]
AddressSanitizer proof (line-for-line excerpt of the kernel logic):
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5150000002c0 ...
READ of size 32 at 0x5150000002c0 thread T0
#0 memcpy
#1 EmbeddingCPUFunctor_apply<float> embedding_oob_harness.cpp:49
0x5150000002c0 is located 64 bytes after 512-byte region [0x515000000080,0x515000000280)
allocated by thread T0 here:
#0 operator new(unsigned long)
#6 std::vector<float>::vector(...)
#7 main embedding_oob_harness.cpp:62
SUMMARY: AddressSanitizer: heap-buffer-overflow in memcpy
exploit_embedding_oob.py
validate.py
asan_crash.txt
How to Reproduce
pip install paddlepaddle==3.3.0
- Run snippet (a) → an out-of-range id (40) returns a vector of raw heap values instead
of raising — heap disclosure.
- Run snippet (b) → the process crashes with SIGSEGV in
EmbeddingCPUFunctor::apply.
- (Optional, deterministic) Compile the kernel-logic excerpt with AddressSanitizer
(g++ -fsanitize=address ...) or reproduce (b) on a cmake -DWITH_ASAN=ON build to
obtain the in-binary trace.
Impact / Real-World Scenario
Embedding layers turn caller-supplied token ids into vectors. Any service that embeds
externally-influenced ids (text classification / retrieval / recommender inference that accept
tokenized input, or pipelines where a tokenizer/vocabulary mismatch can emit ids ≥
num_embeddings) is exposed:
- Confidentiality (High): the output vector returns raw heap bytes adjacent to the weight
table — potentially other tensors/buffers/secrets in the process heap.
- Availability (High): a single crafted id crashes an inference worker.
- The attacker-controlled read offset (
ids[i] * row_width, ids[i] up to int64) is a
flexible OOB-read primitive.
Suggested Remediation
Make the range check unconditional:
for (int64_t i = 0; i < ids_numel; ++i) {
PADDLE_ENFORCE_GE(ids[i], 0, ...);
PADDLE_ENFORCE_LT(ids[i], row_number, ...);
}
Keep only the padding-row zeroing conditional on padding_idx. Apply the same fix to the
GPU/XPU embedding kernels, which share the pattern.
Environment
- PaddlePaddle 3.3.0 (CPU wheel) and
develop HEAD (source verified)
- Python 3.12, Ubuntu 24.04, g++ 13 + AddressSanitizer
CC: @luotao1
其他补充信息 Additional Supplementary Information
No response
bug描述 Describe the Bug
paddle/phi/kernels/cpu/embedding_kernel.cc(EmbeddingCPUFunctor::apply)paddle.nn.Embedding/paddle.nn.functional.embedding(withpadding_idxset)AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H)paddlepaddle==3.3.0(heap disclosure + SIGSEGV) and via AddressSanitizerdevelopHEAD (verified)Affected Component
Vulnerable Code
Root Cause
The id-range validation is incorrectly scoped: it is nested inside
if (padding_idx_ == kNoPadding && ...).kNoPaddingis-1, so as soon as an embeddinglayer is created with any
padding_idx(e.g. the ubiquitouspadding_idx=0for NLPmodels), the bounds check is skipped for every id, and the copy loop indexes the weight table
with an attacker-controlled
ids[i](int64). Only the padding-row zeroing should beconditional on
padding_idx; the range check must be unconditional.Proof of Concept (public API,
paddlepaddle==3.3.0)(a) Heap memory disclosure — an out-of-range id returns raw heap bytes past the table:
(b) Remote crash (SIGSEGV) — a large id dereferences an unmapped page:
Evidence
Real-wheel crash with Paddle's own C++ traceback naming the exact sink:
AddressSanitizer proof (line-for-line excerpt of the kernel logic):
exploit_embedding_oob.py
validate.py
asan_crash.txt
How to Reproduce
pip install paddlepaddle==3.3.0of raising — heap disclosure.
EmbeddingCPUFunctor::apply.(
g++ -fsanitize=address ...) or reproduce (b) on acmake -DWITH_ASAN=ONbuild toobtain the in-binary trace.
Impact / Real-World Scenario
Embedding layers turn caller-supplied token ids into vectors. Any service that embeds
externally-influenced ids (text classification / retrieval / recommender inference that accept
tokenized input, or pipelines where a tokenizer/vocabulary mismatch can emit ids ≥
num_embeddings) is exposed:table — potentially other tensors/buffers/secrets in the process heap.
ids[i] * row_width,ids[i]up toint64) is aflexible OOB-read primitive.
Suggested Remediation
Make the range check unconditional:
Keep only the padding-row zeroing conditional on
padding_idx. Apply the same fix to theGPU/XPU embedding kernels, which share the pattern.
Environment
developHEAD (source verified)CC: @luotao1
其他补充信息 Additional Supplementary Information
No response