Google’ın kernelCTF PoW’unu AVX512 ile Nasıl Yendik
(anemato.de)- Crusaders of Rust ekibi, Linux packet scheduler’daki CVE-2025-38001 exploit’ini göndermeye çalışıyordu; ancak kernelCTF’in ilk gelen kazanır yapısı nedeniyle yarışın belirleyici noktası hatanın kendisinden çok PoW süresini kısaltmak oldu
- Gönderim süreci 12:00 UTC’de bağlanma, yaklaşık 4 saniyelik PoW, yaklaşık 2,5 saniyelik VM açılışı, exploit’in çalıştırılması ve Google Form gönderimi şeklinde ilerliyordu; önceki raunddaki 4,5 saniyelik gönderim rekoru darboğazı ortaya koydu
- Hedef PoW, “sloth” adlı bir VDF idi; 1280 bitlik bir tamsayı üzerinde
2^1279 - 1modüler karesini tekrarlayan seri bir hesaplama olduğundan CPU/GPU çekirdeği artırarak kısaltılması zordu - GMP/C++ ve Mersenne modüler indirgeme ile süre 1,9 saniyeye,
-march=nativestatik linkleme ile yaklaşık 1,4 saniyeye indirildi; ardından AVX512IFMA’nın 52 bit tamsayı fused multiply-add komutlarıyla Ryzen 9950X üzerinde yaklaşık 0,21 saniyeye düşürüldü - 16 Mayıs 2025’te ekip, Zen 5 Google Cloud sunucusu ve optimize edilmiş POST gönderim yolu ile flag’i 3,6 saniyede gönderdi; 28 Mayıs’ta kernelCTF PoW’un kaldırıldığını duyurdu
Yarış koşulu: sorun hatadan çok hızlı gönderimdi
- Mayıs 2025’te Crusaders of Rust ekibinden William Liu ve Savy Dicanosa, Linux packet scheduler’da use-after-free hatası olan CVE-2025-38001’i keşfedip bir exploit geliştirdi
- William, yüksek lisans tezi için Linux’u fuzzing yaparken hatayı buldu
- Savy, exploit çalışma süresini yaklaşık 0,55 saniyeye indirdi
- Google kernelCTF, iki haftada bir UTC öğleninde gönderim penceresini açıyor; sunucuyu exploit ederek flag’i Google Form’a ilk gönderen ekip ödül alıyordu
- Gönderim her zaman aynı sırayla ilerliyordu
- 12:00:00 UTC’de kernelCTF sunucusuna bağlanma
- proof of work çözümü, yaklaşık 4 saniye
- instance’ın açılmasını bekleme, yaklaşık 2,5 saniye
- exploit’i yükleme ve çalıştırma
- flag’i Google Form’a gönderme
- Beklenen bounty 51.000 $ idi
- Temel ödül 21.337 $
- Çalışma kararlılığı ödülü 10.000 $
- 0-day hata ödülü 20.000 $
Önceki gönderim rekorunun ortaya çıkardığı PoW darboğazı
- 2 Mayıs 2025 gönderim penceresinde ilk gönderim öğleden 4,5 saniye sonra geldi
- Sadece yaklaşık 4 saniyelik PoW ile yaklaşık 2,5 saniyelik VM açılışı bile 6,5 saniye gerektirdiğinden, 4,5 saniyelik rekor basit hesapla uyuşmuyordu
- kernelCTF sunucu kodunun yuvarlama özelliği nedeniyle VM instance’ı gerçekte 11:59:59’da açılıyordu; bu da zaman çelişkisini ortadan kaldırdı
- Yine de flag üretim zaman damgası, kazanan ekibin PoW’u 1 saniyenin altında çözdüğünü gösteriyordu
- Rakip ekip FPGA kullandıysa 1 saniye altı PoW mümkün olmuş olabilir
- FPGA, belirli işleri çok hızlı yapabilen özel amaçlı donanımdır
- Genel amaçlı işler için uygun değildir; fiyatı ve programlama zorluğu yüksektir
sloth VDF: iyi paralelleştirilemeyen PoW
- kernelCTF PoW’u “sloth” adlı doğrulanabilir gecikme fonksiyonu (VDF) idi
- VDF, uzun seri hesaplamalarla zaman geçtiğini kanıtlayan ve sonuç kanıtı görece hızlı doğrulanabilen kriptografik bir temel bileşendir
- Hesaplamanın kendisi seri olduğundan daha fazla CPU veya GPU çekirdeği kullanmak çalışma süresini kısaltmayı zorlaştırır
- Optimize edilen temel döngü şu yapıdaydı
difficulty=7337- Her difficulty yinelemesinde 1277 kez
x = (x * x) % (2 ** 1279 - 1)çalıştırma - Ardından
x’in en düşük anlamlı bitini tersine çevirme
- Google’ın referans implementasyonu Python’da gmpy kullanıyordu; gmpy, GMP için Python binding’idir
- GMP, platforma özgü toplama ve çarpma çekirdeklerini assembly ile uygulayan çok duyarlıklı tamsayı kütüphanesidir
GMP tabanlı ilk optimizasyon
- İlk optimizasyon,
2^1279 - 1değerinin bir Mersenne sayısı olmasından yararlanan modüler indirgemeydi- 2560 bitlik ara çarpım, alt 1279 bit ve üst bitler olarak bölünüp toplandı
- Sonuç modülden büyük veya eşitse bir kez çıkarılarak
%işlemi değiştirildi
- Python FFI overhead’ini azaltmak için C++’a taşındı; bu sürüm M1 MacBook Pro’da 1,9 saniyede çalıştı
- William, libgmp’yi yerelde
-march=nativeile derleyip statik linkleyerek Intel Ice Lake dizüstünde süreyi yaklaşık 1,4 saniyeye indirdi - Rust ile yazılmış benzer optimize solver aynı Mersenne tekniğini kullansa da yaklaşık 2,4 saniye sürdü
- Daha sonra FLINT de denendi, ancak hızı GMP ile neredeyse aynıydı
AVX512IFMA ile büyük tamsayı karesini yeniden yazmak
- AVX512, Intel x86 ISA uzantısıdır; vektör register sayısını ve genişliğini artırır, maskeli tahmin ve çeşitli yeni komutlar ekler
- Intel, Alder Lake’ten itibaren istemci CPU’larda AVX512 desteğini devre dışı bıraktı
- Sunucu tarafında destek devam etti; AMD ise Zen 4 ve Zen 5’te hem tüketici hem de sunucu CPU’larında AVX512’yi uyguladı
- Kilit nokta AVX512IFMA idi
vpmadd52luq: 52 bitlik çarpımın alt yarısını 64 bitlik akümülatöre eklervpmadd52huq: 52 bitlik çarpımın üst yarısını 64 bitlik akümülatöre ekler
- Bu komutlar 52×52→104 bitlik çarpımın alt ve üst bölümlerini hesaplayıp vektör register’larda biriktirir
- Zen 5, 512 bitlik veri yoluna sahip olduğundan bu komutlardan saat çevrimi başına 2 tane başlatabiliyordu
- Doğal taban
2^52idi; 1280 bitlik tamsayı 25 adet 52 bit limb ile temsil edildi- Tek bir 512 bit zmm register 8 limb taşıyabilir
- Değerin tamamı 4 zmm register’a sığar
Çarpma düzeni ve Mersenne indirgeme
- 1280 bitlik kare alma, 25 adet 52 bit limb’in karesini alıp 50 limb’lik ara sonuç üretme şeklinde uygulandı
- Karenin simetrisinden yararlanılarak gereken çarpma sayısı neredeyse yarıya indirildi
- Diyagonal bileşenler
ai^2 i < jiçin çapraz terimler2 * ai * aj
- Diyagonal bileşenler
- Çapraz terim hesaplamasında, shuffle sayısını azaltmak için ardışık 8 limb’lik sliding window tek bir multiplier limb ile çarpıldı
- AVX512’nin merge masking özelliğiyle nihai toplama dahil edilmeyen çarpmaların akümüle edilmemesi sağlandı
- Modüler indirgeme, üst 1279 bitin alt 1279 bite eklenmesiyle yapıldı
- Akümülatör elemanları
2^52 - 1değerini aşabileceğinden carry propagation toplama sonrasına ertelendi - Sonucun
2^1279 - 1değerinden büyük veya eşit olup olmadığı, 1280’inci bitin 1 olup olmadığına bakılarak belirlendi 2^1279 - 1değerini çıkarmak, 1280’inci biti temizleyip en düşük limb’e 1 eklemekle eşdeğerdir
- Akümülatör elemanları
- Son aşamada çok küçük bir overflow olasılığı kalıyordu
- Son limb tam olarak
2^52 - 1ise carry propagation gerekiyordu - Rastgele PoW için gerçekleşme olasılığı çalıştırma başına yaklaşık 2 milyarda 2 olarak görülüp yok sayıldı
- Son limb tam olarak
0,45 saniyeden 0,21 saniyeye inen mikro optimizasyonlar
- İlk AVX512IFMA sürümü, kiralanan Ryzen 9950X üzerinde PoW’u yaklaşık 0,45 saniyede işledi
- multiply-add komutunun gecikmesi 4 çevrimdi ve saat çevrimi başına 2 tane başlatılabiliyordu; çarpma birimini doyurmak için en az 8 akümülatör gerekiyordu
- Mevcut sürümde yalnızca 7 akümülatör vardı
- Alt yarı için 7 ve üst yarı için 7 olmak üzere toplam 14 akümülatör kullanıp sonda birleştirme yöntemine geçildi
- Bu değişiklik süreyi yaklaşık 0,32 saniyeye indirdi
- GCC ve clang, döngüyü unroll ederken
vbroadcastsd zmm, m64üretiyor; register allocation sırasında vektör register’lar yetmediği için stack spill ve reload oluşuyordu- Inline assembly ile
vpmadd52luq/vpmadd52huqkomutlarının memory broadcast operand kullanması zorlandı - multiplier limb ayrı bir vektör register’a koyulmadan bellekten okunup tüm vektör elemanlarına çoğaltıldı
- Bu broadcast load, vektör ALU kaynağı kullanmadan load unit üzerinde işlendi
- Bu aşamada süre yaklaşık 0,23 saniyeye indi
- Inline assembly ile
- Tamsayıyı belleğe hizalı kaydedip hizasız yüklemeyle window oluşturmak store-forwarding stall’a yol açıyordu
valignqile zmm register içinde hizasız yükleme taklit edilerek bellek erişimi azaltıldı- Nihai PoW süresi yaklaşık 0,21 saniye oldu
16 Mayıs 2025 gönderim sonucu
- Ekip, 16 Mayıs 2025 saat 04:30 PST’de nihai gönderime hazırlandı
- Gecikmeyi azaltmak için Google Form gönderim sunucusuna coğrafi olarak yakın Hollanda’daki Zen 5 Google Cloud sunucusu kullanıldı
- Gönderimden birkaç dakika önce sahte flag ile Google Form POST isteği yakalanıp kaydedildi
- Bryce Casaje ve Larry Yuan, Form gönderim programını tasarlayıp optimize etti
- Max Cai de geliştirme ve gönderime yardımcı oldu
- Saat 05:00’te sunucu kernelCTF sunucusuna bağlandı, PoW’u çözdü, Savy’nin optimize exploit’ini çalıştırdı ve flag’i POST isteğine ekleyip gönderdi
- Sonuç 3,6 saniyelik gönderimdi; o sırada kernelCTF tarihindeki en hızlı gönderimdi
- kernelCTF operatörü aynı gün bounty uygunluğunu doğruladı
PoW’un kaldırılması ve nihai solver’ın yayımlanması
- 28 Mayıs 2025’te kernelCTF operatörü koczkatamas PoW’un kaldırıldığını duyurdu
- PoW ortadan kalkınca slot rekabeti exploit çalışma süresi ve ağ gecikmesi eksenine kaydı
- Bu değişiklik, FPGA veya inline assembly optimizasyon bilgisi olmadan da profesyonel ekiplerle aynı koşullarda yarışmayı mümkün kıldı
- Nihai solver kodu, 14–15 Mayıs 2025’te yaklaşık 12 saatlik çalışmanın ürünüydü ve GNU AGPL 3.0 ile yayımlandı
- Build örneği
gcc main.c -O3 -march=znver5 -masm=intel -lgmpidi
1 yorum
Hacker News yorumları
Harika bir içerik. Bu yöntem, AVX-512 için optimize edilmiş RSA uygulamasına da çok benziyor; çünkü RSA da çok büyük üs alma işlemleri yapmak zorunda
Bu makale[1], RSA’nın windowing’i nasıl yaptığını ele alıyor ve pencere boyutunun keyfi olabileceğini gösteren bir formül de içeriyor. AVX-512 RSA uygulaması ayrıca [0..2^{window-size}) aralığındaki çarpım sonuçlarını bir tabloda saklıyor; her pencere için o sonucu tablo[2]dan alıp yalnızca shift/yeniden düzenleme yapıyor
https://dpitt.me/files/sime.pdf (bir dergiden aldığım için kendi alan adımda barındırıyorum)
https://github.com/aws/aws-lc/blob/9c8bd6d7b8adccdd8af4242e0...
Ayrıca maske register’larını aritmetik işlemler nedeniyle genel amaçlı register’lara taşıyor; bu Zen 4/5’te optimum değil. Ayrı olarak, carry’nin gerçekten tek seferde yayılması gerekip gerekmediğini de merak ediyorum. Kendi kodumda carry’nin yalnızca bir kez oluştuğunu varsaydım ve gerekirse döngüye geri dönecek şekilde yaptım; böylece yaygın durumdaki gecikmeyi azalttım. Ancak dallanma olursa zamanlama saldırısı sorunu doğabilir
dpitt.me/files/sime.pdfarchive.org’a da yüklenebilir: https://archive.org/download/sime_20250531/sime.pdf“Tüketici CPU’larında birkaç nesildir [AVX512] desteklenmesine rağmen” kısmı biraz tuhaf
Rocket Lake’ten (11. nesil) önce AVX-512 yalnızca üst seviye hobi CPU’larında, Xeon CPU’larda ve bazı mobil işlemcilerde vardı; mobil işlemcilere tüketici CPU’su demek de biraz tartışmalı. 12. nesilde performans/verimlilik çekirdeği yapısı nedeniyle birkaç ay sonra o çekirdeklerde devre dışı bırakıldı ve bir daha görünmedi. Yine de AMD AVX-512 ile bir ölçüde başarılı olursa Intel’in bunu tekrar getirme ihtimalinin yüksek olduğunu düşünüyorum. Bu arada hâlâ Intel i9-11900 kullanıyorum
Doğru gidişat bu. Intel’in birkaç ay önce güncellediği AVX10 teknik dokümanı[1] da bunu doğruluyor gibi. 512 bit AVX’in hem P çekirdeklerinde hem de E çekirdeklerinde standart olacağını açıkça söylüyor ve 256 bit’e özel yapılandırmadan uzaklaşıldığını belirtiyor
Bu, AVX-512’nin yalnızca sunuculara değil, E çekirdekli gelecekteki tüketici CPU’larına da düzgün biçimde geri döneceğine dair güçlü bir işaret gibi görünüyor. Muhtemelen AMD’nin daha geniş AVX-512 benimsemesini yakalamaya çalışıyorlar
[1] - https://cdrdv2.intel.com/v1/dl/getContent/784343 (PDF)
Verimlilik çekirdeklerine alan maliyeti nedeniyle AVX512 konmadığı için tüm CPU AVX512 desteklemiyor kabul edildi. Yalnızca bazı BIOS seçeneklerinin tuhaf davranışından yararlanarak verimlilik çekirdeklerini kapatıp kalan CPU’da AVX512’yi açabiliyordunuz; bunun bedeli de E çekirdeklerinden vazgeçmekti
Kazanan kayıt 3,6 saniyeydi ama ikinci 3,73 saniyeydi; kazanan kayıtla aynı basamak sayısına yuvarlarsak 3,74 saniye. O halde ikinci olanın da proof-of-work’ü optimize ettiğini ya da FPGA kullandığını mı düşünmek gerekir?
Yazar, önceki gönderimin pahalı FPGA tabanlı olduğunu söylemesine rağmen 4 saniyenin üzerindeydi demişti. O zaman o haftanın ikincisinin tarihteki en hızlı ikinci gönderim olma ihtimali de vardı; bunun hakkında bir şeylerden bahsetmesini beklerdim
Etkileyici ama optimizasyon hedefi yanlış gibi görünüyor. CTF, gönderim operasyonu savaşına dönüşmemeli
Gönderim penceresi içinde flag gönderen tüm takımların ödülü paylaşması herkes için daha iyi olmaz mı?
Bu yüzden pratikte “yanlış” davranışı aktif biçimde teşvik edebilir
Doğru anladıysam 4 saniyelik proof-of-work var ve ödül ayda bir kez ödeniyor
Gerçekten her ay insanların rekabet edeceği kadar çok exploit var mı?
Herkese açık CTF zor. Sonunda bazı ekipler bitiş çizgisine koşarken DDoS’a benzer davranışlar sergiliyor. Sonrasında Google proof-of-work adımını kaldırdı
Harika bir içerik ama bu challenge’ı kazanmak için aşılması gereken engellere bakınca komedi gibi okunuyor. Tam bir Rube Goldberg makinesi gibi
Bu yazıda bahsedilen 52 tabanlı gösterim hakkında daha fazlasını öğrenmek istiyorsanız bugün ana sayfadaki başka bir yazıya da bakmaya değer: https://news.ycombinator.com/item?id=44132673
Küçük bir düzeltme: statik linkleme inlining sağlamaz, yalnızca PLT overhead’ini kaldırır. Inlining fırsatlarını artıran şey LTO’dur
Neden yarıştırdıklarını anlamıyorum. Her benzersiz exploit için doğrudan ödül verilemez mi?
Ayrıca Linux o kadar çok bug içeriyor ki tüm 0-day’lere para vermeye başlarsanız iş kontrolden çıkar. Google da bir dönem insanların biriktirdiği bug’ları boşaltmak için rekabetsiz, sınırlı süreli bir promosyon yapmıştı; tüm 0-day’ler kabul edilince gönderimler patladı. Aynı zamanda topluluğu kızdırmak istemedikleri için yapı böyle oldu
Aradan bunca yıl geçmişken uzmanların 3 saniyede bir Linux makinesini ele geçirebilmesi biraz moral bozucu