CF Incident Brief
Security Disclosure Workers AI 26 Οκτωβρίου 2026

Όταν το cache ενός LLM διάβασε ξένα prompts

Το Cloudflare δημοσίευσε coordinated disclosure για cross-tenant έκθεση KV cache στο μοντέλο GLM-5.3 των Workers AI. 155 απαντήσεις περιείχαν δεδομένα άλλων πελατών. Η αιτία δεν ήταν «χακάρισμα του μοντέλου», αλλά λάθος ρύθμιση inference engine και ένα bug στο SGLang.

Πηγή: blog.cloudflare.com Μοντέλο: @cf/zai-org/glm-5.3 Παράθυρο έκθεσης: 27 Αυγ – 9 Σεπτ 2026
155επηρεασμένες απαντήσεις
681.280tokens όριο KV pool
1.048.576διαφημιζόμενο context
40 λεπτάαπό incident σε offline

Τι συνέβη

Στις 8 Σεπτεμβρίου 2026, ο ερευνητής Gadi Evron της Knostic ενημέρωσε την Cloudflare μέσω HackerOne ότι πολύ μεγάλα prompts προς το GLM-5.3 επέστρεφαν κείμενο που ανήκε σε άλλες συνεδρίες χρηστών. Η Cloudflare αναπαρήγαγε το πρόβλημα το επόμενο πρωί, έθεσε το μοντέλο offline μέσα σε 40 λεπτά από την κήρυξη περιστατικού και το επανέφερε την ίδια μέρα με διορθωμένη ρύθμιση και patched engine.

Ανάμεσα στις 27 Αυγούστου (ημέρα launch) και τις 9 Σεπτεμβρίου, μικρός αριθμός πελατών έλαβε απαντήσεις που περιείχαν δεδομένα από προηγούμενα requests άλλων πελατών στο ίδιο μοντέλο. Συνολικά επηρεάστηκαν 155 απαντήσεις, όλες σε prompts μεγαλύτερα από 681.280 tokens.

Κρίσιμο Δεν βρέθηκαν ενδείξεις εσκεμμένης εκμετάλλευσης. Μετά τον αποκλεισμό των tests της Cloudflare και του ερευνητή, ο όγκος και ο χρονισμός ταιριάζουν με κανονική χρήση long-context, όχι με επαναλαμβανόμενη επίθεση. Κανένα άλλο μοντέλο στο Workers AI δεν επηρεάστηκε.

Η δημοσίευση έγινε στις 26 Οκτωβρίου 2026, σε συντονισμό με την ομάδα του SGLang (RadixArk) και τον ερευνητή. Το ίδιο bug class αφορά isolation σε multi-tenant LLM serving: το KV cache είναι κοινό pool σελίδων στη GPU μνήμη και η ιδιοκτησία των σελίδων πρέπει να είναι αλάνθαστη.

Πώς σερβίρεται το GLM-5.3

Το Workers AI τρέχει το GLM-5.3 με SGLang σε disaggregated διάταξη:

  • Prefill GPUs: επεξεργάζονται το prompt και παράγουν το KV cache.
  • RDMA / Mooncake: μεταφέρουν το cache στο decode cluster.
  • Decode GPUs: γεννούν την απάντηση token-token, διαβάζοντας αυτό το cache.

Το KV cache είναι κοινό pool σταθερού μεγέθους σελίδων. Κάθε request παίρνει σελίδες στην εισδοχή και τις απελευθερώνει όταν τελειώσει. Οι σελίδες δεν μηδενίζονται ανάμεσα σε requests: το επόμενο request απλώς τις ξαναγράφει. Αυτό είναι συνηθισμένο για απόδοση. Σημαίνει όμως ότι ο engine είναι υπεύθυνος να μην αφήσει decode να διαβάσει σελίδες που δεν έγραψε το τρέχον request.

Το μέγεθος του pool δεν είναι ξεχωριστό config value. Το SGLang το υπολογίζει στο startup από την ελεύθερη GPU μνήμη μετά τη φόρτωση των βαρών. Το advertised context length είναι άλλο config. Τίποτα δεν τα υποχρεώνει να συμφωνούν.

Δύο overlapping προβλήματα

1. Misconfiguration

Στα prefill nodes του GLM-5.3, το KV pool χωρούσε 681.280 tokens, ενώ το μοντέλο διαφημιζόταν με context 1.048.576 tokens. Η ομάδα το ήξερε πριν το launch. Περίμενε ότι ο engine θα γυρίσει σφάλμα για prompt που δεν χωράει. Τα long-context tests επέστρεφαν HTTP 200 και streaming body χωρίς error frames, οπότε θεώρησαν ότι «κάπως το χειρίζεται».

Το κενό στο testing: ελέγχθηκαν status codes και απόδοση, όχι συνοχή και περιεχόμενο εξόδου.

2. Bug στο SGLang (disaggregated mode)

Όταν ένα prompt ξεπερνούσε το pool, ο scheduler το απέρριπτε εσωτερικά και το αντικαθιστούσε με placeholder ενός token, ώστε να στραγγίξει από το batch χωρίς πραγματική δέσμευση μνήμης. Σε single-node αυτό δούλευε: ο client έπαιρνε error.

Σε disaggregated prefill/decode, οι ουρές εισδοχής έλεγχαν αν το request είναι finished, όχι αν είναι rejected. Το rejected request φαινόταν ζωντανό.

01 Bootstrap
Το decode δεσμεύει π.χ. 8 σελίδες για το μεγάλο prompt.
02 Prefill
Το rejected request γίνεται admit. Τρέχει forward pass στο 1-token placeholder.
03 Transfer
Στέλνεται 1 σελίδα KV. Το decode νομίζει ότι πήρε ολόκληρο το prompt.
04 Decode
Η attention διαβάζει και τις 8 σελίδες. Οι 7 έχουν ακόμα δεδομένα προηγούμενων χρηστών.

Αποτέλεσμα: HTTP 200 αντί για 400. Το prompt_tokens ταίριαζε με το μεγάλο prompt. Η απάντηση ήταν συνεκτική, ερχόταν πιο γρήγορα από ό,τι έπρεπε για 8 σελίδες prefill, και μπορούσε να περιέχει prompts ή μερικές γεννήσεις άλλων χρηστών.

Κάθε request πάνω από το όριο συμπεριφερόταν έτσι. Παρόμοιο ζήτημα είχε αναφερθεί στο SGLang ως issue #30233 στις 6 Ιουλίου 2026. Το patch μπήκε στο v0.5.20, με follow-ups στα v0.5.21 και v0.5.22.

Χρονολόγιο (UTC)

ΠότεΤι
27 Αυγ 2026Launch GLM-5.3 με pool μικρότερο από το context. Η έκθεση υπάρχει από την πρώτη μέρα.
8 Σεπτ 23:11Report μέσω HackerOne.
9 Σεπτ 08:00Έναρξη επαλήθευσης.
9 Σεπτ 13:22Κήρυξη security incident. Αναπαραγωγή με δύο ανεξάρτητους λογαριασμούς σε 25 λεπτά.
9 Σεπτ 14:00Το μοντέλο γίνεται private. HTTP 400. Status page.
9 Σεπτ 14:35Root cause: prefill pool < context length.
9 Σεπτ 14:52Όλα τα prefill nodes με διορθωμένο config.
9 Σεπτ 19:04Full rebuild: σωστό pool + engine patch που επιστρέφει error σε rejected requests.
9 Σεπτ 19:40Το μοντέλο επανέρχεται δημόσια.
10 ΣεπτΣυνάντηση με maintainers SGLang.
16 Σεπτ – 12 ΟκτUpstream merges: admission fix, checksums, deferred KV release by default.
19 ΟκτSGLang v0.5.22.
26 ΟκτΣυντονισμένη δημοσίευση με ερευνητή και RadixArk.

Αποκατάσταση

Δύο άξονες, και οι δύο απαραίτητοι:

  1. Το pool να χωράει όλο το context. Η ρύθμιση ευθυγραμμίστηκε με το GLM-5.2. Το prefill KV pool πήγε περίπου στα 2,04 εκατ. tokens, σχεδόν διπλάσιο από το context 1.048.576. Έτσι το rejection path δεν αγγίζεται για έγκυρο request.
  2. Το rejection path να είναι ασφαλές. Request με pending rejection αποσύρεται στην disaggregation admission queue, με το αρχικό σφάλμα, πριν από bootstrap, allocation ή KV transfer. Το decode επαληθεύει ότι τα tokens που μεταφέρθηκαν ταιριάζουν με όσα δέσμευσε.

Σχετικά upstream: PR #38704 (Cloudflare draft), ήδη στο main ως #36988 (6 Σεπτ, άλλο failure path), και #38935 που απορρίπτει νωρίτερα. Σύσταση Cloudflare: SGLang v0.5.22 ή νεότερο.

Επιπλέον hardening σε όλα τα disaggregated deployments: deferred KV release και στα prefill source pages, bounded Mooncake transfer batches, load balancer που δεν κάνει abort στη μέση του transfer όταν φεύγει ο client, και υποχρεωτικός έλεγχος pool-vs-context πριν από κάθε αλλαγή deployment.

Τι να κάνουν άλλοι operators

Αν τρέχετε SGLang σε disaggregated prefill/decode:

  1. Μετρήστε το KV pool και συγκρίνετέ το με το context length σε κάθε prefill και decode node, από το startup log, μετά από κάθε αλλαγή weights, quantization, parallelism ή memory fraction. Αν το pool είναι μικρότερο, κατεβάστε το context.
  2. Αναβαθμίστε σε build που περιλαμβάνει τα #36988 και #38935 (v0.5.22+).
  3. Deferred KV release και στα δύο roles. Περιορίστε τα Mooncake transfer batches.
  4. Ενεργοποιήστε ανίχνευση διαφθοράς όπου το μοντέλο το υποστηρίζει (--disaggregation-enable-kv-checksum, --kv-canary) και επιβεβαιώστε από τα logs ότι όντως γίνεται verify.
  5. Μην αφήνετε το proxy να γεννά άσκοπα aborts πριν το πρώτο token.

Μεγαλύτερη εικόνα

Η Cloudflare περιγράφει μια οικογένεια bugs ιδιοκτησίας KV σελίδων: σελίδα απελευθερώνεται, ξαναδίνεται ή διαβάζεται ενώ κάτι άλλο την κρατάει ακόμα. Δύο συχνές αιτίες: lifetime μέσα σε έναν κόμβο (generation, offload, abort paths) και race ανάμεσα σε κόμβους όταν γίνεται abort ενώ το RDMA γράφει ακόμα.

Μακροπρόθεσμα θέλουν integrity by construction: κάθε φυσική σελίδα να έχει tag που αλλάζει στο reallocation, κάθε request να κρατά τα tags που περιμένει, και ο έλεγχος να γίνεται πριν το decode διαβάσει το cache. Πρωτότυπο του καλοκαιριού είχε κόστος κάτω από 1% σε throughput. Θέλουν επίσης formal verification του RDMA KV transfer protocol (Mooncake), στο πνεύμα δουλειάς όπως Topaz και Meerkat.

Για το blog: αυτό δεν είναι «το μοντέλο έστειλε δεδομένα στην Κίνα». Είναι αποτυχία isolation σε shared GPU memory ενός inference stack. Το μάθημα είναι βαρετό και σημαντικό: μέτρα το pool, μην εμπιστεύεσαι το HTTP 200, και μην θεωρείς το KV cache ιδιωτικό αν οι σελίδες δεν μηδενίζονται.

Πηγές

Συχνές ερωτήσεις

Τι συνέβη με το GLM-5.3;

Cross-tenant KV cache leak στο Cloudflare Workers AI.

Πόσοι επηρεάστηκαν;

155 requests από 16 πελάτες.