
اكتشاف تحميلات الذاكرة التي يستحدثها المترجم وتحوّل الكود الآمن المكتوب بلغة C إلى ثغرات TOCTOU. يشمل تدقيقات آلية لمصدر الكود، وتحليلًا ثنائيًا قائمًا على Unicorn، ومسوحات للمترجمات/المعماريات/الأعلام عبر أكثر من 100 مشروع.
"...تعريف «المُصرّف السليم» يزداد تراخيًا."
الملف التنفيذي الذي تُشغّله ليس البرنامج الذي كتبته. يُعيد مُحسِّن المُصرّف كتابة شفرتك المصدرية بطرق لا تراها أبدًا — وبعض هذه التغييرات يمكنها، بصمت وبشكل مشروع، أن تحوّل شيفرة تبدو آمنة إلى ملفات تنفيذية قابلة للاستغلال. قد يكون السطر نفسه آمنًا مع مُصرّف وقابلًا للاستغلال مع آخر، دون ما في الشيفرة المصدرية يُخبرك بأيهما: ثغرة معلّقة في تراكب كمّي، لا تنهار إلا عند البناء. يستكشف Schrödinger's TOCTOU التحميلات التي يخترعها المُصرّف وآثارها الواسعة على ثغرات وقت الفحص إلى وقت الاستخدام (TOCTOU) — الموجودة عبر الأنوية والمُشغِّلات الافتراضية والبيئات المعزولة والبرمجيات الثابتة والمكتبات. في كل مكان ننظر إليه، تظل الشيفرة التي تبدو آمنة معرّضة لأهواء المُصرّف. لكن تلك مجرد عيّنة، لا حدّ؛ فمن المرجح جدًا أن تكون الثغرات نفسها في شيفرتك أنت أيضًا.
"ابدأ بشيء سهل."
كم مرة تُحمّل هذه الدالة *p؟```c
unsigned int g(unsigned short *p)
{
short t = p; / copy *p into a local for safekeeping */
return (unsigned short)t - t;
}
التلميح: الإجابة هي 1 — يقوم المصدر بتحميل `*p` مرة واحدة فقط في `t`.
الصقه في [Compiler Explorer](https://godbolt.org/z/c5K9P4dPd) (`arm gcc 14.2.0`،
`-O2`) وعدّ مرات التحميل من `r0`، الذي يحمل `p`:```asm
g:
ldrh r2, [r0] # load *p, once
ldrsh r0, [r0] # load *p, twice
subs r0, r2, r0
bx lr
تحميل واحد في الكود المصدري، واثنان في الملف الثنائي. أما الثاني فهو تحميل مُبتكَر — قراءة صُنعت بواسطة المترجم ولم تكتبها أنت أبدًا. وهي قانونية في إطار الآلة المُجرَّدة للغة C، التي تفترض أن الذاكرة لا يمكن أن تتغير بين قراءتين. لكن عندما تكون تلك الذاكرة قابلة للكتابة من قبل المهاجم، يتحول الافتراض إلى استغلال: إذ يمكن أن يقع التحميل المُبتكَر بعد فحص أمني، معيدًا بصمت فتح نافذة من نوع التحقق من الوقت إلى الاستخدام (TOCTOU) التي ظن المبرمج أنه أغلقها. فالقيمة التي تحققت منها والقيمة التي تستخدمها لم يعودا مضمونين أن يكونا متماثلين — رغم أنك لم تكتب أبدًا كودًا يعيد قراءتها.
يُثبت التحدي وجود التحميل المُبتكَر؛ فلنرَ كيف يتحول ذلك إلى تلفٍ في الذاكرة.
في ثغرة من نوع TOCTOU، يتحقق البرنامج من أن القيمة آمنة ثم يستخدمها. ومع ذلك، توجد نافذة للاستغلال إذا استطاع المهاجم تغيير القيمة في جزء صغير من الوقت بين هاتين القراءتين — فالقيمة غير الضارة تجتاز الفحص بينما الخطيرة هي التي تُستخدم:```c if (shared->len <= 20) // CHECK reads shared->len // ** attacker modifies shared->len ** memcpy(out, shared->data, shared->len); // USE reads it again: buffer overflow
الإصلاح الكلاسيكي هو **اللقطة أولاً**: انسخ أي بيانات قد يعبث بها المهاجم
في متغير محلي لا يمكن للمهاجم الوصول إليه، ثم لا تثق إلا
بذلك المتغير المحلي. بمجرد أن يصبح `len` في متغير محلي يكون مجمّدًا — لا يمكن للمهاجم الذي يتسابق مع
الذاكرة المشتركة أن يلمسه بعد الآن — لذا فإن الفحص والنسخ مضمونان
لرؤية نفس القيمة. هكذا يعمل الكود في `receive` أدناه على إصلاح TOCTOU:
يلتقط لقطة من الرسالة، ويتحقق من صحة اللقطة، وينشر النسخة الموثوقة
في `slot` ليتمكن المستهلك من إعادة توجيهها:```c
#include <string.h>
struct message {
int len; /* payload length */
char data[20]; /* payload */
};
struct message slot; /* the most recently validated message */
char out[20]; /* fixed 20-byte destination */
void receive(struct message *shared) {
struct message local = *shared; /* 1. snapshot untrusted input */
if (local.len <= 20) /* 2. validate the snapshot */
slot = local; /* 3. publish the validated copy */
}
void forward(void) { /* the time of use, later */
memcpy(out, slot.data, slot.len); /* slot.len was checked <= 20 ... right? */
}
بالنسبة للمصدر، هذا صحيح. len يُقرأ مرة واحدة بالضبط — في اللقطة —
لذا القيمة التي تجتاز فحص <= 20 هي القيمة المنشورة في slot.
نافذة TOCTOU مغلقة والكود آمن.
إلا أنه ليس كذلك. تحت x86-64 gcc مع -O2، يقرأ receive إياه من
الذاكرة المشتركة الأصلية مرتين: مرة
كقيمة عددية لبوابة الفحص، ومرة أخرى كجزء من النسخ
الجماعي الذي يُنشر في slot:```nasm
receive:
cmp DWORD PTR [rdi], 20 ; READ #1: the CHECK reads shared->len directly
movdqu xmm0, XMMWORD PTR [rdi] ; READ #2: the bulk copy re-reads it (len is byte 0)
mov rax, QWORD PTR [rdi+16] ; (the bulk copy's tail: struct bytes 16-23)
jg .L1 ; len > 20? skip the publish
mov QWORD PTR slot[rip+16], rax ; (publish that tail)
movaps XMMWORD PTR slot[rip], xmm0 ; and publish the TOCTOU-vulnerable snapshot
.L1:
ret
forward:
movsx rdx, DWORD PTR slot[rip] ; copy size = slot.len, the unchecked READ #2 value
mov esi, OFFSET FLAT:slot+4 ; src = slot.data
mov edi, OFFSET FLAT:out ; dst = out[20]
jmp memcpy ; copies slot.len bytes into out[20]
يعمل الفحص على READ #1؛ القيمة التي تستقر في `slot.len` هي READ #2. المهاجم الذي يقلب `len` بينهما يمرّر قيمة آمنة إلى فحص `<= 20` بينما تُنشر قيمة ضخمة في `slot` — وبعدها تنسخ `forward` ذلك العدد من البايتات إلى `out[20]`، أي الفيض نفسه الذي كانت اللقطة تهدف إلى منعه، ويعيده المُحسِّن إلى الحياة.
هذا يتحوّل إلى إثبات مفهوم كامل في [`poc/example.c`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/poc/example.c)، حيث يستخدم الكود النهج المتعارف عليه لتحصين TOCTOU: بنية `message` غير الموثوقة تُلتقط في `local` بحيث لا يمكن تعديلها، ويتم التحقق من `local.len` الخاص باللقطة مقابل سعة المخزن المؤقت، ولا يُنشر في `slot` إلا النسخة الموثقة؛ ويقوم مستهلك لاحقًا بنسخ عدد من البايتات بقدر `slot.len` من الحمولة إلى مخزن مؤقت ثابت. في الوقت نفسه، ينافس المهاجم على `shared->len`. حمل مبتكر غير متوقع من المترجم يعيد قراءة `shared->len` من أجل النشر الجماعي، لذلك يحمل `slot.len` قيمة المهاجم الضخمة على الرغم من نجاح الفحص — معيدًا إدخال TOCTOU الذي كان المبرمج يحاول الدفاع ضده، ومنشئًا فيض مخزن مؤقت يبدو مستحيلًا — من العدم.
## السبب
> *بحلول الوقت الذي يصل فيه C إلى كود الآلة، يكون قد أعيد تشكيله عبر تخفيض الواجهة الأمامية، وتحسينات IR، وتخصيص السجلات، وتوليد الكود الخلفي — خط أنابيب عميق متعدد المراحل يتخذ قرارات لا يمكنك رؤيتها. لا توجد مرحلة واحدة تُلام. الحمل المبتكر خاصية ناشئة لخط الأنابيب بأكمله، وليس خطأً في أي جزء منه.*
عند هذه النقطة: يمكن للمترجمين *أن يصدروا* حمولات مبتكرة، والنمط نفسه الذي كان يهدف إلى منع الخطأ — الالتقاط، التحقق، الاستخدام — هو ما يعيد إدخاله. الخطوة التالية (لمعرفة ما إذا كنا عرضة فعليًا للخطر) هي توصيف *متى* يحدث ذلك. يتبيّن أن ذلك صعب.
في [`cat-states/`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/)، نبحث عن إثباتات المفهوم التي تُظهر أنه حقيقي — وأنه في كل مكان:
| Mechanism | Toolchains | Targets |
|---|---|---|
| [**Rematerialization**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#rematerialization-class-1) | GCC, Clang, ICX, ICC, MSVC | x86-64, i386, m68k, VAX, MSP430 |
| [**Width-mismatch reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#width-mismatch-reload-class-2) | GCC | ARM, MIPS, MIPS64, RV64, s390x |
| [**Bulk-vs-scalar overlap**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#bulk-vs-scalar-overlap-class-3) | GCC, Clang, ICX, MSVC | x86-64, ARM, AArch64, AVR, Xtensa, SPARC, PPC64, s390x, MIPS64, RV64, m68k, MSP430, VAX, HPPA |
| [**Cross-class reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#cross-class-reload-class-4) | GCC | x86-64, s390x |
| [**CISC mem-op fold**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#cisc-alu-mem-op-fold-class-7) | GCC, Clang | m68k, MSP430, s390x, VAX, 6502 |
| [**Byte-order reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#byte-order-divergent-reload-class-8) | GCC | s390x |
كل إثبات مفهوم أعلاه يحدد نقطة واحدة حيث يمكن أن يظهر الحمل؛ [`alpha-lab/`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/) يرسم الفضاء حولها ليجد أين تقع الحواف — خط أنابيب من ثلاث مراحل يُدار من ملف `.c` واحد. يقوم [مشغّل المصفوفة](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/matrix_runner.py) بمسح مصفوفة المترجم × البنية × الأعلام على [Compiler Explorer](https://godbolt.org)؛ و[كاشف الحمولات](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/detect.py) يشغّل كل ثنائي ناتج تحت [Unicorn](https://www.unicorn-engine.org/) ويلتقط أي بايت يُقرأ مرتين؛ و[مصغّر الأعلام](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/flag_search.py) ينقّح كل إصابة بالتنقيح التدريجي (delta-debugging) وصولًا إلى أصغر مجموعة أعلام تحوّل بناءً آمنًا إلى TOCTOU مزدوج القراءة.
**النتيجة**: لا يوجد مترجم واحد أو علم واحد أو مرحلة واحدة يُلام — القراءة المزدوجة تنشأ من التفاعل المعقّد لطبقات مترجم عديدة، تتخذ كل منها قرارات صحيحة محليًا. التأثير غير خطي: التغييرات الصغيرة في المصدر أو الأعلام أو الهدف يمكن أن [تتتالى في نتائج مختلفة](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy). الطريقة الوحيدة الموثوقة لمعرفة ما إذا كان سطر معيّن عرضة للخطر هي [**ترجمته والنظر إليه.**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/README.md#same-source-different-outcome)
**القطة حيّة — وليست كذلك.** حتى تبني، فإن موقع استدعاء يلتقط نسخة محلية ويتحقق منها ويستخدمها هو *ليس* آمنًا ولا عرضة للخطر — بل هو كلاهما، والمترجم ونسخته والهدف والأعلام هي التي تقرر أيهما. البناء هو القياس، وهو ينهار التراكب في اتجاه أو آخر. هذه **TOCTOU شرودنغر**: فحصٌ لقيمة اعتقد المبرمج أنها مجمّدة، وتسمح معايير C بهدوء للمترجم بإعادة قراءتها من ذاكرة يتحكم بها المهاجم. يظل الصندوق مغلقًا حتى يختار شخص ما، في مكان ما، سلسلة أدوات ويفتحه.
## الأثر
> *النمط يظهر في كل مكان تقريبًا — منسوجًا في أكثر الأكواد مراجعةً في العالم عبر لغة C اصطلاحية بسيطة.*
المشكلة **شبه مستعصية**. نفس مقتطف الكود يمكن أن يكون عرضة للخطر أو غير عرضة اعتمادًا على التركيبة الدقيقة للمترجم × الإصدار × البنية × الأعلام — وهناك تركيبات أكثر من عدد الذرات في الكون المرصود. حصره حتى لقاعدة أكواد واحدة هو بحث شبه مستحيل؛ القيام به عبر النظام البيئي أسوأ بكثير.
حتى تحديد ما إذا كان موقع استدعاء *واحد* آمنًا يقاوم الفحص: حاجز محتمل مثل `copy_from_user` في النواة [يغلق الخطأ](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#analysis) فقط بعد أن تستقر حوالي ست طبقات من التضمين والوحدات والأنساخ الفرعية `CONFIG`/ميزات وحدة المعالجة المركزية في `asm` غير شفاف — والسطر نفسه من المصدر ليس حاجزًا إطلاقًا في تركيبات أخرى. قراءة موقع الاستدعاء لا تخبرنا بشيء.
الطريق الوحيد للأمام هو الأتمتة. تم تشغيل تحليل قائم على الاستدلال عبر أهداف مفتوحة المصدر بارزة — برامج مراقبة (hypervisors)، بيئات تشغيل TEE/enclave، برمجيات ثابتة، أنظمة فرعية للنواة، مكتبات بروتوكولات — وعُثر على **أكثر من 300 TOCTOU من نمط شرودنغر** عبر **أكثر من 100 مشروع حساس أمنيًا**: مواقع تسمح فيها معايير C *للمترجم* بإعادة قراءة ذاكرة قابلة للكتابة من قبل المهاجم بين الفحص واستخدامه. التحليل الآلي يحدد حدود الثقة، ويبحث عن نمط شرودنغر، ويقيّم الاحتمالية/الأثر/الخطر.
النتائج تُظهر أن الأحمال المبتكرة التي تبدو غير ضارة تتصاعد بسهولة إلى عواقب مدمرة.
المترجم لا يبتكر *حمولة* بقدر ما يبتكر *القدرة* التي تمنحها تلك الحمولة للمهاجم:
---
- **هروب من جهاز افتراضي ابتكره المترجم** — [QEMU](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact), [Xen](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-xen-ptwalk-RELEASE-4.21.1.md#candidate-1--guest_walk_tables-pte-walk), [bhyve](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-bhyve-release-15.0.0.md#candidate-1--ahci-prdt-byte-count-write-path-oob-write), [KVM](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-kvm-host.md#candidate-1--svm-nested-vmcb12-save-area-cache-flagship), [ACRN](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-acrn-v3.3.md#candidate-1--nested-ept-shadow-walk)
- **صلاحيات جذر ابتكرها المترجم** — [siw](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline), [VMBus](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index), [systemd](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy), [af-packet](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-af-packet.md#candidate-1--tp_len-tx-packet-length), [snd-pcm](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-snd-pcm-v7.0.md#candidate-1--snd_pcm_indirect_playback_transfer-appl_ptr-snapshot-used-for-diff-and-stored-baseline), [seL4](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window)
- **استمرارية منصة ابتكرها المترجم** — [edk2](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore), [coreboot](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-coreboot-26.03.md#candidate-1--smmstore_rawread_region-bufsize--com-buffer-mapping-overflow), [U-Boot](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary), [OpenSBI](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-opensbi-v1.8.1.md#candidate-1--dbtr-update-trigger-index-primary)
- **اختراق enclave ابتكره المترجم** — [SGX](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural), [Keystone](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-keystone-master-88c49ee.md#candidate-1--edge_call_get_ptr_from_offset--edge_call_ret_ptr-host-written-return-offsetsize), [OpenEnclave](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-openenclave-v0.19.15.md#candidate-1--sgx-ecall-context-ocall-buffer), [OP-TEE](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-optee-os-4.10.0.md#candidate-1--register_shm-raw-tmem-reads)
---
كل واحد من هذه يمكن أن يكون كارثيًا بمفرده، لكن الاتساع هو ما يقلق: الشكل نفسه يظهر في كل مكان يبحث فيه التحليل، في أكواد لا تشترك في شيء سوى النمط الاصطلاحي:
| الهدف | الموقع | الأثر |
|---|---|---|
| **QEMU** | [`ahci_populate_sglist`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact) | طول PRDT الخاص بـ AHCI للضيف يُثبَّت مرة واحدة → **قراءة خارج الحدود / DMA للمضيف موجه من المهاجم** |
| **Linux / RDMA** | [`siw_rqe_get`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline) | `num_sge` الخاص بـ software-RDMA يُعاد استخدامه → **كتابة خارج الحدود في النواة** |
| **edk2 / UEFI** | [`SmmLockBoxRestore`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore) | طول مخزن SMM المؤقت يُعاد استخدامه → **كتابة خارج الحدود في SMRAM** (ring -2) |
| **TPM 2.0** | [`CryptParameterDecryption`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-ms-tpm-20-ref-v1.83r1.md#candidate-1--cryptparameterdecryption-in-place-decrypt-length) | طول فك التشفير في الموقع يُعاد استخدامه → **كتابة خارج الحدود في جذر الثقة لـ TPM** |
| **seL4** | [`decodeUntypedInvocation`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window) | نافذة الكائن المعاد تصنيفها (retype) تُعاد استخدامها → **اختراق النواة** |
| **Xen** | [`guest_walk_tables`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-xen-ptwalk-RELEASE-4.21.1.md#86-per-candidate-finding) | PTE الخاص بالضيف يُعاد استخدامه أثناء المشي → **تصعيد الامتيازات** |
| **SGX** | [edger8r ECALL bridge](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural) | طول `[in]`/`[in,out]` يُعاد استخدامه في `malloc`/`memcpy_s` → **فيض في كومة الـ enclave** (كل ECALL) |
| **ARM TF-A** | [`spmc_ffa_fill_desc`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-tf-a-v2.15.0.md#candidate-1--spmc-ffa_mem_sharelend-send-path-primary-could--yes) | حقل واصف FF-A يُعاد استخدامه لتحديد حجم `memcpy` → **فيض كومة في المراقب الآمن EL3** |
| **Linux / Hyper-V** | [Hyper-V VMBus `__vmbus_on_msg_dpc`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index) | `msgtype` الخاص بالمضيف يُعاد استخدامه لفهرسة جدول المعالجات → **استدعاء غير مباشر جامح** في نواة الضيف |
| **U-Boot** | [`virtqueue_get_buf`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary) | `id` لـ used-ring الخاص بـ virtio يُعاد استخدامه كفهرس مصفوفة → **قراءة/كتابة خارج الحدود في الكومة** داخل محمّل الإقلاع |
| **glibc** | [`_dl_check_map_versions`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-glibc-glibc-2.42.md#candidate-1--_dl_check_map_versions-verneed-version-index-write) | فهرس إصدار VERNEED في المحمّل الديناميكي يُعاد استخدامه كمنخفض كتابة → **كتابة خارج الحدود في `ld.so`** عند تعيين مكتبة مشتركة مصنوعة بعناية |
| **systemd** | [`sd_journal_enumerate_fields`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy) | حقل حجم دفتر اليومية يُعاد استخدامه عبر التخصيص/النسخ → **كتابة خارج الحدود في الكومة** في `journalctl`/`coredumpctl` (غالبًا بصلاحيات root) |
| **git** | [`read_table_of_contents`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-git-v2.54.0.md#candidate-1--read_table_of_contents-chunk-offset-to-start-pointer) | إزاحة قطعة مخزن الكائنات تُعاد استخدامها كقاعدة/حجم لقطعة → **قراءة خارج الحدود** عند تحليل `.idx` / multi-pack-index / commit-graph مصنوع بعناية (مستودع مشترك / خلفية forge) |
| **SQLite** | [`btreeComputeFreeSpace`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sqlite-version-3.53.2.md#candidate-1--btreecomputefreespace-freeblock-offset-pc-→-data-index) | إزاحة freeblock في شجرة B تُعاد استخدامها كفهرس صفحة → **قراءة خارج الحدود لصفحة قاعدة بيانات معيّنة عبر `mmap`** |
| **FreeType** | [`ft_var_readpackedpoints`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-freetype-VER-2-14-3.md#candidate-1--ft_var_readpackedpoints-gvar-packed-point-count-n) | عدد النقاط المعبأة لخط متغير يُعاد استخدامه → **كتابة خارج الحدود في الكومة** عند عرض خط مصنوع بعناية (منتشر: Android / Chrome / سطح المكتب) |
| **libtiff** | [`NeXTDecode`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-libtiff-v4.7.1.md#candidate-1--nextdecode-literalspan-off--n-controlled-oob-write) | إزاحة/طول مدى NeXT-RLE يُعاد استخدامهما → **كتابة خارج الحدود في الكومة** عند فك تشفير TIFF مصنوع بعناية (وضع القراءة الافتراضي عبر `mmap`) |
| **binutils / ld** | [`sframe_decode`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-binutils-binutils-2_46_1.md#candidate-1--sframe_decode-sfh_num_fdes-fde-table-alloc-vs-fill) | عدد FDE في SFrame يُعاد استخدامه كحجم تخصيص **و**حد أقصى للتعبئة → **كتابة خارج الحدود في الكومة داخل الرابط** على كائن مصنوع بعناية |
| **ClamAV** | [`autoit` EA05 `csize`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-clamav-clamav-1.5.2.md#candidate-1--autoit-ea05-csize-alloc-vs-fill-heap-oob-write) | `csize` في AutoIt يُعاد استخدامه كحجم تخصيص **و**طول نسخ → **كتابة خارج الحدود في الكومة** في الماسح |
| **YARA** | [`pe_parse_exports`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-yara-v4.5.7.md#candidate-1--pe_parse_exports-number_of_exports-loop-bound) | عدد تصديرات PE يُعاد استخدامه كحد للحلقة → **قراءة خارج الحدود** عند فحص عينة مصنوعة بعناية |
| **WAMR** | [`_vprintf_wa`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-wamr-WAMR-2.4.4.md#candidate-1--_vprintf_wa-s-handler-s_offset-string-address-rematerialization) | إزاحة `%s` للضيف تُعاد قراءتها خارج ساحة الرمل → **قراءة خارج الحدود تسرّب ذاكرة المضيف إلى ضيف wasm** |
| **ImageMagick** | [`ReadSUNImage`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy) | طول SUN-raster يُعاد استخدامه كحجم تخصيص **و**طول نسخ → **كتابة خارج الحدود في الكومة → تنفيذ تعسفي للكود RCE** عند فك تشفير صورة مصنوعة بعناية (بنى LTO) |
| **FreeBSD** | [`virtqueue_dequeue`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-freebsd-drivers-release-15.0.0.md#candidate-1--virtqueue_dequeue-used-ring-desc_idx) | `id` المكتوب من المضيف لـ used-ring في virtio يُعاد استخدامه كفهرس مصفوفة غير محدود → **تحرير مزدوج للوصف / استخدام بعد التحرير UAF** في النواة |
كل شيء عرضة للخطر. وكل شيء ليس كذلك. في كل حالة، يفعل المصدر الشيء الصحيح: يلتقط الإدخال غير الموثوق، ويتحقق من النسخة، ويستخدم النسخة. لكن في كل حالة، تسمح معايير C بهدوء للمترجم بأن *يلغي* تلك العملية اختياريًا، ويخلق TOCTOU من العدم. ما إذا كان موقع معيّن قابلًا للاستغلال *ليس خاصية من خصائص المصدر*: بل يُقرره المترجم ونسخته والبنية والأعلام، ولا ينهار في اتجاه واحد إلا عندما تبني. وحتى ذلك الحين، يكون كل واحد منهما كلاهما — ثغرة معلّقة في تراكب، لا يمكن تمييزها على مستوى المصدر عن كود سليم تمامًا. كل واحد هو TOCTOU شرودنغر — والجدول أعلاه هو شكلها على نطاق واسع.
الجزء المقلق ليس أن هذه المشاريع بالذات معيبة — بل أن النمط يظهر في كل مكان يبحث فيه التحليل، منسوجًا في [أكثر الأكواد مراجعةً في العالم](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#82-executive-summary) عبر لا شيء سوى لغة C اصطلاحية. المستودعات التي تتجاوز 100 هي **عينة، وليست الحدود**: نفس الخطأ الكامن يكاد يصل بالتأكيد إلى قاعدة الأكواد الخاصة بك.
التحليل الكامل وتحليل الأثر موجود في [observer-effect/](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/) وفي [REPORT.md](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/REPORT.md).
## الحلول
> *لا يوجد أي منها.*
لكن هناك بعض الأشياء التي يمكننا تجربتها على أي حال.
الحل الانعكاسي هو محاولة تثبيت الحمل — [`volatile`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch), [`READ_ONCE`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch), [atomic](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-2--atomic--acquire-load), [`"memory"`-clobber `barrier()`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier). هذه الحلول سليمة من حيث المواصفات وتصمد أمام `-O3` وLTO والتضمين؛ وحيثما وُجدت قراءة عارية *[مكتشفة](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-kvm-host-sev-snp.md#candidate-1--snp_begin_psc-idx_end-loop-bound)*، فهي [التصحيح الصحيح](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#confirmed-in-the-wild). لكن للأسف، تعالج الجرح وليس السبب:
- **`volatile` يختفي بصمت.** إنه [يؤهل *الوصول عبر lvalue*](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#the-volatile-cat-state)، وليس الكائن أو المؤشر أو المنطقة. قراءة `volatile T *p` عبر lvalue عادي [تعطي حماية صفرية](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/volatile_lvalue_launder.c)، ويُسقَط المؤهل **بدون أي تشخيص** عندما [يمر عبر `const void *` في `memcpy`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/volatile_memcpy_overlap.c) — إذ لا يوجد [`memcpy` يحافظ على volatile](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#false-friends--look-like-barriers-but-are-not). الحاجز الذي كتبته يتبخر عند الاستدعاء الذي لم تكتبه.
- **`READ_ONCE` لا يتوسع.** استخدام `READ_ONCE` يعني في الحقيقة: وسم *كل* وصول يصل إليه المهاجم إلى [*كل* حقل](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline)، إلى الأبد، و[وضع حاجز بين القراءة و*جميع* استخداماتها](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier). [أخطئ في واحد](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-io_uring.md#candidate-1--nvme_uring_cmd_io-nsid) فتكون القاعدة باطلة. إنه [لا يمكن فرضه على نطاق واسع](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch)، وينحدر بصمت.
- **صحة `barrier()` تتقرر على بُعد إطارات من سطر المصدر.** تحديد ما إذا كان `copy_from_user(&local, uptr, n)` يحمل حتى تأثير `"memory"` clobber يعني [تتبّع خمس طبقات مضمّنة واستدعاء خارج الخط](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#analysis) من C العامة إلى asm خاص بالبنية، وحلّ حفنة من التشعبات `CONFIG`/ميزات وحدة المعالجة/`__builtin`. وحتى بعد العثور عليه، فإن clobber [لا يسمّي أي قراءة](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier): خطوة في غير مكانها [لا تثبّت شيئًا](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier)؛ وخطوة في الاتجاه الآخر [*تفرض* بالضبط إعادة التحميل التي كان يجب أن يوقفها](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#principles--shared-facts-the-cards-lean-on).
لكن الأهم من ذلك: المصدر لا يطلب إعادة تحميل من الأساس. هذه هي القضية الأعمق. كتب المبرمج `local.len` وكان *يقصد* `local.len`: قيمة واحدة، تُقرأ مرة واحدة. إذا قلنا `x` فإننا نعني `x`، وليس «`x`، لكن `y` إذا كان المترجم يفضّل ذلك بدلًا منه». إعادة التحميل مبتكرة تحت الآلة المجردة، لذا فإن الكود الذي يحتاج إلى الترميز [يبدو مطابقًا للكود الذي لا يحتاجه](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/README.md#same-source-different-outcome) — لا توجد [إشارة في الموقع إلى أن حاجزًا مطلوب](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#proposed-levers-do-not-exist-in-usable-form-today). لا يمكنك أن تتذكر حماية قراءة لم تكتبها أبدًا.
الفهرس الكامل للدفاعات — مع [نقاط قوتها](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-binder-v7.0.md#executive-summary) و[إخفاقاتها](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-libspdm-3.8.2.md#durability-assessment) — موجود في [تقرير الحواجز](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md).
## افتح الصندوق
> *نمط TOCTOU من العدم موجود في كل مكان. تحقق مما إذا كان كودك يحتوي عليه.*
افحص كودك الخاص باستخدام [`observer-effect/AUDIT-PROMPT.md`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/AUDIT-PROMPT.md)، الذي سيبحث عن حدود الثقة، ويبحث عن نمط شرودنغر، ويستبعد الحواجز المتوافقة مع المواصفات، ويقيّم الاحتمالية/الأثر/الخطر. سلّمه إلى وكيل البرمجة المفضل لديك مع وضع مصدرك في السياق ووجّهه إلى نظام فرعي:```sh
cd ~/your-project # the codebase you want audited
claude -p "$(cat path/to/observer-effect/AUDIT-PROMPT.md)
Audit drivers/net/ for invented-load TOCTOUs."
لا يعتمد على أي شيء آخر في هذا المستودع — انسخ الملف الواحد وابدأ.
Schrödinger's TOCTOU يشخّص نموذجًا محددًا واحدًا من بعض التحسينات العشوائية التي تسمح بها مواصفات لغة C المكوّنة من 500 صفحة. لكنه لا يخدش سوى السطح: فهناك مساحات شاسعة جدًا متبقية لاستكشافها. سيستمر هذا المستودع في تقصّي والتقاط وتوثيق الطرق غير المتوقعة التي يخذلك بها المترجم المفضل لديك — بصمت، وبشكل قانوني، وفي كل مستوى تحسين.
"... إذا فعل gcc ذلك، فسيشتعل جزء كبير من النواة."
— Paul E. McKenney, LKML, 2009-04-16 · lore
"يحب الناس الحديث عن 'C الآمنة'، لكن القائمين على المترجمات حاولوا بنشاط جعل C غير آمنة لعقود. وكانت لجنة معايير C متواطئة."
— Linus Torvalds, 2025-02-21 · lore
"كنت أفضل بشدة وجود مفتاح للمترجم يوجّهه لعدم القيام بأشياء غبية للغاية كهذه بدلًا من وسم كل عملية تحميل/تخزين أخرى في النواة بـ volatile."
— Peter Zijlstra, 2015-06-17 · lore
"المواصفة مجرد ورق تواليت. الشيء الوحيد الذي يهم هو ما تفعله العتاد الحقيقي."
— Linus Torvalds, 2006-12-04 · lore
"بهذا المنطق نحتاج إلى تغطية نصف النواة بـ _ONCE() … هل يمكننا أخيرًا أن نضع قدمنا ونقول لأصحاب المترجمات ولجنة المعايير: أوقفوا هذا الجنون؟
— Thomas Gleixner, 2019-08-16 · lore
"المترجمات التي «تحسّن» الأمور بحيث تلمس حقولًا لا يلمسها الكود المصدري هي ببساطة هراء معطوب بطبيعته. لست مهتمًا إطلاقًا بالانحناء لجنونهم... الادعاء بأنها تحتاج إلى وسم volatile هو عرض من أعراض كاتب مترجمات مريض."
— Linus Torvalds, 2014-12-04 · lore
"مجنون؟ على الأرجح. لكن هناك رجال مترجمات يقسمون به."
— Paul E. McKenney, LKML, 2008-02-04 · lore
"من الجيد أنهم اختبروا جميع مسارات الكود، لكنها اختُبرت دائمًا بمترجم لا يبذل جهدًا إضافيًا لمحاولة توليد كود «قانوني لكنه سخيف». لذا لن يجد الاختبار عادةً الحالات التي قد يكون مسموحًا للمترجم أن يفعل فيها شيئًا آخر. ... مطوّرو المترجمات الذين لا يدركون هذا ليسوا من أهل المترجمات. إنهم أكاديميون منخرطون في العادة السرية الذهنية."
— Linus Torvalds, LKML, 2007-01-04 · lore
"بالطبع، ليست المترجمات الغبية هي ما يقلقني، بل الذكية منها..."
— Paul E. McKenney, LKML, 2013-10-09 · lore
"... كان لدينا كتّاب مترجمات يقولون «إذا قرأت المواصفات، فالأمر مقبول». لا، ليس مقبولًا. لأن الواقع يتفوق على أي قراءة مراوغة للمواصفات."
— Linus Torvalds, LKML, 2019-08-16 · lore
"... تعريف «المترجم السليم» يزداد تخففًا باستمرار."
— Paul E. McKenney, LKML, 2013-09-24 · lore
Schrödinger's TOCTOU هو جهد بحثي من Christopher Domas (@xoreaxeaxeax)
